douzi2778 2016-10-06 21:05
浏览 19
已采纳

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

I am trying to implement expressjs like features to httprouter package . I create a struct type mounter

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

and a Route struct which represents subRoutes

type Route struct {
    Path   string
    Method string
    Func   Handle
}

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

type Params interface{}

i have a NewRoutes Function which is the main thing i wanted to port from expressjs new routes does the same thing as express.Router

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

and i have get post put delete methods under *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})
}

and finally i have a Mount method which mounts the router to the actual router

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

everything works pretty nice and the methods are working fine too if i try to send a post request to a get endpoint it gives a nice 404 but the only issue is it always responds with the handler of last added member regardless of subpath so

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

Will always respond test/post and the output of the range i am doing above for test purposes is So do you have any idea why it uses always the same function to respond but recognizes paths perfectly?

  • 写回答

1条回答 默认 最新

  • doushang4293 2016-10-08 05:21
    关注

    The problem lies in Mount method, which is known as Iteration Variables and Closures. Declare a new variable for capturing route e.g.

    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)
        })
    //...
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 乌班图ip地址配置及远程SSH
  • ¥15 怎么让点阵屏显示静态爱心,用keiluVision5写出让点阵屏显示静态爱心的代码,越快越好
  • ¥15 PSPICE制作一个加法器
  • ¥15 javaweb项目无法正常跳转
  • ¥15 VMBox虚拟机无法访问
  • ¥15 skd显示找不到头文件
  • ¥15 机器视觉中图片中长度与真实长度的关系
  • ¥15 fastreport table 怎么只让每页的最下面和最顶部有横线
  • ¥15 java 的protected权限 ,问题在注释里
  • ¥15 这个是哪里有问题啊?