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

报告相同问题?

悬赏问题

  • ¥20 java在应用程序里获取不到扬声器设备
  • ¥15 echarts动画效果的问题,请帮我添加一个动画。不要机器人回答。
  • ¥60 许可证msc licensing软件报错显示已有相同版本软件,但是下一步显示无法读取日志目录。
  • ¥15 Attention is all you need 的代码运行
  • ¥15 一个服务器已经有一个系统了如果用usb再装一个系统,原来的系统会被覆盖掉吗
  • ¥15 使用esm_msa1_t12_100M_UR50S蛋白质语言模型进行零样本预测时,终端显示出了sequence handled的进度条,但是并不出结果就自动终止回到命令提示行了是怎么回事:
  • ¥15 前置放大电路与功率放大电路相连放大倍数出现问题
  • ¥30 关于<main>标签页面跳转的问题
  • ¥80 部署运行web自动化项目
  • ¥15 腾讯云如何建立同一个项目中物模型之间的联系