Golang Api仅匹配最后一条路线

Golang Api仅匹配最后一条路线,api,go,mux,Api,Go,Mux,我有一个golang api应用程序。我已经定义了一组路由和处理程序。但是,mux路由器只返回最后一条路由 当我请求/api/info时,我在日志中得到了以下信息: 9:0:38应用程序| 2018/02/05 09:00:38 GET/api/info用户创建308.132µs 为什么走错路线了 路由包: // NewRouter establishes the root application router func NewRouter(context *config.Application

我有一个golang api应用程序。我已经定义了一组路由和处理程序。但是,mux路由器只返回最后一条路由

当我请求
/api/info
时,我在日志中得到了以下信息:

9:0:38应用程序| 2018/02/05 09:00:38 GET/api/info用户创建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
,它将尝试调用一篇帖子到
/users/create
。但是,如果删除第二个路由,它将正确路由到
InfoShow
处理程序

为什么多路复用器要覆盖第一条路由?我相当肯定这辆车有毛病

HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    logRoute(setJSONHeader(route.HandlerFunc), route.Name)(context, w, r)
}) 
但我不知道为什么这会导致它在第一条路线上绘制地图


想法

通过阅读您的代码和gorilla/mux,我想我知道这个问题。在函数文本中使用for循环变量
route
,特别是它的字段HanderFunc,但由于函数文本的工作方式,在调用该函数文本之前,不会计算该字段的值。在Go中,范围循环中的第二个变量在每次迭代中都会被重用,而不是重新创建。因此,在for循环之后,如果它仍然在任何范围内(如函数文本),它将包含上一次循环迭代的值。这里有一个例子来说明我的意思:

在第一个示例中,
i
a
在每个循环迭代中都被重用,并且在lambda(函数文本)中不计算它们的值,直到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)
        })
}

通过阅读您的代码和gorilla/mux,我想我知道这个问题。在函数文本中使用for循环变量
route
,特别是它的字段HanderFunc,但由于函数文本的工作方式,在调用该函数文本之前,不会计算该字段的值。在Go中,范围循环中的第二个变量在每次迭代中都会被重用,而不是重新创建。因此,在for循环之后,如果它仍然在任何范围内(如函数文本),它将包含上一次循环迭代的值。这里有一个例子来说明我的意思:

在第一个示例中,
i
a
在每个循环迭代中都被重用,并且在lambda(函数文本)中不计算它们的值,直到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)
        })
}

请澄清您正在使用的
mux
软件包。@leadbebebop我正在使用最新的不相关软件包,但这是一种可怕的做法:
router:=router.NewRouter(&context,routes,notFoundHandler)
。您正在使用
router
变量跟踪
router
包。给他们起不同的名字。作为一种实践,建议不要按照别人从包中自然命名的类型命名包。请使用您在OP中指定的
路由
名称,而不是路由器。这样可以减少代码混淆。谢谢。刚来的。这是无意的,我会重新命名它。我试图做的全部工作就是将数据库传递给处理程序。围棋迫使我把所有的东西都分成几个包,但从根本上说,这些东西都是一个包,所以我很难找到一个好的结构。如果你愿意,你可以把它们都放在同一个包里。分离它们可能是一种很好的做法,因为它可以让您更好地隔离关注点,但Go中没有任何东西会强迫您将代码分离到包中,除非您希望获得单独名称空间的文档优势。请澄清您使用的是什么
mux
包。@leadbebebebop我使用的是最新的,但这是一种可怕的做法:
router:=router.NewRouter(&context,routes,notFoundHandler)
。您正在使用
router
变量跟踪
router
包。给他们起不同的名字。作为一种实践,建议不要按照别人从包中自然命名的类型命名包。请使用您在OP中指定的
路由
名称,而不是路由器。这样可以减少代码混淆。谢谢。刚来的。这是无意的,我会重新命名它。我试图做的全部工作就是将数据库传递给处理程序。围棋迫使我把所有的东西都分成几个包,但从根本上说,这些东西都是一个包,所以我很难找到一个好的结构。如果你愿意,你可以把它们都放在同一个包里。分离它们可能是一种很好的做法,因为它可以让您更好地隔离关注点,但是在Go中没有任何东西可以强迫您将代码分离到包中,除非您希望获得单独名称空间的文档优势。啊,这太有意义了!谢谢你的例子。我稍后会测试,但基于你的操场,我认为这是绝对正确的。我想知道,这种迭代设置一系列路由的模式是否真的是一种好方法,我见过更糟的。它允许您在
main
中指定路由,同时实际处理将它们连接到路由包中的路由器的操作。这设计很好,贝考