diff --git a/actix-web/src/middleware/mod.rs b/actix-web/src/middleware/mod.rs index 0a61ad6cb..42da8a290 100644 --- a/actix-web/src/middleware/mod.rs +++ b/actix-web/src/middleware/mod.rs @@ -1,4 +1,216 @@ //! A collection of common middleware. +//! +//! # Introduction +//! +//! Actix Web's middleware system allows us to add additional behavior to request/response processing. +//! Middleware can hook into incoming request and outgoing response processes, +//! enabling us to modify requests and responses as well as halt request processing to return a response early. +//! +//! Typically, middleware is involved in the following actions: +//! +//! * Pre-process the request +//! * Post-process a response +//! * Modify application state (through [`ServiceRequest`][crate::dev::ServiceRequest]) +//! * Access external services ([redis](https://docs.rs/actix-redis), [logging][Logger], [sessions](https://docs.rs/actix-session)) +//! +//! Middleware is registered for each [`App`][crate::App], [`Scope`][crate::Scope], or [`Resource`][crate::Resource] +//! and executed in opposite order as registration. +//! In general, a middleware is a type that implements the [`Service`][Service] trait and [`Transform`][Transform] trait. +//! Each method in the traits has a default implementation. Each method can return a result immediately or a [`Future`][std::future::Future]. +//! +//! ## Order +//! +//! ``` +//! # use actix_web::{web, middleware, get, App, Responder}; +//! # +//! # // some basic types to make sure this compiles +//! # type ExtractorA = web::Json; +//! # type ExtractorB = ExtractorA; +//! #[get("/")] +//! async fn service(a: ExtractorA, b: ExtractorB) -> impl Responder { "Hello, World!" } +//! +//! # fn main() { +//! # // These aren't snake_case, because they are supposed to be unit structs. +//! # let MiddlewareA = middleware::Compress::default(); +//! # let MiddlewareB = middleware::Compress::default(); +//! # let MiddlewareC = middleware::Compress::default(); +//! let app = App::new() +//! .wrap(MiddlewareA) +//! .wrap(MiddlewareB) +//! .wrap(MiddlewareC) +//! .service(service); +//! # } +//! ``` +//! +//! ```text +//! Request +//! ⭣ +//! ╭────────────────────┼───╮ +//! │ MiddlewareC │ │ +//! │ ╭──────────────────┼──╮│ +//! │ │ MiddlewareB │ ││ +//! │ │ ╭────────────────┼─╮││ +//! │ │ │ MiddlewareA │ │││ +//! │ │ │ ╭──────────────┼╮│││ +//! │ │ │ │ ExtractorA │││││ +//! │ │ │ ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┤│││ +//! │ │ │ │ ExtractorB │││││ +//! │ │ │ ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┤│││ +//! │ │ │ │ service │││││ +//! │ │ │ ╰──────────────┼╯│││ +//! │ │ ╰────────────────┼─╯││ +//! │ ╰──────────────────┼──╯│ +//! ╰────────────────────┼───╯ +//! ⭣ +//! Response +//! ``` +//! The request _first_ gets processed by the middleware specified _last_ - `MiddlewareC`. +//! It passes the request (or a modified one) to the next middleware - `MiddlewareB` - +//! _or_ directly responds to the request (e.g. when the request was invalid or an error occurred). +//! `MiddlewareB` processes the request as well and passes it to `MiddlewareA`, which then passes it to the [`Service`][Service]. +//! In the [`Service`][Service], the extractors will run first. They don't pass the request on, but only view it (see [`FromRequest`][crate::FromRequest]). +//! After the [`Service`][Service] responds to the request, the response it passed back through `MiddlewareA`, `MiddlewareB`, and `MiddlewareC`. +//! +//! As you register middleware using [`wrap`][crate::App::wrap] and [`wrap_fn`][crate::App::wrap_fn] in the [`App`][crate::App] builder, +//! imagine wrapping layers around an inner [`App`][crate::App]. +//! The first middleware layer exposed to a Request is the outermost layer (i.e., the *last* registered in +//! the builder chain, in the example above: `MiddlewareC`). Consequently, the *first* middleware registered in the builder chain is +//! the *last* to start executing during request processing (`MiddlewareA`). +//! Ordering is less obvious when wrapped services also have middleware applied. In this case, +//! middlewares are run in reverse order for [`App`][crate::App] _and then_ in reverse order for the +//! wrapped service. +//! +//! # Middleware Traits +//! +//! ## `Transform` +//! +//! The [`Transform`][Transform] trait is the factory for the actual [`Service`][crate::dev::Service]s that handle the requests. +//! All the middleware you pass to the `wrap` methods implement this trait. +//! During construction, each thread assembles a chain of [`Service`][Service]s +//! by calling [`new_transform`][crate::dev::Transform::new_transform] and passing the next [`Service`][Service] (`S`) in the chain. +//! The created [`Service`][Service] handles requests of type `Req`. +//! +//! In the example from the [Order](#Order) section, the chain would be: `MiddlewareCService { next: MiddlewareBService { next: MiddlewareAService {..} } } }`. +//! +//! ## `Service` +//! +//! A [`Service`][Service] `S` represents an asynchronous operation +//! that turns a request of type `Req` into a response of type [`S::Response`][crate::dev::Service::Response] +//! or an error of type [`S::Error`][crate::dev::Service::Error]. You can think of the service of being a `async fn (&self, req: Req) -> Result`. +//! +//! In most cases the [`Service`][Service] implementation will call the next [`Service`][Service] in its [`Future`][std::future::Future] returned by [`call`][crate::dev::Service::call]. +//! +//! Note that the [`Service`][Service]s created by [`new_transform`][crate::dev::Transform::new_transform] don't need to be [`Send`][Send] nor [`Sync`][Sync]. +//! +//! # Example +//! +//! ``` +//! use std::{future::{ready, Ready, Future}, pin::Pin}; +//! +//! use actix_web::{ +//! dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, +//! web, Error, +//! # App +//! }; +//! +//! pub struct SayHi; +//! +//! // `S` - type of the next service +//! // `B` - type of response's body +//! impl Transform for SayHi +//! where +//! S: Service, Error = Error>, +//! S::Future: 'static, +//! B: 'static, +//! { +//! type Response = ServiceResponse; +//! type Error = Error; +//! type InitError = (); +//! type Transform = SayHiMiddleware; +//! type Future = Ready>; +//! +//! fn new_transform(&self, service: S) -> Self::Future { +//! ready(Ok(SayHiMiddleware { service })) +//! } +//! } +//! +//! pub struct SayHiMiddleware { +//! /// The next service to call +//! service: S, +//! } +//! +//! // This future doesn't have the requirement of being `Send`. +//! // See futures_util::future::LocalBoxFuture +//! type LocalBoxFuture = Pin + 'static>>; +//! +//! // `S` - type of the next service +//! // `B` - type of the body - try to be generic over the body where possible +//! impl Service for SayHiMiddleware +//! where +//! S: Service, Error = Error>, +//! S::Future: 'static, +//! B: 'static, +//! { +//! type Response = ServiceResponse; +//! type Error = Error; +//! type Future = LocalBoxFuture>; +//! +//! // This service is ready when its next service is ready +//! forward_ready!(service); +//! +//! fn call(&self, req: ServiceRequest) -> Self::Future { +//! println!("Hi from start. You requested: {}", req.path()); +//! +//! // A more complex middleware, could return an error or an early response here. +//! +//! let fut = self.service.call(req); +//! +//! Box::pin(async move { +//! let res = fut.await?; +//! +//! println!("Hi from response"); +//! Ok(res) +//! }) +//! } +//! } +//! +//! # fn main() { +//! let app = App::new() +//! .wrap(SayHi) +//! .route("/", web::get().to(|| async { "Hello, middleware!" })); +//! # } +//! ``` +//! +//! # Simple Middleware +//! +//! In simple cases, you can use a function instead. +//! You can register these in [`App::wrap_fn`][crate::App::wrap_fn], [`Scope::wrap_fn`][crate::Scope::wrap_fn], and [`Resource::wrap_fn`][crate::Resource::wrap_fn]. +//! The [order](#order) remains the same. +//! +//! The middleware from [above](#example) can be written using `wrap_fn`: +//! +//! ``` +//! use actix_web::{dev::Service, web, App}; +//! +//! # fn main() { +//! let app = App::new() +//! .wrap_fn(|req, srv| { +//! println!("Hi from start. You requested: {}", req.path()); +//! let fut = srv.call(req); +//! async { +//! let res = fut.await?; +//! +//! println!("Hi from response"); +//! +//! Ok(res) +//! } +//! }) +//! .route("/", web::get().to(|| async { "Hello, middleware!" })); +//! # } +//! ``` +//! +//! [Service]: crate::dev::Service +//! [Transform]: crate::dev::Transform mod compat; mod condition;