Set gorilla mux subrouter - go

If I have a mux.Router, how do I set it to be a "subrouter"? All examples I can find creates a new router by calling Route.Subrouter() and then setting Handlers on it, but I already have a router!
// does not know about "/api/v1/"
v1_router := mux.NewRouter()
subrouter.HandleFuc("/route1/", ...)
subrouter.HandleFuc("/route2/", ...)
// does not now about route1, route2
r := mux.NewRouter()
r.PathPrefix("/api/v1/").???(v1_router)
I hope I'm making sense...

I feel the same way, and have to live with the same "workaround". I would like to set the subrouter to an existing router. Like:
r.PathPrefix("/api").SetSubrouter(api.GetRouter()) //won't work
That would let my api feel more autonomous / loosely coupled. But getting a subrouter is all we have from gorilla.
s := r.PathPrefix("/api").Subrouter()
api.SetRoutes(s)

You can do it like this:
v1 package file:
func Handlers(subrouter *mux.Router) {
//base handler, i.e. /v1
r.StrictSlash(true)
subrouter.HandleFuc("/route1/", ...)
subrouter.HandleFuc("/route2/", ...)
}
main file:
r := mux.NewRouter()
package.Handlers(r.PathPrefix("/api/v1").Subrouter())

Related

Serve static file from within a group with Gin

I want to server static file by mapping /fs to filesys in the disk. I can server static file like this:
r := gin.New()
r.Use(static.Serve("/fs", static.LocalFile("./filesys", false)))
// followed by other routes definition such as R.GET()
I also want to guard access by using a authentication middleware, without affecting other routes. I imagine it's something I need to do with Gin's group like this:
r := gin.New()
g := r.Group("/fs")
{ // what is the purpose of this parenthesis BTW?
g.Use(authMiddleWare)
g.Use(static.Serve("/fs", static.LocalFile(fileUploadDir, false)))
}
However, I can't get it to work. It is not routed in. If I do additional g.GET afterward, the path came out to be wrong.
How to go about this?
Hi I checked this issue has been open for 3 years on git with no solution for 3 years and the static package seems not being maintained anymore
This is an alternate solution that might help you
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
grp := r.Group("/static")
{
grp.StaticFS("", http.Dir("/your_directory"))
}
r.Run()
}

Gorilla mux - modify request before passing it to routers

Is there a way to catch a *http.Request object before It will be parsed and forwarded to Gorilla mux router handler?
For example, we have some routing map with their handlers:
r := mux.NewRouter()
r.HandleFunc("/products/{key}", ProductHandler)
r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
I plan to use a dynamic language prefix (2 symbols). Example:
without language code (for default language option):
https://example.com/products/1
https://example.com/articels/2
with language code:
https://example.com/ru/products/1
https://example.com/ru/articels/2
Is there a way to catch full URL in the middleware, extract language (if exists) and then after some modifications pass It to Gorilla mux routers? It will help to build beautiful URLs:
https://example.com/products/1 <- default language
https://example.com/ru/products/1 <- russian language (same resource but in different language)
That looks more attractive than this variant:
https://example.com/en/products/1 <- mandatory default language
https://example.com/ru/products/1 <- russian language
Something like this will probably work:
r := mux.NewRouter()
r.HandleFunc("/products/{key}", ProductHandler)
r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
m := http.NewServeMux()
m.HandeFunc("/", func(w http.ResponseWriter, req *http.Request) {
// do something with req
r.ServeHTTP(w, req)
})
http.ListenAndServe(":8080", m)

Gorilla mux Handle any pattern with exception to static FileServer

I try to Handle any pattern another than "/app/*" to FileServer Handler. I use mux router to handle "/app/*" urls, and than I need to handle any others urls to static FileServer.This is my code:
r := mux.NewRouter()
r.Handle("/app/deleteComment", c.Handler(PreHandler(DeleteCommentHandler)))
r.Handle("/app/adminDeleteComment", c.Handler(PreHandler(AdminDeleteCommentHandler)))
(...)
fsa := justFilesFilesystem{http.Dir("build/")}
http.Handle("/", http.StripPrefix("/", http.FileServer(fsa)))
And now it works with pattern "/" but i want it to works with any pattern diffrent then "/app/*". I try it many ways but i think there is a simple solution how to handle it. Please help. Cheers.

