从中间件中获取gorilla/mux路由器的当前路由名称。

huangapple go评论77阅读模式
英文:

Get gorilla/mux router current route name from middleware

问题

问题:
无法从中间件访问mux.CurrentRoute(r).GetName()。(尽管我之前能够从我的中间件访问它,但由于之前无法访问请求,我不得不更改我的中间件的工作方式)。所以我搞砸了一些东西,不确定如何恢复到一个可以访问路由名称的工作状态。

非常感谢任何帮助!

错误:

运行时错误:无效的内存地址或空指针解引用

代码:

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
    /**
       这就是出错的地方!
    */
	routeName := mux.CurrentRoute(r).GetName()
	switch routeName {
	case "USERS_CREATE":
        // route-specific authorization
		break
    ...
	default:
		break
	}
	return
}

更新(2015-01-04 @ 4:49PM EST):
因此,在删除中间件之后(或者至少注释掉尝试读取mux.CurrentRoute的部分)我能够从目标handlerfunc(例如:usersCreateHandlerFunc或brandsCreateHandlerFunc)中检索路由名称。这并没有解决我的问题(我仍然希望在中间件中执行身份验证/授权,而不是在每个handlerfunc中执行),我有一种直觉,它告诉我*mux.Router在最后的.ServeHTTP调用之后才可用于我的中间件。(或类似的情况...)

更新(2015-01-04 @ 5:41PM EST):
尝试了一个不同(尽管不太受欢迎)的方向,使用Negroni作为中间件组件。当我尝试获取mux.CurrentRoute时,仍然会出现空指针错误。

更新(2015-01-04 @ 6:17PM EST):
我能够从中间件函数中访问请求(例如:r.URL),但是仍然无法访问mux.Route(例如:mux.CurrentRoute(r))。在更详细地查看mux源代码后,我认为这是因为当前的mux上下文没有设置,因为路由器尚未执行匹配器(因此在中间件完成之后才知道当前所在的路由)。然而,我仍然不确定如何解决这个问题,或者重新构造我的代码以处理这个问题。

英文:

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.

答案1

得分: 3

routeName := mux.CurrentRoute(r).GetName()

其中 r*http.Request。不要忘记导入 github.com/gorilla/mux。记住,在定义路由时,必须给它一个名称。

英文:

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

答案2

得分: 2

CurrentRoute godoc中可以得知:
> CurrentRoute函数返回当前请求的匹配路由(如果有)。这仅在调用匹配路由的处理程序内部调用时有效,因为匹配路由存储在请求上下文中[...]。

在你的示例中,你的mwAuthenticate, mwAuthorize链附加到了路由"/"上,而没有使用gorilla mux。这意味着当请求经过你的处理程序时,它还没有经过gorilla mux路由器。

尝试以下代码(简化了你的示例):

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和mwAuthenticate基本上是相同的
	mw := []func(http.Handler) http.Handler{mwAuthenticate, mwAuthorize}

	// 开发端点
	devRouter.Handle("/db/seed", use(http.HandlerFunc(devDbSeed), mw...)).Name("foo")

	// 将所有请求发送到mux路由器
	err = http.ListenAndServe(":9000", r)
	if err != nil {
		log.Fatal(err)
	}
}

func use(h http.Handler, mw ...func(http.Handler) http.Handler) http.Handler {
	// 执行顺序: 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":
		// 特定路由的授权
		log.Println("USERS_CREATE")
		break
	default:
		break
	}
	return
}
英文:

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
}

答案3

得分: 0

我遇到了同样的问题,我是这样解决的:

var match mux.RouteMatch
routeExists := s.Router.Match(r, &match)
if routeExists && match.Route.GetName(){
routeName := match.Route.GetName()
}

当我定义路由时,我添加了.Name("route/:param"),其中route/:param是我的路由。

英文:

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.

huangapple
  • 本文由 发表于 2015年1月5日 02:06:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/27768655.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定