actix / actix-web

Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.
https://actix.rs
Apache License 2.0
21.69k stars 1.68k forks source link

Apply middleware(s) to group of routes without prefix #414

Open sadika9 opened 6 years ago

sadika9 commented 6 years ago

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

max-frai commented 6 years ago

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.

fafhrd91 commented 6 years ago

We can reuse predicates for middlewares. PR is welcome

sadika9 commented 6 years ago

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

fafhrd91 commented 5 years ago

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.

jdosornio commented 5 years ago

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?

fafhrd91 commented 5 years ago

i think this is right approach

fafhrd91 commented 5 years ago

@jdosornio

you can do something like:

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

is that what you asking?

jdosornio commented 5 years ago

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?.

fafhrd91 commented 5 years ago

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.

ghost commented 4 years ago

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.

DominicWrege commented 3 years ago

progress #1865

stevenhansel commented 2 years ago

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))                                                                                    
                    )
joao-conde commented 3 months ago

Any updates on this? I think grouping services to apply middleware (without having to specify a prefix) is a very common backend web pattern.

Akin to scope and resource can we have something like the proposed group?