echo icon indicating copy to clipboard operation
echo copied to clipboard

Apply Middleware to a path (group, route, etc.)

Open bubbajoe opened this issue 4 years ago • 9 comments
trafficstars

Issue Description

Hey guys, I just switched from gin to echo, and I am having an issue. How do I apply a middleware to a specific group (route). I couldn't find any reference to this in the docs either.

Expected behaviour

MIddleware should only be applied to the userGroup ("/users/*")

Actual behaviour

MIddleware is applied to all routes

userGroup := e.Group("/users")
{
  userGroup.Use(middleware.CheckPermissions)
  userGroup.GET("/:id", userController.GetByID)
}
e.GET("/self", userController.GetSelf)

bubbajoe avatar Dec 29 '20 10:12 bubbajoe

Third argument for route can be middleware(s)

https://github.com/labstack/echo/blob/b065180250530e6434de09ae5a2e4443dc7cb210/echo.go#L412

same way can be used for group also https://github.com/labstack/echo/blob/b065180250530e6434de09ae5a2e4443dc7cb210/echo.go#L552

aldas avatar Dec 29 '20 11:12 aldas

As aldas said you just need to pass the middleware as additional argument to the group function:

e := echo.New()
e.GET("/self", userController.GetSelf)
// Guard group /users with middleware.CheckPermissions
userGroup := e.Group("/users", middleware.CheckPermissions)
userGroup.GET("/:id", userController.GetByID)

Please close if that works for you.

lammel avatar Jan 01 '21 17:01 lammel

I can't reproduce the issue as I am able to use a middleware to a specific group. I am using echo v4 (github.com/labstack/echo/v4).

e := echo.New()
...

// Unauthenticated routes
publicRoutes := e.Group("/v1")
{
    publicRoutes.GET("/login", func(c echo.Context) error {
        return c.HTML(http.StatusOK, login)
    })
}

// Restricted routes
protectedRoutes := e.Group("/v1")
{
    config := middleware.JWTConfig{
        KeyFunc: handlers.GetKey, // https://github.com/labstack/echo/issues/1731
    }
    protectedRoutes.Use(middleware.JWTWithConfig(config))
    protectedRoutes.POST("/auth", handlers.Login)

    user := protectedRoutes.Group("/user")
    {
        user.GET("/:id", handlers.GetUser)
        user.POST("", handlers.CreateUser)
        user.PUT("", handlers.UpdateUser)
        user.DELETE("/:id", handlers.DeleteUser)
    }
}

Another example can be found on: https://echo.labstack.com/cookbook/jwt

e := echo.New()

// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())

// Login route
e.POST("/login", login)

// Unauthenticated route
e.GET("/", accessible)

// Restricted group
r := e.Group("/restricted")
r.Use(middleware.JWT([]byte("secret")))
r.GET("", restricted)

antonindrawan avatar Jan 12 '21 13:01 antonindrawan

@BubbaJoe I guess with the comments it should work for you. So please let us know and close if you get it working.

lammel avatar Mar 12 '21 11:03 lammel

Nice! it works here too 🚀

Clivern avatar Jul 30 '21 10:07 Clivern

I have a strange behaviour with version 4.5.0

the Middleware is only applied if there is an exact match of the url defined in the Group, it is not working.

r := e.Group("/restricted")

doing a call with url /restricted works but /restricted/test does not

brianmori avatar Aug 26 '21 19:08 brianmori

Could you provide example? For some cases with routes the order how routing is set up is important.

When group is created it registers prefix and prefix* routes with 404handler with given middlewares.

https://github.com/labstack/echo/blob/7f502b1ff10913aeab28abab64f2bf45952c768d/group.go#L21-L30

For example modify this to reproduce your problematic situation:

func main() {
	e := echo.New()

	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, c.Path())
	})

	g := e.Group("/group")
	g.Use(middleware.JWT([]byte("secret")))

	g.GET("/test", func(c echo.Context) error {
		return c.String(http.StatusOK, c.Path())
	})

	if err := e.Start(":8088"); err != http.ErrServerClosed {
		log.Fatal(err)
	}
}

aldas avatar Aug 26 '21 19:08 aldas

I have done further tests. I used oapi-codegen to generate from an OpenAPI spec all the routes and I suspect the problem may be caused there. I have not created the routes directly from Echo.

If I specify the exact path of the route, the Middleware applied by the Group does not get applied, but if I specify a different path the Middleware is invoked correctly.

This is from the generated code in the web app

func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) {

	wrapper := ServerInterfaceWrapper{
		Handler: si,
	}

	router.POST(baseURL+"/test/:id/", wrapper.PostTest)

}

and this is the code from the webapp

	e := echo.New()
	e.Use(middleware.Recover())
	api.RegisterHandlersWithBaseURL(e, a, "/v1")

	e.Group("/v1", firebaseAuthMidl)

A note, if the Middleware is global, it works well

This user has a similar issue https://github.com/deepmap/oapi-codegen/issues/55

brianmori avatar Aug 26 '21 20:08 brianmori

api.RegisterHandlersWithBaseURL(e, a, "/v1")
e.Group("/v1", firebaseAuthMidl)

These two things are completely different. Group in Echo is basically helper that sets prefix and middlewares to routes when you add them though group instance. Every route that is added out of group is different. They do not share middlewares.

maybe something like that works

g := e.Group("/v1", firebaseAuthMidl)
api.RegisterV1Routes(g, a)

where RegisterV1Routes is:

func RegisterV1Routes(router EchoRouter, si ServerInterface) {
	wrapper := ServerInterfaceWrapper{
		Handler: si,
	}
	router.POST("/:id", wrapper.PostTest)  // this path will be `/v1/:id`
}

in that case you are adding that post route to group and that group should have /v1 and /v1/* ANY routes registered with 404 handler.

aldas avatar Aug 26 '21 20:08 aldas

No further comments, closing.

lammel avatar Dec 02 '22 22:12 lammel