How to use a middleware for individual route? I wanna use different middleware per route and don't want apply middleware on group routes level. Is it possible to do that? how?
You have two options:
1) The simple option - make a group that only has a single route in it, and apply the route specific middleware to that the group.
2) If for some reason you don't want to do 1, you can manually wrap all of your route handlers in your middleware. Remember that a middleware is just a function that accepts a handler and returns a handler. So something kinda like;
middleware1(middleware2(handler))
Will just return a handler with all the middleware applied.
I would also suggest in the event you have a middleware that only applies to a single route, you might consider not having that functionality in a middleware at all.
Although I don't know your specific use case, generally speaking middleware are intended to function are shared functionality allowing for DRY handlers.
@syntaqx +1
If these middleware will exclusively be used for this route, incorporate them into the handler.
Hi, thanks for yours anwser.
Maybe this title's issue and its explanation is unclear and misleading. Sorry for that.
Btw, Can echo do something such this?
a := mw([]e.Middlewares{jwtauth, checkuser})
b := mw([]e.Middlewares{jwtauth, checkIdField, checkWhoRequest})
e.Get("/", a(XHandler))
e.Post("/", b(YHandler))
I'm a little confused about what you're asking. In your example, a and b are slices of middleware. They are not functions, so you cannot try to invoke them and pass in a handler. This is syntactically invalid.
@axdg: edited. Thanks for correction
You can implement this in a few ways, but to follow suite with @axdg's initial recommendation, you could use something like the following:
e.Use(jwtauth)
e.Get("/", checkuser(XHandler))
e.Post("/", checkIdField(checkWhoRequest(YHandler)))
Or optionally (depending on the number of handlers in play), you could simply attach it to your handler directly (which imho is a bit nicer for initial development when things are a bit subject to change)
e.Use(jwtauth)
e.Get("/", YHandler)
e.Post("/", XHandler)
Used with:
func XHandler(c *echo.Context) error {
checkuser(c)
// ...
}
func YHandler(c *echo.Context) error {
checkIdField(c)
checkWhoRequest(c)
// ...
}
Either of the above implementations are functionally similar, and you can wrap them in any number of ways (ie, having a closure that wraps a closure that wraps your handler, etc.), but the end result is pretty much the same.
As you end up having more cross over with multiple middlewares, I recommend utilizing groups and specifying middlewares similar to the following to achieve what you're wanting:
grp := e.Group("/base")
grp.Use(middleware1, middleware2, middleware3)
grp.Get("/", SomeHandler)
However, you will still need to wrap handlers when using multiple middlewares on an individual handler basis.
Hi @syntaqx I am interested with this style:
func YHandler(c *echo.Context) error {
checkIdField(c)
checkWhoRequest(c)
// ...
}
Could you explain me more the flow, how to create a middleware in such fashion?
Because it ended up with this warning
response already committed
Could you give me an example of the code you're currently using and I can diagnose? The example I used made a lot of assumptions, and might not be ideal for your implementation.
These codes below are just an example to grasp the concept, correct me if i'm wrong, because I'm new with echo
this doesn't effect
func XHandler(c *echo.Context) error {
checkuser(c)
// ...
}
func checkuser(c *echo.Context) echo.MiddlewareFunc {
return func(h echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) error {
if c.Query("user") == "test" {
return c.String(http.StatusNotFound, "not found")
}
}
}
}
this get me warn message
func checkuser(c *echo.Context) error {
if c.Query("user") == "test" {
return c.String(http.StatusNotFound, "not found")
}
}
While I don't prefer middleware on group, because my routes seems don't fit:
// route group you describe above
grp := e.Group("/base")
grp.Use(middleware1, middleware2, middleware3)
grp.Get("/", SomeHandler)
// my route example
user := e.Group("/user")
user.Use(checkJwt)
// user details and comments don't use jwt
user.Get("/:username", UserDetails)
user.Get("/:username/comments", UserComments)
// all of these use jwt
user.Post("/", CreateUser)
user.Put("/", UpdateUser)
user.Delete("/", DeleteUser)
// this use middleware named checkUser, checkId, etc how?
// It seems too long if I wrap it
// checkUser(checkId(etcMw1(etcMw2(etcMw3(CreateComment))))
// the code becomes unreadable
user.Post("/comment", CreateComment)
Once again, CMIIW
Actually I am looking middleware feature such as in express.js:
var a = [mw1, mw2, mw3]
var b = [mw4, mw33]
var c = [mw1, mw3, mw4]
app.post("/user", a, User.create)
app.put("/user", b, User.update)
app.delete("/user", c, User.delete)
@raitucarp In this case you can simply use a, b or c middleware stack inside the handlers. Something like below:
a = [func(c *echo.Context) error { println("1") }, func(c *echo.Context) error { println("2") }]
e.Post("/user", func(c *echo.Context) error {
for _, m := range(a) {
if err := m(c); err != nil {
return err
}
}
// handler logic
})
Hi @vishr thanks for your code. But, actually, I still confuse. How to create proper middleware?
for example:
func YHandler(c *echo.Context) error {
checkIdField(c)
checkWhoRequest(c)
// ...
}
checkIdField is checking id field, if there is empty id field, then it will send json with codes internal server error, checkWhoRequest will not run, and YHandler will not run
checkWhoRequest is also doing the same, YHandler will not execute if there is an error instead, send error response either json or html,
but currently, I ended it up with middleware not running or warning message response already committed
Correct me if I'm wrong. So how to create proper middleware?
And I remind you again about my case @vishr:
// route group you describe above
grp := e.Group("/base")
grp.Use(middleware1, middleware2, middleware3)
grp.Get("/", SomeHandler)
// my route example
user := e.Group("/user")
user.Use(checkJwt)
// user details and comments don't use jwt
user.Get("/:username", UserDetails)
user.Get("/:username/comments", UserComments)
// all of these use jwt
user.Post("/", CreateUser)
user.Put("/", UpdateUser)
user.Delete("/", DeleteUser)
// this use middleware named checkUser, checkId, etc how?
// It seems too long if I wrap it
// checkUser(checkId(etcMw1(etcMw2(etcMw3(CreateComment))))
// the code becomes unreadable
user.Post("/comment", CreateComment)
@raitucarp If you want to abort request anywhere in the middleware, just return. So if checkIdField fails check for error and return (don't execute other middleware and handler). For reference, https://github.com/labstack/echo/blob/master/middleware/auth.go#L49.
what's wrong with this?
I tried with this style:
func XHandler(c *echo.Context) error {
checkuser(c)
// ...
}
Here is the result
func checkId(ctx *echo.Context) error {
if ctx.Param("id") == "3" {
return ctx.String(http.StatusNotFound, "test")
}
return nil
}
func GetUser(ctx *echo.Context) error {
checkId(ctx)
return ctx.String(http.StatusOK, "yes")
}
I receive this message
WARN|echo|response already committed
And the responses are:
http://localhost/user/1 ==> "yes"
http://localhost/user/3 ==> "testyes"
@vishr @syntaqx
btw what should I do with this case?
// route group you describe by syntaqx
grp := e.Group("/base")
grp.Use(middleware1, middleware2, middleware3)
grp.Get("/", SomeHandler)
// my route example
user := e.Group("/user")
user.Use(checkJwt)
// user details and comments don't use jwt
user.Get("/:username", UserDetails)
user.Get("/:username/comments", UserComments)
// all of these use jwt
user.Post("/", CreateUser)
user.Put("/", UpdateUser)
user.Delete("/", DeleteUser)
// this use middleware named checkUser, checkId, etc how?
// It seems too long if I wrap it
// checkUser(checkId(etcMw1(etcMw2(etcMw3(CreateComment))))
// the code becomes unreadable, and spaghetti code
user.Post("/comment", CreateComment)
It would be nice if echo has feature:
user := e.Group("/user")
// user details and comments don't use jwt
user.Get("/:username", UserDetails)
user.Get("/:username/comments", UserComments)
// all of these use jwt
user.Post("/", CreateUser).Use(jwtAuth, checkUser)
user.Put("/", UpdateUser).Use(jwtAuth, checkUser, checkA)
user.Delete("/", DeleteUser).Use(jwtAuth, checkUser, checkB)
// custom middleware
user.Post("/comment", CreateComment).Use(checkId, etcMw1, etcMw2, etcMw3)
Or this
auth := e.Middlewares(jwtAuth, checkUser)
custom := e.Middlwares(checkId, etcMw1, etcMw2, etcMw3)
user := e.Group("/user")
// user details and comments don't use jwt
user.Get("/:username", UserDetails)
user.Get("/:username/comments", UserComments)
// all of these use jwt
user.Post("/", auth(CreateUser))
user.Put("/", auth(UpdateUser))
user.Delete("/", auth(DeleteUser))
// custom middleware
user.Post("/comment", custom(CreateComment))
You can let all user routes use JWT middleware but inside the middleware filter based on method or parameter or any other property.
If I use middleware for group, what about the get method?
user := e.Group("/user")
user.Use(jwt)
user.Get("/:username", UserDetails) // this is not using jwt... How??
user.Post("/", CreateUser)
I think it's being ugly route:
restricted := e.Group("/restricted")
restricted.Post("/user", UserCreate)
nonRestricted := e.Group("/nonrestricted")
nonRestricted.Get("/user", UserDetails)
the result is ugly endpoint
POST http://localhost/restricted/user
GET http://localhost/nonrestricted/user
What??
Not like this, inside your jwt middleware only check for post method and let it skip get.
I think the condition should be reversed.
what about POST method that doesn't consume the middleware? Ignore it or creating more filter such that? Because it would be hard if route design is complex and keep DRY
You should also check this post: https://medium.com/@matryer/writing-middleware-in-golang-and-how-go-makes-it-so-much-fun-4375c1246e81#.vs27uzaxp
@raitucarp I have added a new API to chain middleware at route level. https://godoc.org/github.com/labstack/echo#Use
Hi @vishr Thank you so much. I will try it
@raitucarp I have added a new API to chain middleware at route level. https://godoc.org/github.com/labstack/echo#Use
@vishr I didn't find this in latest version. Was that been removed?
Nvm, realized http methods itself support middleware. Thanks much.
Most helpful comment
It would be nice if echo has feature:
Or this