actix_web/middleware/mod.rs
1//! A collection of common middleware.
2//!
3//! # What Is Middleware?
4//!
5//! Actix Web's middleware system allows us to add additional behavior to request/response
6//! processing. Middleware can hook into incoming request and outgoing response processes, enabling
7//! us to modify requests and responses as well as halt request processing to return a response
8//! early.
9//!
10//! Typically, middleware is involved in the following actions:
11//!
12//! - Pre-process the request (e.g., [normalizing paths](NormalizePath))
13//! - Post-process a response (e.g., [logging][Logger])
14//! - Modify application state (through [`ServiceRequest`][crate::dev::ServiceRequest])
15//! - Access external services (e.g., [sessions](https://docs.rs/actix-session), etc.)
16//!
17//! Middleware is registered for each [`App`], [`Scope`](crate::Scope), or
18//! [`Resource`](crate::Resource) and executed in opposite order as registration.
19//!
20//! # Simple Middleware
21//!
22//! In many cases, you can model your middleware as an async function via the [`from_fn()`] helper
23//! that provides a natural interface for implementing your desired behaviors.
24//!
25//! ```
26//! # use actix_web::{
27//! # App, Error,
28//! # body::MessageBody,
29//! # dev::{ServiceRequest, ServiceResponse, Service as _},
30//! # };
31//! use actix_web::middleware::{self, Next};
32//!
33//! async fn my_mw(
34//! req: ServiceRequest,
35//! next: Next<impl MessageBody>,
36//! ) -> Result<ServiceResponse<impl MessageBody>, Error> {
37//! // pre-processing
38//!
39//! // invoke the wrapped middleware or service
40//! let res = next.call(req).await?;
41//!
42//! // post-processing
43//!
44//! Ok(res)
45//! }
46//!
47//! App::new()
48//! .wrap(middleware::from_fn(my_mw));
49//! ```
50//!
51//! ## Complex Middleware
52//!
53//! In the more general ase, a middleware is a pair of types that implements the [`Service`] trait
54//! and [`Transform`] trait, respectively. The [`new_transform`] and [`call`] methods must return a
55//! [`Future`], though it can often be [an immediately-ready one](actix_utils::future::Ready).
56//!
57//! All the built-in middleware use this pattern with pairs of builder (`Transform`) +
58//! implementation (`Service`) types.
59//!
60//! # Ordering
61//!
62//! ```
63//! # use actix_web::{web, middleware, get, App, Responder};
64//! #
65//! # // some basic types to make sure this compiles
66//! # type ExtractorA = web::Json<String>;
67//! # type ExtractorB = ExtractorA;
68//! #[get("/")]
69//! async fn service(a: ExtractorA, b: ExtractorB) -> impl Responder { "Hello, World!" }
70//!
71//! # fn main() {
72//! # // These aren't snake_case, because they are supposed to be unit structs.
73//! # type MiddlewareA = middleware::Compress;
74//! # type MiddlewareB = middleware::Compress;
75//! # type MiddlewareC = middleware::Compress;
76//! let app = App::new()
77//! .wrap(MiddlewareA::default())
78//! .wrap(MiddlewareB::default())
79//! .wrap(MiddlewareC::default())
80//! .service(service);
81//! # }
82//! ```
83//!
84//! ```plain
85//! Request
86//! ⭣
87//! ╭────────────────────┼────╮
88//! │ MiddlewareC │ │
89//! │ ╭──────────────────┼───╮│
90//! │ │ MiddlewareB │ ││
91//! │ │ ╭────────────────┼──╮││
92//! │ │ │ MiddlewareA │ │││
93//! │ │ │ ╭──────────────┼─╮│││
94//! │ │ │ │ ExtractorA │ ││││
95//! │ │ │ ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┤│││
96//! │ │ │ │ ExtractorB │ ││││
97//! │ │ │ ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┤│││
98//! │ │ │ │ service │ ││││
99//! │ │ │ ╰──────────────┼─╯│││
100//! │ │ ╰────────────────┼──╯││
101//! │ ╰──────────────────┼───╯│
102//! ╰────────────────────┼────╯
103//! ⭣
104//! Response
105//! ```
106//! The request _first_ gets processed by the middleware specified _last_ - `MiddlewareC`. It passes
107//! the request (possibly a modified one) to the next middleware - `MiddlewareB` - _or_ directly
108//! responds to the request (e.g. when the request was invalid or an error occurred). `MiddlewareB`
109//! processes the request as well and passes it to `MiddlewareA`, which then passes it to the
110//! [`Service`]. In the [`Service`], the extractors will run first. They don't pass the request on,
111//! but only view it (see [`FromRequest`]). After the [`Service`] responds to the request, the
112//! response is passed back through `MiddlewareA`, `MiddlewareB`, and `MiddlewareC`.
113//!
114//! As you register middleware using [`wrap`][crate::App::wrap] and [`wrap_fn`][crate::App::wrap_fn]
115//! in the [`App`] builder, imagine wrapping layers around an inner [`App`]. The first middleware
116//! layer exposed to a Request is the outermost layer (i.e., the _last_ registered in the builder
117//! chain, in the example above: `MiddlewareC`). Consequently, the _first_ middleware registered in
118//! the builder chain is the _last_ to start executing during request processing (`MiddlewareA`).
119//! Ordering is less obvious when wrapped services also have middleware applied. In this case,
120//! middleware are run in reverse order for [`App`] _and then_ in reverse order for the wrapped
121//! service.
122//!
123//! # Middleware Traits
124//!
125//! ## `Transform<S, Req>`
126//!
127//! The [`Transform`] trait is the builder for the actual [`Service`]s that handle the requests. All
128//! the middleware you pass to the `wrap` methods implement this trait. During construction, each
129//! thread assembles a chain of [`Service`]s by calling [`new_transform`] and passing the next
130//! [`Service`] (`S`) in the chain. The created [`Service`] handles requests of type `Req`.
131//!
132//! In the example from the [ordering](#ordering) section, the chain would be:
133//!
134//! ```plain
135//! MiddlewareCService {
136//! next: MiddlewareBService {
137//! next: MiddlewareAService { ... }
138//! }
139//! }
140//! ```
141//!
142//! ## `Service<Req>`
143//!
144//! A [`Service`] `S` represents an asynchronous operation that turns a request of type `Req` into a
145//! response of type [`S::Response`](crate::dev::Service::Response) or an error of type
146//! [`S::Error`](crate::dev::Service::Error). You can think of the service of being roughly:
147//!
148//! ```ignore
149//! async fn(&self, req: Req) -> Result<S::Response, S::Error>
150//! ```
151//!
152//! In most cases the [`Service`] implementation will, at some point, call the wrapped [`Service`]
153//! in its [`call`] implementation.
154//!
155//! Note that the [`Service`]s created by [`new_transform`] don't need to be [`Send`] or [`Sync`].
156//!
157//! # Example
158//!
159//! ```
160//! use std::{future::{ready, Ready, Future}, pin::Pin};
161//!
162//! use actix_web::{
163//! dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
164//! web, Error,
165//! # App
166//! };
167//!
168//! pub struct SayHi;
169//!
170//! // `S` - type of the next service
171//! // `B` - type of response's body
172//! impl<S, B> Transform<S, ServiceRequest> for SayHi
173//! where
174//! S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
175//! S::Future: 'static,
176//! B: 'static,
177//! {
178//! type Response = ServiceResponse<B>;
179//! type Error = Error;
180//! type InitError = ();
181//! type Transform = SayHiMiddleware<S>;
182//! type Future = Ready<Result<Self::Transform, Self::InitError>>;
183//!
184//! fn new_transform(&self, service: S) -> Self::Future {
185//! ready(Ok(SayHiMiddleware { service }))
186//! }
187//! }
188//!
189//! pub struct SayHiMiddleware<S> {
190//! /// The next service to call
191//! service: S,
192//! }
193//!
194//! // This future doesn't have the requirement of being `Send`.
195//! // See: futures_util::future::LocalBoxFuture
196//! type LocalBoxFuture<T> = Pin<Box<dyn Future<Output = T> + 'static>>;
197//!
198//! // `S`: type of the wrapped service
199//! // `B`: type of the body - try to be generic over the body where possible
200//! impl<S, B> Service<ServiceRequest> for SayHiMiddleware<S>
201//! where
202//! S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
203//! S::Future: 'static,
204//! B: 'static,
205//! {
206//! type Response = ServiceResponse<B>;
207//! type Error = Error;
208//! type Future = LocalBoxFuture<Result<Self::Response, Self::Error>>;
209//!
210//! // This service is ready when its next service is ready
211//! forward_ready!(service);
212//!
213//! fn call(&self, req: ServiceRequest) -> Self::Future {
214//! println!("Hi from start. You requested: {}", req.path());
215//!
216//! // A more complex middleware, could return an error or an early response here.
217//!
218//! let fut = self.service.call(req);
219//!
220//! Box::pin(async move {
221//! let res = fut.await?;
222//!
223//! println!("Hi from response");
224//! Ok(res)
225//! })
226//! }
227//! }
228//!
229//! # fn main() {
230//! let app = App::new()
231//! .wrap(SayHi)
232//! .route("/", web::get().to(|| async { "Hello, middleware!" }));
233//! # }
234//! ```
235//!
236//! [`Future`]: std::future::Future
237//! [`App`]: crate::App
238//! [`FromRequest`]: crate::FromRequest
239//! [`Service`]: crate::dev::Service
240//! [`Transform`]: crate::dev::Transform
241//! [`call`]: crate::dev::Service::call()
242//! [`new_transform`]: crate::dev::Transform::new_transform()
243//! [`from_fn`]: crate
244
245mod compat;
246#[cfg(feature = "__compress")]
247mod compress;
248mod condition;
249mod default_headers;
250mod err_handlers;
251mod from_fn;
252mod identity;
253mod logger;
254mod normalize;
255
256#[cfg(feature = "__compress")]
257pub use self::compress::Compress;
258pub use self::{
259 compat::Compat,
260 condition::Condition,
261 default_headers::DefaultHeaders,
262 err_handlers::{ErrorHandlerResponse, ErrorHandlers},
263 from_fn::{from_fn, Next},
264 identity::Identity,
265 logger::Logger,
266 normalize::{NormalizePath, TrailingSlash},
267};
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272 use crate::{http::StatusCode, App};
273
274 #[test]
275 fn common_combinations() {
276 // ensure there's no reason that the built-in middleware cannot compose
277
278 let _ = App::new()
279 .wrap(Compat::new(Logger::default()))
280 .wrap(Condition::new(true, DefaultHeaders::new()))
281 .wrap(DefaultHeaders::new().add(("X-Test2", "X-Value2")))
282 .wrap(ErrorHandlers::new().handler(StatusCode::FORBIDDEN, |res| {
283 Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
284 }))
285 .wrap(Logger::default())
286 .wrap(NormalizePath::new(TrailingSlash::Trim));
287
288 let _ = App::new()
289 .wrap(NormalizePath::new(TrailingSlash::Trim))
290 .wrap(Logger::default())
291 .wrap(ErrorHandlers::new().handler(StatusCode::FORBIDDEN, |res| {
292 Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
293 }))
294 .wrap(DefaultHeaders::new().add(("X-Test2", "X-Value2")))
295 .wrap(Condition::new(true, DefaultHeaders::new()))
296 .wrap(Compat::new(Logger::default()));
297
298 #[cfg(feature = "__compress")]
299 {
300 let _ = App::new().wrap(Compress::default()).wrap(Logger::default());
301 let _ = App::new().wrap(Logger::default()).wrap(Compress::default());
302 let _ = App::new().wrap(Compat::new(Compress::default()));
303 let _ = App::new().wrap(Condition::new(true, Compat::new(Compress::default())));
304 }
305 }
306}