Module actix_web::middleware
source · Expand description
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)
- Post-process a response (e.g., logging)
- Modify application state (through
ServiceRequest
) - Access external services (e.g., sessions, etc.)
Middleware is registered for each App
, Scope
, or
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.
§Ordering
#[get("/")]
async fn service(a: ExtractorA, b: ExtractorB) -> impl Responder { "Hello, World!" }
let app = App::new()
.wrap(MiddlewareA::default())
.wrap(MiddlewareB::default())
.wrap(MiddlewareC::default())
.service(service);
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
and 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 section, the chain would be:
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
or an error of type
S::Error
. You can think of the service of being roughly:
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,
};
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)
})
}
}
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
utility which allows
an async fn to be wrapped and used in the same way as other middleware. See the
from_fn
docs for more info and examples of it’s use.
While from_fn
is experimental currently, it’s likely this helper will graduate
to Actix Web in some form, so feedback is appreciated.
Structs§
- Middleware for enabling any middleware to be used in
Resource::wrap
, andCondition
. - Compress
__compress
Middleware for compressing response payloads. - Middleware for conditionally enabling other middleware.
- Middleware for setting default response headers.
- Middleware for registering custom status code based error handlers.
- Middleware for logging request and response summaries to the terminal.
- Middleware for normalizing a request’s path so that routes can be matched more flexibly.
Enums§
- Return type for
ErrorHandlers
custom handlers. - Determines the behavior of the
NormalizePath
middleware.