Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide documentation in the middleware module #3070

Merged
merged 5 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions actix-web/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,16 +319,7 @@ where
/// Middleware can be applied similarly to individual `Scope`s and `Resource`s.
/// See [`Scope::wrap`](crate::Scope::wrap) and [`Resource::wrap`].
///
/// # Middleware Order
/// Notice that the keyword for registering middleware is `wrap`. As you register middleware
/// using `wrap` in the App builder, imagine wrapping layers around an inner App. The first
/// middleware layer exposed to a Request is the outermost layer (i.e., the *last* registered in
/// the builder chain). Consequently, the *first* middleware registered in the builder chain is
/// the *last* to start executing during request processing.
///
/// Ordering is less obvious when wrapped services also have middleware applied. In this case,
/// middlewares are run in reverse order for `App` _and then_ in reverse order for the
/// wrapped service.
/// For more info on middleware take a look at the [`middleware` module][crate::middleware].
///
/// # Examples
/// ```
Expand Down
217 changes: 217 additions & 0 deletions actix-web/src/middleware/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,221 @@
//! A collection of common middleware.
//!
//! # What Is Middleware?
//!
//! 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 (e.g., [normalizing paths](NormalizePath))
//! - Post-process a response (e.g., [logging][Logger])
//! - Modify application state (through [`ServiceRequest`][crate::dev::ServiceRequest])
//! - 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. In general, a
//! middleware is a pair of types that implements the [`Service`] trait and [`Transform`] trait,
//! respectively. The [`new_transform`] and [`call`] methods must return a [`Future`], though it
//! can often be [an immediately-ready one](actix_utils::future::Ready).
//!
//! # Ordering
//!
//! ```
//! # use actix_web::{web, middleware, get, App, Responder};
//! #
//! # // some basic types to make sure this compiles
//! # type ExtractorA = web::Json<String>;
//! # 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();

Check warning on line 37 in actix-web/src/middleware/mod.rs

View workflow job for this annotation

GitHub Actions / doc tests

variable `MiddlewareA` should have a snake case name
//! # let MiddlewareC = middleware::Compress::default();

Check warning on line 38 in actix-web/src/middleware/mod.rs

View workflow job for this annotation

GitHub Actions / doc tests

variable `MiddlewareB` should have a snake case name
//! let app = App::new()

Check warning on line 39 in actix-web/src/middleware/mod.rs

View workflow job for this annotation

GitHub Actions / doc tests

variable `MiddlewareC` should have a snake case name
//! .wrap(MiddlewareA)
//! .wrap(MiddlewareB)
//! .wrap(MiddlewareC)
//! .service(service);
//! # }
//! ```
//!
//! ```plain
//! Request
//! ⭣
//! ╭────────────────────┼────╮
//! │ MiddlewareC │ │
//! │ ╭──────────────────┼───╮│
//! │ │ MiddlewareB │ ││
//! │ │ ╭────────────────┼──╮││
//! │ │ │ MiddlewareA │ │││
//! │ │ │ ╭──────────────┼─╮│││
//! │ │ │ │ ExtractorA │ ││││
//! │ │ │ ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┤│││
//! │ │ │ │ ExtractorB │ ││││
//! │ │ │ ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┤│││
//! │ │ │ │ service │ ││││
//! │ │ │ ╰──────────────┼─╯│││
//! │ │ ╰────────────────┼──╯││
//! │ ╰──────────────────┼───╯│
//! ╰────────────────────┼────╯
//! ⭣
//! Response
//! ```
//! The request _first_ gets processed by the middleware specified _last_ - `MiddlewareC`. It passes
//! the request (modified 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`]. In the [`Service`], the extractors will run first. They don't pass the request on,
//! but only view it (see [`FromRequest`]). After the [`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`] builder, imagine wrapping layers around an inner [`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,
//! middleware are run in reverse order for [`App`] _and then_ in reverse order for the wrapped
//! service.
//!
//! # Middleware Traits
//!
//! ## `Transform<S, Req>`
//!
//! The [`Transform`] trait is the builder for the actual [`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`]s by calling [`new_transform`] and passing the next
//! [`Service`] (`S`) in the chain. The created [`Service`] handles requests of type `Req`.
//!
//! In the example from the [ordering](#ordering) section, the chain would be:
//!
//! ```plain
//! MiddlewareCService {
//! next: MiddlewareBService {
//! next: MiddlewareAService { ... }
//! }
//! }
//! ```
//!
//! ## `Service<Req>`
//!
//! A [`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 roughly:
//!
//! ```ignore
//! async fn(&self, req: Req) -> Result<S::Response, S::Error>
//! ```
//!
//! In most cases the [`Service`] implementation will, at some point, call the wrapped [`Service`]
//! in its [`call`] implementation.
//!
//! Note that the [`Service`]s created by [`new_transform`] don't need to be [`Send`] or [`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<S, B> Transform<S, ServiceRequest> for SayHi
//! where
//! S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
//! S::Future: 'static,
//! B: 'static,
//! {
//! type Response = ServiceResponse<B>;
//! type Error = Error;
//! type InitError = ();
//! type Transform = SayHiMiddleware<S>;
//! type Future = Ready<Result<Self::Transform, Self::InitError>>;
//!
//! fn new_transform(&self, service: S) -> Self::Future {
//! ready(Ok(SayHiMiddleware { service }))
//! }
//! }
//!
//! pub struct SayHiMiddleware<S> {
//! /// The next service to call
//! service: S,
//! }
//!
//! // This future doesn't have the requirement of being `Send`.
//! // See: futures_util::future::LocalBoxFuture
//! type LocalBoxFuture<T> = Pin<Box<dyn Future<Output = T> + 'static>>;
//!
//! // `S`: type of the wrapped service
//! // `B`: type of the body - try to be generic over the body where possible
//! impl<S, B> Service<ServiceRequest> for SayHiMiddleware<S>
//! where
//! S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
//! S::Future: 'static,
//! B: 'static,
//! {
//! type Response = ServiceResponse<B>;
//! type Error = Error;
//! type Future = LocalBoxFuture<Result<Self::Response, Self::Error>>;
//!
//! // 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!" }));
//! # }
//! ```
//!
//! # Simpler Middleware
//!
//! In many cases, you _can_ actually use an async function via a helper that will provide a more
//! natural flow for your behavior.
//!
//! The experimental `actix_web_lab` crate provides a [`from_fn`][lab_from_fn] utility which allows
//! an async fn to be wrapped and used in the same way as other middleware. See the
//! [`from_fn`][lab_from_fn] docs for more info and examples of it's use.
//!
//! While [`from_fn`][lab_from_fn] is experimental currently, it's likely this helper will graduate
//! to Actix Web in some form, so feedback is appreciated.
//!
//! [`Future`]: std::future::Future
//! [`App`]: crate::App
//! [`FromRequest`]: crate::FromRequest
//! [`Service`]: crate::dev::Service
//! [`Transform`]: crate::dev::Transform
//! [`call`]: crate::dev::Service::call()
//! [`new_transform`]: crate::dev::Transform::new_transform()
//! [lab_from_fn]: https://docs.rs/actix-web-lab/latest/actix_web_lab/middleware/fn.from_fn.html

mod compat;
mod condition;
Expand Down
Loading