Hello Everyone,
I am working on a project where I need to setup the multiple sub-domains with the routes. I tried code with two sub-domains, but in my case it would be 100 sub-domains. I tried the following code for this:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
"strings"
)
type Subdomains map[string]http.Handler
func (subdomains Subdomains) ServeHTTP(w http.ResponseWriter, r *http.Request) {
domainParts := strings.Split(r.Host, ".")
if mux := subdomains[domainParts[0]]; mux != nil {
mux.ServeHTTP(w, r)
} else {
http.Error(w, "Not found", 404)
}
}
func main() {
r := gin.Default()
r2 := gin.Default()
hs := make(Subdomains)
hs["admin"] = r
hs["analytics"] = r2
r.GET("/ping", adminHandlerOne)
r2.GET("/ping", adminHandlerOne)
http.ListenAndServe(":9090", hs)
}
func adminHandlerOne(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
}
But I think that this is not good. Is anybody know the proper way to do this?
You have a few options (write a router, write wrappers, attempt to bend gin to your will), but as the default ServeMux supports this, and you probably really don't need gin in particular, I'd go with the standard router. Take a look at the source of the DefaultServeMux first, to get an idea of how simple a router is at heart - a router is just a map of paths to handlers.
The Default ServeMux actually does what you want (allows matching on host as well as path), so I'd suggest trying with that first. Register your patterns:
mux := http.NewServeMux()
mux.HandleFunc("/", handlerRoot)
mux.HandleFunc("analytics.example.com/", handlerOne)
mux.HandleFunc("admin.example.com/", handlerTwo)
err := http.ListenAndServe(":9090", mux)
if err != nil {
panic(err)
}
Write some handlers (obviously you might write json with an encoder rather than directly, this is just an example):
func handlerOne(w http.ResponseWriter, r *http.Request) {
j := fmt.Sprintf(`{"one":"%s"}`, r.Host)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(j))
}
If you're then testing this on localhost, you'd probably want to edit your /etc/hosts file to be sure you hit the server with the right hostname.
Putting that together, you could use something like this to test with:
https://play.golang.org/p/ut-GT_s3Gf
Note these subdomains could be dynamic if you want (the mention of 100 plus makes me think they might be), and you could just customise behaviour at runtime in handlers rather than using separate handlers, it depends how differently the domains behave.
You can use *httputil.ReverseProxy
for this. Here is how I would reroute to a subdomain based on a hostname without any middleware in Gin.
router.GET("/:username", func(c *gin.Context) {
uri, ok := c.Get("location")
if !ok {
c.JSON(500, gin.H{
"reason": "Location unknown",
})
}
hostname := "awesome.io"
if uri.(*url.URL).Host == hostname {
// Want to send client to "https://auth.awesome.io/:username"
s := fmt.Sprintf("https://auth.%s/%s", domain, c.Param("username"))
uri, err := url.Parse(s)
if err != nil {
c.JSON(500, gin.H{
"reason": "Subdomain is wrong",
})
}
rp := new(httputil.ReverseProxy)
// Modify request's URL to point to the new uri
rp.Director = func(req *http.Request) {
req.URL = uri
}
rp.ServeHTTP(c.Writer, c.Request)
}
})