mux.Vars not working

I'm running on HTTPS (port 10443) and use subroutes:
mainRoute := mux.NewRouter()
mainRoute.StrictSlash(true)
mainRoute.Handle("/", http.RedirectHandler("/static/", 302))
mainRoute.PathPrefix("/static/").Handler(http.StripPrefix("/static", *fh))
// Bind API Routes
apiRoute := mainRoute.PathPrefix("/api").Subrouter()
apiProductRoute := apiRoute.PathPrefix("/products").Subrouter()
apiProductRoute.Handle("/", handler(listProducts)).Methods("GET")
And the functions:
func listProducts(w http.ResponseWriter, r *http.Request) (interface{}, *handleHTTPError) {
vars := mux.Vars(r)
productType, ok := vars["id"]
log.Println(productType)
log.Println(ok)
}
ok is false and I have no idea why. I'm doing a simple ?type=model after my URL..
When you enter a URL like somedomain.com/products?type=model you're specifying a query string, not a variable.
Query strings in Go are accessed via r.URL.Query - e.g.
vals := r.URL.Query() // Returns a url.Values, which is a map[string][]string
productTypes, ok := vals["type"] // Note type, not ID. ID wasn't specified anywhere.
var pt string
if ok {
if len(productTypes) >= 1 {
pt = productTypes[0] // The first `?type=model`
}
}
As you can see, this can be a little clunky as it has to account for the map value being empty and for the possibility of a URL like somedomain.com/products?type=model&this=that&here=there&type=cat where a key can be specified more than once.
As per the gorilla/mux docs you can use route variables:
// List all products, or the latest
apiProductRoute.Handle("/", handler(listProducts)).Methods("GET")
// List a specific product
apiProductRoute.Handle("/{id}/", handler(showProduct)).Methods("GET")
This is where you would use mux.Vars:
vars := mux.Vars(request)
id := vars["id"]
Hope that helps clarify. I'd recommend the variables approach unless you specifically need to use query strings.
An easier way to solve this is to add query parameters in your route through Queries, like:
apiProductRoute.Handle("/", handler(listProducts)).
Queries("type","{type}").Methods("GET")
You can get it using:
v := mux.Vars(r)
type := v["type"]
NOTE: This might not have been possible when the question was originally posted but I stumbled across this when I encountered a similar problem and the gorilla docs helped.

Nesting subrouters in Gorilla Mux

I've been using gorilla/mux for my routing needs. But I noticed one problem, when I nest multiple Subrouters it doesn't work.
Here is the example:
func main() {
r := mux.NewRouter().StrictSlash(true)
api := r.Path("/api").Subrouter()
u := api.Path("/user").Subrouter()
u.Methods("GET").HandleFunc(UserHandler)
http.ListenAndServe(":8080", r)
}
I wanted to use this approach so I can delegate populating the router to some other package, for example user.Populate(api)
However this doesn't seem to work. It works only if I use single Subrouter in the chain.
Any ideas?
I figured it out, so I'll just post it here in case someone is as stupid as I was. :D
When creating path-based subrouter, you have to obtain it with PathPrefix instead of Path.
r.PathPrefix("/api").Subrouter()
Use r.Path("/api") only when attaching handlers to that endpoint.
For those who are struggling to split between auth and noauth routes, the following works fine for me:
r := mux.NewRouter()
noAuthRouter := r.MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool {
return r.Header.Get("Authorization") == ""
}).Subrouter()
authRouter := r.MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool {
return true
}).Subrouter()
Then you can apply middleware for authRouter only
If you need to Separate out the UI and API routers, you can simply do what the OP suggested:
appRouter := r.PathPrefix("/").Subrouter()
appRouter.Use(myAppRouter)
apiRouter := r.PathPrefix("/api").Subrouter()
apiRouter.Use(myAPIRouter)
Many thanks for the OP for providing the answer. Hopefully having it all in one place for my use case will help someone.

Resources