actix-web icon indicating copy to clipboard operation
actix-web copied to clipboard

Apply middleware(s) to group of routes without prefix

Open sadika9 opened this issue 6 years ago • 13 comments

I have set of routes that need to pass through group of middlewares. I tried to use route scopes but it requires prefix to be applied as well. I'm looking for feature like in laravel route groups

eg. public routes: /login

private routes: /dashboard /users/** /activities/** /logout

sadika9 avatar Jul 23 '18 04:07 sadika9

I ran into similar issue, the one option I used is to attach usual global middleware and inside it check for route matched the exact list where logic should be applied.

max-frai avatar Jul 23 '18 09:07 max-frai

We can reuse predicates for middlewares. PR is welcome

fafhrd91 avatar Jul 23 '18 09:07 fafhrd91

Not sure how to use it. can you please explain it more?

sadika9 avatar Jul 23 '18 09:07 sadika9

if anyone interested, it should be possible in 1.0, resources and scopes are implemented generically in actix-web 1.0, so route group could be custom service with router.

PR is welcomed.

fafhrd91 avatar Mar 23 '19 17:03 fafhrd91

I was thinking about how to implement this feature, which once implemented, could look like this:

app.service(
  group()
    .wrap(middleware_1)
    .wrap(middleware_2)
    .route("/", web::route().guard(guard::Get()).to(handler))
    .service(web::resource("/user/profile").to(profile_handler))
    .service(web::scope("/account").service(
       web::resource("{id}").guard(guard::Post()).to(handler),
    ))
).wrap(global_middleware_1)
.wrap(global_middleware_2)
.service(
  group()...
).service...

Looking at the other service implementations, seems like a path is needed to register a service on the app's configuration, but I wonder if it's possible to register a service without a path too, to be able to add pathless group services.

Or do you have any other ideas on mind?

jdosornio avatar Apr 02 '19 02:04 jdosornio

i think this is right approach

fafhrd91 avatar Apr 02 '19 02:04 fafhrd91

@jdosornio

you can do something like:

App::new().service(
    web::scope("/prefix").service(
        web::resource("").to(..)))   // handler for: /prefix

is that what you asking?

fafhrd91 avatar Apr 02 '19 02:04 fafhrd91

I'm asking more about the specifics on how to implement a custom group service like the scope and resource ones already implemented.

Following the OP example, I'm thinking about adding a group service like this:

App::new().service(
    web::group().service(
      web::resource("/dashboard").to(..)
    ).service(
      web::scope("/users")...
    ).service(
      web::scope("/activities")...
    ).service(
      web::resource("/logout").to(..)
    )
) 

Where the "/dashboard", "/users", "/activities" and "/logout" routes will share guards and middlewares added to the group. But the problem is that to register a service in the internal configuration, a path, (or ResourceDef) is needed, and in this case the group service wouldn't have a path (or will be like ResourceDef::new("")). Unless there's a better way to implement this?.

jdosornio avatar Apr 02 '19 03:04 jdosornio

i see. this is not that easy.

possible solution is to create multiple resources within group and then register each one separately. it is possible to construct middlewares for each resource because .wrap() accepts middleware factory.

fafhrd91 avatar Apr 02 '19 03:04 fafhrd91

Bumping this. It would be a very useful feature to have route groups with their own middleware. Certain routes like static assets may not need typical middleware like logging or sessions; this is not easily accomplished with web::scope() since various routes with different prefixes need to be grouped together. I thought that perhaps defining my static asset route before adding middleware might skip the middlware, but it doesn't. jdosornio's suggestion seems like a decent solution.

ghost avatar May 11 '20 20:05 ghost

progress #1865

DominicWrege avatar Jan 04 '21 17:01 DominicWrege

Any more updates regarding this?

Well, personally my current workaround for individual routes is something like this:

        App::new()
            .wrap(cors)
            .route("/", web::get().to(health_check))
            .service(
                web::scope("/dashboard")
                    .route("/v1/auth/register", web::post().to(auth_http::register))
                    .service(
                        web::scope("/v1/me")
                            .wrap(AuthenticationMiddlewareFactory::new(auth_service.clone()))
                            .route("", web::get().to(auth_http::me)),
                    )
            )

I believe you can also span over multiple routes over course:

                    .service(
                        web::scope("/v1/products")
                            .wrap(AuthenticationMiddlewareFactory::new(auth_service.clone()))
                            .route("", web::get().to(get_products))
                            .route("", web::post().to(create_product))
                            .route("/{product_id}", web::put().to(update_product))
                            .route("/{product_id}", web::delete().to(delete_product))                                                                                    
                    )

stevenhansel avatar Jul 17 '22 16:07 stevenhansel