代码之家  ›  专栏  ›  技术社区  ›  nikoss

Golang httpRouter在与函数片一起使用时返回最后一个响应

  •  0
  • nikoss  · 技术社区  · 8 年前

    我正在努力实现 expressjs 喜欢的功能 httprouter 包裹 我创建了一个结构 type mounter

    type Mounter struct {
        BasePath string
        Routes   []*Route
    }
    

    Route 表示子例程的结构

    type Route struct {
        Path   string
        Method string
        Func   Handle
    }
    

    type Handle func(http.ResponseWriter, *http.Request, Params)

    type Params interface{}

    我有一个 NewRoutes 函数,这是我想从expressjs新路由中移植的主要功能,它与 express.Router

    func NewRoutes(base string) (mounter *Mounter) {
        mounter = &Mounter{
            BasePath: base,
        }
        return
    }
    

    我有 get post put delete *Mounter

    //GET request handler
    func (mounter *Mounter) GET(path string, Func Handle) {
        mounter.Routes = append(mounter.Routes, &Route{path, "get", Func})
    }
    
    //POST request handler
    func (mounter *Mounter) POST(path string, Func Handle) {
        mounter.Routes = append(mounter.Routes, &Route{path, "post", Func})
    }
    
    //PUT request handler
    func (mounter *Mounter) PUT(path string, Func Handle) {
        mounter.Routes = append(mounter.Routes, &Route{path, "put", Func})
    }
    
    //DELETE request handler
    func (mounter *Mounter) DELETE(path string, Func Handle) {
        mounter.Routes = append(mounter.Routes, &Route{path, "delete", Func})
    }
    

    最后,我有一个挂载方法,将路由器挂载到实际路由器

    func (mounter *Mounter) Mount(router *rtr.Router) {
        mounter.BasePath = strings.TrimSuffix(mounter.BasePath, "/")
        for _, route := range mounter.Routes {
            path := route.Path
            if !strings.HasSuffix(path, "/") {
                path += "/"
            }
            path = mounter.BasePath + path
            switch strings.ToLower(route.Method) {
            case "get":
                router.GET(path, func(res http.ResponseWriter, req *http.Request, params rtr.Params) {
                    route.Func(res, req, params)
                })
            case "post":
                router.POST(path, func(res http.ResponseWriter, req *http.Request, params rtr.Params) {
                    route.Func(res, req, params)
                })
            case "delete":
                router.DELETE(path, func(res http.ResponseWriter, req *http.Request, params rtr.Params) {
                    route.Func(res, req, params)
                })
            case "put":
                router.PUT(path, func(res http.ResponseWriter, req *http.Request, params rtr.Params) {
                    route.Func(res, req, params)
                })
            }
        }
    }
    

    一切都很好,方法也很好。如果我尝试向get端点发送post请求,它会给出一个很好的404,但唯一的问题是它总是使用最后添加的成员的处理程序进行响应,而不管子路径如何

    package api
    var ApiRouter = express.NewRoutes("/api/")
    
    func init() {
        ApiRouter.GET("/", func(res http.ResponseWriter, req *http.Request, _ express.Params) {
            fmt.Fprintln(res, "testget/")
        })
        ApiRouter.GET("/pt", func(res http.ResponseWriter, req *http.Request, _ express.Params) {
            fmt.Fprintln(res, "pt")
        })
        ApiRouter.POST("/test", func(res http.ResponseWriter, req *http.Request, _ express.Params) {
            fmt.Fprintln(res, "test/post")
        })
    }
    
    package main
    func main() {
        router := express.New()
        api.ApiRouter.Mount(router)
        for _, route := range api.ApiRouter.Routes {
            fmt.Println(*route)
        }
        router.ServeFiles("/public/*filepath", http.Dir("./public/"))
        http.ListenAndServe(":1024", router)
    }
    

    总是会回应 test/post range 我以上所做的是为了测试目的 那么,你知道为什么它总是使用相同的函数来响应,但却能完美地识别路径吗?

    1 回复  |  直到 8 年前
        1
  •  1
  •   putu    8 年前

    问题在于 Mount 方法,称为 Iteration Variables and Closures .声明用于捕获的新变量 route 例如

    thisRoute := route //capture iteration variable
    switch strings.ToLower(route.Method) {
    case "get":
        router.GET(path, func(res http.ResponseWriter, req *http.Request, params rtr.Params) {
            thisRoute.Func(res, req, params)
        })
    case "post":
        router.POST(path, func(res http.ResponseWriter, req *http.Request, params rtr.Params) {
            thisRoute.Func(res, req, params)
        })
    //...
    }