7

I'm creating an endpoint using Go's Gin web framework. I need full server URL in my handler function. For example, if server is running on http://localhost:8080 and my endpoint is /foo then I need http://localhost:8080/foo when my handler is called.

If anyone is familiar with Python's fast API, the Request object has a method url_for(<endpoint_name>) which has the exact same functionality: https://stackoverflow.com/a/63682957/5353128

In Go, I've tried accessing context.FullPath() but that only returns my endpoint /foo and not the full URL. Other than this, I can't find appropriate method in docs: https://pkg.go.dev/github.com/gin-gonic/gin#Context

So is this possible via gin.Context object itself or are there other ways as well? I'm completely new to Go.

blackgreen
  • 34,072
  • 23
  • 111
  • 129
Kaushal28
  • 5,377
  • 5
  • 41
  • 72
  • Do you want to get `http://localhost:8080/foo` from gin context or handle path variable like you mentioned in python example? – nipuna Aug 18 '21 at 17:01
  • @nipuna I want to get that base URL from gin context as that's only available object inside my handler function. I don't want to handle path variables. – Kaushal28 Aug 18 '21 at 17:12

2 Answers2

13

c.Request.Host+c.Request.URL.Path should work but the scheme has to be determined.

package main

import (
    "fmt"

    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()

    r.GET("/foo", func(c *gin.Context) {
        fmt.Println("The URL: ", c.Request.Host+c.Request.URL.Path)
    })

    r.Run(":8080")
}

You can determine scheme which also you may know already. But you can check as follows:

scheme := "http"
if c.Request.TLS != nil {
    scheme = "https"
}

If your server is behind the proxy, you can get the scheme by c.Request.Header.Get("X-Forwarded-Proto")

Nick
  • 1,017
  • 1
  • 10
  • 16
  • Related: [Why are request.URL.Host and Scheme blank in the development server?](https://stackoverflow.com/questions/6899069/why-are-request-url-host-and-scheme-blank-in-the-development-server) – blackgreen Aug 18 '21 at 20:30
  • 1
    @blackgreen Yes, I noticed that too basically on development server nothing much available on `Request.URL` except `Path`. That is why I think it's safer to user `Request.Host` for host. – Nick Aug 18 '21 at 20:58
  • Thanks. How did you come up with this? Is it documented? – Kaushal28 Aug 19 '21 at 07:36
  • 1
    @Kaushal28 not directly documented, because this depends on the http library, not Gin. See this https://github.com/gin-gonic/gin/issues/1233 – blackgreen Aug 20 '21 at 07:41
  • 1
    @Kaushal28 As blackgreen mentioned, it's not documented directly. But you come to know it by experience specifically if you have it with `net/http` library. – Nick Aug 20 '21 at 10:18
  • Might this not be unsafe? It reads the url _from the request_. From its doc string: "To prevent DNS rebinding attacks, server Handlers should validate that the Host header has a value for which the Handler considers itself authoritative." - The _real_ server url might be different, might it not? – Zsar May 15 '22 at 16:05
1

You can get host part localhost:8080 from context.Request.Host and path part /foo from context.Request.URL.String().

package main

import (
    "fmt"
    "net/http"

    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.GET("/foo", func(c *gin.Context) {
        c.String(http.StatusOK, "bar")
        fmt.Println(c.Request.Host+c.Request.URL.String())
    })
    // Listen and Server in 0.0.0.0:8080
    r.Run(":8080")
}

And you can get http protocol version by context.Request.Proto, But it will not determine http or https. you need to get it from your service specifications.

nipuna
  • 3,697
  • 11
  • 24