pub trait Middleware: Send + Sync {
// Required method
fn handle(
&mut self,
request: &mut Request,
next: impl Endpoint,
) -> impl Future<Output = Result<Response>> + Send + Sync;
}Expand description
Trait for implementing middleware that can process HTTP requests and responses.
Middleware sits between the initial request and the final endpoint, allowing you to implement cross-cutting concerns like authentication, logging, rate limiting, compression, and other request/response transformations.
Middleware operates in a chain where each middleware can:
- Inspect and modify the incoming request
- Decide whether to call the next middleware/endpoint in the chain
- Inspect and modify the outgoing response
- Handle errors and implement fallback behavior
§Implementation Pattern
A typical middleware implementation follows this pattern:
- Pre-process the request (logging, validation, etc.)
- Call
next.respond(request).awaitto continue the chain - Post-process the response (add headers, transform body, etc.)
- Return the final response
§Examples
§Request Logging Middleware
use http_kit::{Request, Response, Result, Middleware, Endpoint};
struct LoggingMiddleware;
impl Middleware for LoggingMiddleware {
async fn handle(&self, request: &mut Request, next: impl Endpoint) -> Result<Response> {
println!("Incoming: {} {}", request.method(), request.uri());
let response = next.respond(request).await?;
println!("Outgoing: {}", response.status());
Ok(response)
}
}§Authentication Middleware
use http_kit::{Request, Response, Result, Middleware, Endpoint, StatusCode};
struct AuthMiddleware {
required_token: String,
}
impl Middleware for AuthMiddleware {
async fn handle(&self, request: &mut Request, next: impl Endpoint) -> Result<Response> {
if let Some(auth_header) = request.get_header(http::header::AUTHORIZATION) {
if auth_header.as_bytes() == self.required_token.as_bytes() {
return next.respond(request).await;
}
}
Ok(Response::new(StatusCode::UNAUTHORIZED, "Authentication required"))
}
}§Response Header Middleware
use http_kit::{Request, Response, Result, Middleware, Endpoint};
struct HeaderMiddleware;
impl Middleware for HeaderMiddleware {
async fn handle(&self, request: &mut Request, next: impl Endpoint) -> Result<Response> {
let mut response = next.respond(request).await?;
response.insert_header(
http::header::SERVER,
http::HeaderValue::from_static("http-kit/1.0")
);
Ok(response)
}
}Required Methods§
Sourcefn handle(
&mut self,
request: &mut Request,
next: impl Endpoint,
) -> impl Future<Output = Result<Response>> + Send + Sync
fn handle( &mut self, request: &mut Request, next: impl Endpoint, ) -> impl Future<Output = Result<Response>> + Send + Sync
Processes a request through the middleware chain.
This method receives the current request and a next parameter representing
the next step in the processing chain (either another middleware or the final endpoint).
The middleware can:
- Modify the request before passing it to
next - Decide whether to call
nextat all (for auth, rate limiting, etc.) - Transform the response returned by
next - Handle errors and provide fallback responses
§Arguments
request- Mutable reference to the HTTP request being processednext- The next step in the processing chain (middleware or endpoint)
§Returns
Returns a Result<Response> which can either be:
Ok(response)- A successful HTTP responseErr(error)- An error with an associated HTTP status code
§Examples
use http_kit::{Request, Response, Result, Middleware, Endpoint};
struct TimingMiddleware;
impl Middleware for TimingMiddleware {
async fn handle(&self, request: &mut Request, next: impl Endpoint) -> Result<Response> {
let start = std::time::Instant::now();
// Call the next middleware or endpoint
let response = next.respond(request).await?;
let duration = start.elapsed();
println!("Request processed in {:?}", duration);
Ok(response)
}
}Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.
Implementations on Foreign Types§
Source§impl Middleware for ()
No-op middleware implementation for the unit type.
impl Middleware for ()
No-op middleware implementation for the unit type.
This implementation allows () to be used as a middleware that does nothing
but pass the request through to the next handler. This is useful for:
- Default values in generic contexts
- Conditional middleware application
- Testing scenarios where middleware is optional
Source§impl<M: Middleware> Middleware for &mut M
impl<M: Middleware> Middleware for &mut M
Source§impl<M: Middleware> Middleware for Box<M>
impl<M: Middleware> Middleware for Box<M>
Source§impl<T1: Middleware, T2: Middleware> Middleware for (T1, T2)
Middleware implementation for tuples of two middleware types.
impl<T1: Middleware, T2: Middleware> Middleware for (T1, T2)
Middleware implementation for tuples of two middleware types.
This allows you to combine two middleware into a single unit where the first
middleware wraps the second middleware, which in turn wraps the endpoint.
The execution order is: self.0 → self.1 → endpoint.
§Examples
use http_kit::{Request, Response, Result, Middleware, Endpoint};
struct LoggingMiddleware;
impl Middleware for LoggingMiddleware {
async fn handle(&self, request: &mut Request, next: impl Endpoint) -> Result<Response> {
println!("Before request");
let response = next.respond(request).await;
println!("After request");
response
}
}
struct TimingMiddleware;
impl Middleware for TimingMiddleware {
async fn handle(&self, request: &mut Request, next: impl Endpoint) -> Result<Response> {
let start = std::time::Instant::now();
let response = next.respond(request).await;
println!("Elapsed: {:?}", start.elapsed());
response
}
}
// Combine middleware using tuple syntax
let combined = (LoggingMiddleware, TimingMiddleware);
// Execution order: LoggingMiddleware → TimingMiddleware → endpoint