guys i am working on coding a router called Garson , and i am trying to implement methods on my router that makes it easy to hook middlewares
i have 2 methods for that, one is called Before() and the other is called After() , the idea is, Before() calles middlewares before each request, and After() executes middlewares after each request.
in my ServeHTTP method, i am trying to execute each handler, my problem is when i execute the handler of the requested route, i am not able to execute the handler of the next middleware - which would be the first middleware of the middlewares registered with After()-, i have tried to call handler.ServeHTTP() and then next.ServeHTTP() but this only executes the first middleware registered with After() and ignores the others.
here is my ServeHTTP() method
here is example of usage, where middleware package is from github.com/emostafa/garson/middleware
func main() {
router := garson.New()
router.Before(middleware.Logger)
router.After(someCustomMiddlewareOfYours)
router.Get("/hello", hello)
http.ListenAndServe(":3000", router)
}
**评论:**
R2A2:
emostafa:Why not this?
func main() { router := garson.New() router.Get("/hello", anotherCustomMiddleware(hello)) http.ListenAndServe(":3000", someCustomMiddleware(middleware.Logger(router))) }
In this way a middleware can act on individual routes, or the whole router. No need to have before/after - just wrap at whichever level.
BTW I think you're totally on the right track with plain http.Handlers and with
func(http.Handler) http.Handler
- this makes your work more flexible than other routers & middlewares (negroni/httprouter/etc etc), which use their own custom parameter specs. Not a criticism of their authors - they were working in a pre-r.Context()
world, which forced their hands a little. Good for you.Edit: sorry I forgot to include an example which does middleware-type stuff before/after running the inner handler/router/middleware.
See below:
https://github.com/laher/context-example/blob/master/main.go#L124
That repo is just some code samples I made for a meetup talk. See readme for more notes.
Does that answer your question?
R2A2:@R2A2 my idea was that, you might need to execute some code after each response, for example, if you want to store some logs, how would you do that using:
router.Get("/hello", anotherCustomMiddleware(hello))
another thing that i have in mind, is if i have like 3 or 4 middlewares that i want to execute before the request, that would be something like this ?
router.Get("/hello", middleware1(middleware2(middleware3(hello))))
my idea that it could look like this
router.Get("/hello", hello).Before(middleware1, middleware2, middleware3).After(middleware4)
of Use() of the whole concept of Before() and After() is wrong: router.Use(LoggerMiddleware) router.Get("/hello", hello).Use(middleware1, middleware2, middleware3, middleware4)
glad you liked the idea of going with plain http.Handler, this way my router could be flexible and to be used with any other libraries. I am glad that i am did something good :D thanks for your motiviation.
Here's a logging example (not storing but hopefully it illustrates the idea):
func anotherCustomMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Println("start", time.Now(), r.Method, "-", r.RequestURI) next.ServeHTTP(w, r) log.Println("finish", time.Now(), r.Method, "-", r.RequestURI, w.Status) }) }
Sorry I don't really value the Before/After/Use thing that much. I think you'd typically have most middlewares wrapped around a single router, and then maybe zero/one/2 middlewares around the individual routes, so not very troublesome. If you're getting more complex than that I'd recommend nesting routers instead. I think Use() just adds an unnecessary layer. But, up to you.
