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?
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)
})
//...
}