api - Golang Api 只匹配最后一条路线

标签 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.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 循环之后,如果它仍在任何范围内(比如你的函数文字),它将包含最后 循环迭代的值。这是我的意思的一个例子:

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()
    }
}

在第一个例子中,ia在每个循环迭代中重复使用,并且在 lambda 实际调用(通过 funcs 循环)之前,不会在 lambda(函数文字)中计算它们的值。要解决此问题,您可以隐藏 ai通过在循环迭代的范围内(但在 lambda 的范围之外)重新声明它们。这会为每次迭代创建一个单独的副本,以避免重复使用相同变量的问题。

特别是对于您的代码,如果您将代码更改为以下内容,它应该可以工作:

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)
        })
}

关于api - Golang Api 只匹配最后一条路线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48627636/

相关文章:

ios - 为什么我的 Collection View Cell 中显示为 nil?

http.FileServer 缓存文件并在编辑后提供旧版本

facebook - 如何使用 facebook api 从群组中获取待售帖子

html - 从 Spotify 应用程序访问播放列表文件夹结构?

unix - syscall.Stat_t.Dev 映射到什么?

go - 如何检查文件是否是 GoLang 中的 tar 文件?

google-app-engine - 通过goroutine异步发布到google pub sub

go - 如何允许CORS使用多个端口?

api - h.264解码器的 "invalid NAL unit size"是什么意思?

javascript - 如何使用谷歌通讯录服务读取日期(出生)