1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
//! 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.
//! # type MiddlewareA = middleware::Compress;
//! # type MiddlewareB = middleware::Compress;
//! # type MiddlewareC = middleware::Compress;
//! let app = App::new()
//! .wrap(MiddlewareA::default())
//! .wrap(MiddlewareB::default())
//! .wrap(MiddlewareC::default())
//! .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 is 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;
mod default_headers;
mod err_handlers;
mod logger;
#[cfg(test)]
mod noop;
mod normalize;
#[cfg(test)]
pub(crate) use self::noop::Noop;
pub use self::{
compat::Compat,
condition::Condition,
default_headers::DefaultHeaders,
err_handlers::{ErrorHandlerResponse, ErrorHandlers},
logger::Logger,
normalize::{NormalizePath, TrailingSlash},
};
#[cfg(feature = "__compress")]
mod compress;
#[cfg(feature = "__compress")]
pub use self::compress::Compress;
#[cfg(test)]
mod tests {
use super::*;
use crate::{http::StatusCode, App};
#[test]
fn common_combinations() {
// ensure there's no reason that the built-in middleware cannot compose
let _ = App::new()
.wrap(Compat::new(Logger::default()))
.wrap(Condition::new(true, DefaultHeaders::new()))
.wrap(DefaultHeaders::new().add(("X-Test2", "X-Value2")))
.wrap(ErrorHandlers::new().handler(StatusCode::FORBIDDEN, |res| {
Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
}))
.wrap(Logger::default())
.wrap(NormalizePath::new(TrailingSlash::Trim));
let _ = App::new()
.wrap(NormalizePath::new(TrailingSlash::Trim))
.wrap(Logger::default())
.wrap(ErrorHandlers::new().handler(StatusCode::FORBIDDEN, |res| {
Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
}))
.wrap(DefaultHeaders::new().add(("X-Test2", "X-Value2")))
.wrap(Condition::new(true, DefaultHeaders::new()))
.wrap(Compat::new(Logger::default()));
#[cfg(feature = "__compress")]
{
let _ = App::new().wrap(Compress::default()).wrap(Logger::default());
let _ = App::new().wrap(Logger::default()).wrap(Compress::default());
let _ = App::new().wrap(Compat::new(Compress::default()));
let _ = App::new().wrap(Condition::new(true, Compat::new(Compress::default())));
}
}
}