Golang Api 只匹配最后一条路线
Golang Api Only Matches Last Route
我有一个 golang api 应用程序。我定义了一组路由和处理程序。但是,多路复用路由器仅 returns 最后一条路线。
当我请求 /api/info
时,我在我的日志记录中得到了这个:
9:0:38 app | 2018/02/05 09:00:38 GET /api/info Users Create 308.132µs
为什么路由到错误的路由?
路由包:
// NewRouter establishes the root application router
func NewRouter(context *config.ApplicationContext, routes Routes, notFoundHandler http.HandlerFunc) *mux.Router {
router := mux.NewRouter()
router.NotFoundHandler = notFoundHandler
for _, route := range routes {
router.
PathPrefix("/api").
Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
// TODO: fix HandlerFunc. Right now, it is overriding previous routes and setting a single handler for all
// this means that the last route is the only router with a handler
HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logRoute(setJSONHeader(route.HandlerFunc), route.Name)(context, w, r)
})
}
return router
}
func logRoute(inner ContextHandlerFunc, name string) ContextHandlerFunc {
return func(c *config.ApplicationContext, w http.ResponseWriter, r *http.Request) {
start := time.Now()
inner(c, w, r)
log.Printf(
"%s\t%s\t%s\t%s",
r.Method,
r.RequestURI,
name,
time.Since(start),
)
}
}
func setJSONHeader(inner ContextHandlerFunc) ContextHandlerFunc {
return func(c *config.ApplicationContext, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
inner(c, w, r)
}
}
主包:
var context = config.ApplicationContext{
Database: database.NewDatabase().Store,
}
var routes = router.Routes{
router.Route{"Info", "GET", "/info", handlers.InfoShow},
router.Route{"Users Create", "POST", "/users/create", handlers.UsersCreate},
}
func main() {
notFoundHandler := handlers.Errors404
router := router.NewRouter(&context, routes, notFoundHandler)
port := os.Getenv("PORT")
log.Fatal(http.ListenAndServe(":"+port, router))
}
如果我访问 /api/info
,它将尝试调用 POST 到 /users/create
。但是,如果我删除第二条路由,它将正确路由到 InfoShow
处理程序。
为什么 mux 覆盖了第一条路线?我相当确定
有问题
HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logRoute(setJSONHeader(route.HandlerFunc), route.Name)(context, w, r)
})
但我不确定为什么这会导致它映射到第一条路线上。
想法?
通读您的代码和 gorilla/mux,我想我知道了这个问题。您在函数文字中使用了 for 循环变量 route
,特别是它的字段 HanderFunc,但是由于函数文字的工作方式,该字段的 value 不是评估直到调用该函数文字。在 Go 中,范围循环中的第二个变量在每次迭代中被 重用 ,而不是重新创建,因此在 for 循环之后,如果它仍然在任何范围内(比如你的函数文字),它将包含 last 循环迭代的值。这是我的意思的一个例子:
https://play.golang.org/p/Xx62tuwhtgG
package main
import (
"fmt"
)
func main() {
var funcs []func()
ints := []int{1, 2, 3, 4, 5}
// How you're doing it
for i, a := range ints {
fmt.Printf("Loop i: %v, a: %v\n", i, a)
funcs = append(funcs, func() {
fmt.Printf("Lambda i: %v, a: %v\n", i, a)
})
}
for _, f := range funcs {
f()
}
fmt.Println("-------------")
// How you *should* do it
funcs = nil
for i, a := range ints {
i := i
a := a
fmt.Printf("Loop i: %v, a: %v\n", i, a)
funcs = append(funcs, func() {
fmt.Printf("Lambda i: %v, a: %v\n", i, a)
})
}
for _, f := range funcs {
f()
}
}
在第一个示例中,i
和 a
在每个循环迭代中重复使用,并且在 lambda 实际上是 调用(通过funcs
循环)。要解决此问题,您可以通过在循环迭代的范围内(但在 lambda 的范围之外) 内 重新声明它们来隐藏 a
和 i
。这会为每次迭代创建一个单独的副本,以避免重复使用相同变量的问题。
特别针对您的代码,如果您将代码更改为以下内容,它应该可以工作:
for _, route := range routes {
route := route // make a copy of the route for use in the lambda
// or alternatively, make scoped vars for the name and handler func
router.
PathPrefix("/api").
Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
// TODO: fix HandlerFunc. Right now, it is overriding previous routes and setting a single handler for all
// this means that the last route is the only router with a handler
HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logRoute(setJSONHeader(route.HandlerFunc), route.Name)(context, w, r)
})
}
我有一个 golang api 应用程序。我定义了一组路由和处理程序。但是,多路复用路由器仅 returns 最后一条路线。
当我请求 /api/info
时,我在我的日志记录中得到了这个:
9:0:38 app | 2018/02/05 09:00:38 GET /api/info Users Create 308.132µs
为什么路由到错误的路由?
路由包:
// NewRouter establishes the root application router
func NewRouter(context *config.ApplicationContext, routes Routes, notFoundHandler http.HandlerFunc) *mux.Router {
router := mux.NewRouter()
router.NotFoundHandler = notFoundHandler
for _, route := range routes {
router.
PathPrefix("/api").
Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
// TODO: fix HandlerFunc. Right now, it is overriding previous routes and setting a single handler for all
// this means that the last route is the only router with a handler
HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logRoute(setJSONHeader(route.HandlerFunc), route.Name)(context, w, r)
})
}
return router
}
func logRoute(inner ContextHandlerFunc, name string) ContextHandlerFunc {
return func(c *config.ApplicationContext, w http.ResponseWriter, r *http.Request) {
start := time.Now()
inner(c, w, r)
log.Printf(
"%s\t%s\t%s\t%s",
r.Method,
r.RequestURI,
name,
time.Since(start),
)
}
}
func setJSONHeader(inner ContextHandlerFunc) ContextHandlerFunc {
return func(c *config.ApplicationContext, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
inner(c, w, r)
}
}
主包:
var context = config.ApplicationContext{
Database: database.NewDatabase().Store,
}
var routes = router.Routes{
router.Route{"Info", "GET", "/info", handlers.InfoShow},
router.Route{"Users Create", "POST", "/users/create", handlers.UsersCreate},
}
func main() {
notFoundHandler := handlers.Errors404
router := router.NewRouter(&context, routes, notFoundHandler)
port := os.Getenv("PORT")
log.Fatal(http.ListenAndServe(":"+port, router))
}
如果我访问 /api/info
,它将尝试调用 POST 到 /users/create
。但是,如果我删除第二条路由,它将正确路由到 InfoShow
处理程序。
为什么 mux 覆盖了第一条路线?我相当确定
有问题HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logRoute(setJSONHeader(route.HandlerFunc), route.Name)(context, w, r)
})
但我不确定为什么这会导致它映射到第一条路线上。
想法?
通读您的代码和 gorilla/mux,我想我知道了这个问题。您在函数文字中使用了 for 循环变量 route
,特别是它的字段 HanderFunc,但是由于函数文字的工作方式,该字段的 value 不是评估直到调用该函数文字。在 Go 中,范围循环中的第二个变量在每次迭代中被 重用 ,而不是重新创建,因此在 for 循环之后,如果它仍然在任何范围内(比如你的函数文字),它将包含 last 循环迭代的值。这是我的意思的一个例子:
https://play.golang.org/p/Xx62tuwhtgG
package main
import (
"fmt"
)
func main() {
var funcs []func()
ints := []int{1, 2, 3, 4, 5}
// How you're doing it
for i, a := range ints {
fmt.Printf("Loop i: %v, a: %v\n", i, a)
funcs = append(funcs, func() {
fmt.Printf("Lambda i: %v, a: %v\n", i, a)
})
}
for _, f := range funcs {
f()
}
fmt.Println("-------------")
// How you *should* do it
funcs = nil
for i, a := range ints {
i := i
a := a
fmt.Printf("Loop i: %v, a: %v\n", i, a)
funcs = append(funcs, func() {
fmt.Printf("Lambda i: %v, a: %v\n", i, a)
})
}
for _, f := range funcs {
f()
}
}
在第一个示例中,i
和 a
在每个循环迭代中重复使用,并且在 lambda 实际上是 调用(通过funcs
循环)。要解决此问题,您可以通过在循环迭代的范围内(但在 lambda 的范围之外) 内 重新声明它们来隐藏 a
和 i
。这会为每次迭代创建一个单独的副本,以避免重复使用相同变量的问题。
特别针对您的代码,如果您将代码更改为以下内容,它应该可以工作:
for _, route := range routes {
route := route // make a copy of the route for use in the lambda
// or alternatively, make scoped vars for the name and handler func
router.
PathPrefix("/api").
Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
// TODO: fix HandlerFunc. Right now, it is overriding previous routes and setting a single handler for all
// this means that the last route is the only router with a handler
HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logRoute(setJSONHeader(route.HandlerFunc), route.Name)(context, w, r)
})
}