Reputation: 31
Problem: Unable to access mux.CurrentRoute(r).GetName() from middleware. (Although I had been able to access it from my middleware, I had to change the way my middleware works due to it's previous inability to access the request). So I've mucked something up and I'm not sure how to get back to a working state where I can access the route name.
Any help would be much appreciated!
Error:
runtime error: invalid memory address or nil pointer dereference
Code:
func main() {
var (
err error
r *mux.Router
devRouter *mux.Router
usersRouter *mux.Router
brandsRouter *mux.Router
)
defer db.Close()
defer store.Close()
r = mux.NewRouter()
devRouter = r.PathPrefix("/api/v1/dev").Subrouter()
usersRouter = r.PathPrefix("/api/v1/users").Subrouter()
brandsRouter = r.PathPrefix("/api/v1/brands").Subrouter()
// development endpoints
devRouter.HandleFunc("/db/seed", devDbSeed)
...
// users
usersRouter.HandleFunc("/create", usersCreateHandlerFunc).Methods("POST").Name("USERS_CREATE")
...
// brands
brandsRouter.HandleFunc("/create", brandsCreateHandlerFunc).Methods("POST").Name("BRANDS_CREATE")
...
// products
brandsRouter.HandleFunc("/{brand_id:[0-9]+}/products", brandsProductsListHandlerFunc).Methods("GET").Name("BRANDS_PRODUCTS_LIST")
...
// mwAuthorize and mwAuthenticate basically work the same
mw := []func(http.Handler) http.Handler{mwAuthenticate, mwAuthorize}
http.Handle("/", use(r, mw...))
err = http.ListenAndServe(":9000", nil)
if err != nil {
logIt(err)
}
}
func use(h http.Handler, mw ...func(http.Handler) http.Handler) http.Handler {
// exec order: mw[0],mw[1],mw[N]...
for i := len(mw) - 1; i >= 0; i-- {
h = mw[i](h)
}
return h
}
func mwAuthorize(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if true != authorize(r) {
w.WriteHeader(http.StatusForbidden)
return
} else {
next.ServeHTTP(w, r)
}
})
}
func authorize(r *http.Request) (isAuthorized bool) {
isAuthorized = false
/**
This is where it's failing!
*/
routeName := mux.CurrentRoute(r).GetName()
switch routeName {
case "USERS_CREATE":
// route-specific authorization
break
...
default:
break
}
return
}
Update (2015-01-04 @ 4:49PM EST): So after removing the middleware (or at least commenting out the section that's trying to read mux.CurrentRoute) I am able to retrieve the route name from the destination handlerfunc (ex: usersCreateHandlerFunc or brandsCreateHandlerFunc). This doesn't solve my problem (I'd still like to perform authentication/authorization in middleware as opposed to every handlerfunc), I have a hunch it's letting me know *mux.Router isn't available in my middleware until after the final .ServeHTTP call. (Or something along those lines...)
Update (2015-01-04 @ 5:41PM EST): Tried a different (albeit less-preferred) direction of using Negroni as the middleware component. Still getting nil-pointer error when I try to get mux.CurrentRoute.
Update (2015-01-04 @ 6:17PM EST): I am able to access the request (ex: r.URL) from the middleware func's, but still no luck on accessing the mux.Route (ex: mux.CurrentRoute(r)). After looking a bit more at the mux source, I think it's because the current mux context isn't getting set because the router hasn't executed the matcher yet (and therefore it doesn't know what route it's currently on until AFTER the middleware is complete). However, I'm still not sure how to either resolve this, or re-structure my code to handle this.
Upvotes: 3
Views: 7245
Reputation: 1598
What about:
routeName := mux.CurrentRoute(r).GetName()
Where r
is the *http.Request
. Don't forget to import "github.com/gorilla/mux"
. Remember that in order to use this, you must give you route a name when you define it
Upvotes: 3
Reputation: 1316
From CurrentRoute godoc:
CurrentRoute returns the matched route for the current request, if any. This only works when called inside the handler of the matched route because the matched route is stored in the request context[...]
In your example, your chain of mwAuthenticate, mwAuthorize
is attached to the route "/" without using gorilla mux. That means when the request passes your handlers, it has not passed gorilla mux router.
Try the following (your example stripped down):
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
var (
err error
r *mux.Router
devRouter *mux.Router
)
func devDbSeed(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "devDbSeed")
return
}
func main() {
r = mux.NewRouter()
devRouter = r.PathPrefix("/api/v1/dev").Subrouter()
// mwAuthorize and mwAuthenticate basically work the same
mw := []func(http.Handler) http.Handler{mwAuthenticate, mwAuthorize}
// development endpoints
devRouter.Handle("/db/seed", use(http.HandlerFunc(devDbSeed), mw...)).Name("foo")
// Send all requests into the mux router
err = http.ListenAndServe(":9000", r)
if err != nil {
log.Fatal(err)
}
}
func use(h http.Handler, mw ...func(http.Handler) http.Handler) http.Handler {
// exec order: mw[0],mw[1],mw[N]...
for i := len(mw) - 1; i >= 0; i-- {
h = mw[i](h)
}
return h
}
func mwAuthorize(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !authorize(r) {
w.WriteHeader(http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
func mwAuthenticate(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
})
}
func authorize(r *http.Request) (isAuthorized bool) {
isAuthorized = false
handlerName := "UNKNOWN"
if route := mux.CurrentRoute(r); route != nil {
routeName := route.GetName()
if routeName != "" {
handlerName = routeName
}
}
log.Println(handlerName)
switch handlerName {
case "USERS_CREATE":
// route-specific authorization
log.Println("USERS_CREATE")
break
default:
break
}
return
}
Upvotes: 2
Reputation: 29
I had the same problem and I resolved in that way:
var match mux.RouteMatch
routeExists := s.Router.Match(r, &match)
if routeExists && match.Route.GetName(){
routeName := match.Route.GetName()
}
And when I defined the route I added .Name("route/:param")
where route/:param
is my route.
Upvotes: 0