diff --git a/actix-web/src/app.rs b/actix-web/src/app.rs index 1099731f3..c8d84a106 100644 --- a/actix-web/src/app.rs +++ b/actix-web/src/app.rs @@ -326,6 +326,20 @@ where /// Middleware can be applied similarly to individual `Scope`s and `Resource`s. /// See [`Scope::wrap`](crate::Scope::wrap) and [`Resource::wrap`]. /// + /// # Middleware Ordering + /// + /// App-level middleware wraps the entire request router and **always executes before** any + /// [`Scope`](crate::Scope)-level or [`Resource`]-level middleware, regardless of the order in + /// which services are registered on the builder. Scope middleware cannot bypass or run before + /// App middleware. + /// + /// If you need certain routes to skip a middleware, apply that middleware at the + /// [`Scope`](crate::Scope) level instead of the `App` level. + /// + /// For more info on middleware ordering, see the + /// [Cross-Level Middleware Ordering](crate::middleware#cross-level-middleware-ordering) + /// section. + /// /// For more info on middleware take a look at the [`middleware` module][crate::middleware]. /// /// # Examples diff --git a/actix-web/src/middleware/mod.rs b/actix-web/src/middleware/mod.rs index 4b5b3e896..527f7d8fc 100644 --- a/actix-web/src/middleware/mod.rs +++ b/actix-web/src/middleware/mod.rs @@ -15,7 +15,10 @@ //! - Access external services (e.g., [sessions](https://docs.rs/actix-session), etc.) //! //! Middleware is registered for each [`App`], [`Scope`](crate::Scope), or -//! [`Resource`](crate::Resource) and executed in opposite order as registration. +//! [`Resource`](crate::Resource) and executed in opposite order as registration. Note that this +//! reverse ordering applies _within_ a single level (`App`, `Scope`, or `Resource`). When +//! middleware exists at multiple levels, App-level middleware always runs first. See +//! [Cross-Level Middleware Ordering](#cross-level-middleware-ordering) for details. //! //! # Simple Middleware //! @@ -120,6 +123,62 @@ //! middleware are run in reverse order for [`App`] _and then_ in reverse order for the wrapped //! service. //! +//! # Cross-Level Middleware Ordering +//! +//! When middleware is registered on _both_ an [`App`] and a [`Scope`](crate::Scope) (or +//! [`Resource`](crate::Resource)), the App-level middleware **always** runs before the +//! Scope/Resource-level middleware. This is because App-level middleware wraps the entire request +//! router, and routing to a specific scope or resource happens _inside_ that middleware chain. +//! +//! ```plain +//! Request +//! ⭣ +//! ╭──────────────────────┼──────╮ +//! │ App Middleware │ │ +//! │ ╭────────────────────┼─────╮│ +//! │ │ Router │ ││ +//! │ │ ╭─────────────┼────╮││ +//! │ │ │ Scope MW │ │││ +//! │ │ │ ╭───────────┼───╮│││ +//! │ │ │ │ Resource │ ││││ +//! │ │ │ │ MW │ ││││ +//! │ │ │ │ ╭─────────┼──╮││││ +//! │ │ │ │ │ Handler │ │││││ +//! │ │ │ │ ╰─────────┼──╯││││ +//! │ │ │ ╰───────────┼───╯│││ +//! │ │ ╰─────────────┼────╯││ +//! │ ╰────────────────────┼─────╯│ +//! ╰──────────────────────┼──────╯ +//! ⭣ +//! Response +//! ``` +//! +//! This means that a middleware applied to a [`Scope`](crate::Scope) **cannot** bypass or run +//! before middleware applied to the enclosing [`App`]. For example, if authentication middleware +//! is registered on `App`, a "bypass auth" middleware on a scope will not work because the App +//! auth middleware has already executed by the time routing reaches that scope. +//! +//! If you need different middleware behavior for different groups of routes, apply middleware at +//! the scope level rather than the app level: +//! +//! ``` +//! use actix_web::{web, middleware, App}; +//! +//! // Instead of applying auth middleware at the App level and trying +//! // to bypass it on certain scopes, apply it per-scope: +//! let app = App::new() +//! .service( +//! web::scope("/api") +//! .wrap(middleware::DefaultHeaders::new()) // e.g., auth middleware +//! // ... api routes +//! ) +//! .service( +//! web::scope("/public") +//! // no auth middleware here +//! // ... public routes +//! ); +//! ``` +//! //! # Middleware Traits //! //! ## `Transform`