Middleware
Wrap the request and response flow at the application, controller, or action level.
Middleware defines shared HTTP behavior around an action. For example, it can add a response header or reject a request early.
Write middleware#
use std::sync::Arc;
use lilyrs::http_api::{
ExecutionCancellation, Extensions, HttpExchange, HttpMiddleware,
HttpMiddlewareError, HttpMiddlewareInitError, HttpNext, MiddlewareDescriptor,
MiddlewareErrorCode, MiddlewareKind,
};
pub struct ResponseHeaderMiddleware;
#[lilyrs::http_api::async_trait::async_trait]
impl HttpMiddleware for ResponseHeaderMiddleware {
async fn new(_: Arc<Extensions>) -> Result<Self, HttpMiddlewareInitError> { Ok(Self) }
fn descriptor(&self) -> MiddlewareDescriptor {
MiddlewareDescriptor::new("response_header", MiddlewareKind::Custom)
}
async fn handle(
&self,
exchange: &mut HttpExchange<'_>,
next: HttpNext<'_>,
_cancellation: ExecutionCancellation,
) -> Result<(), HttpMiddlewareError> {
next.run(exchange).await?;
exchange.response_mut().try_insert_header("x-framework", "lily")
.map_err(|_| HttpMiddlewareError::internal(
MiddlewareErrorCode::new("HEADER_WRITE_FAILED").unwrap()
))?;
Ok(())
}
}The example adds an x-framework: lily header after the downstream operation returns. The constructor runs at application startup. Keep the descriptor label stable and free of user-supplied data.
Register at application level#
AppBuilder::new("127.0.0.1:8080")
.middleware::<ResponseHeaderMiddleware>();This snippet creates a builder; complete the actual startup with build().await?.start().await?.
Controller and action levels#
Use #[middleware(ResponseHeaderMiddleware)] on a controller struct or action. Repeating the same middleware type in the combined controller-and-action list is not allowed.
In global registration, the same type can be installed twice if it has no exclusive key. Register each global type only once; do not assume automatic deduplication. The distinction between controller/action and global registration scopes is explained in the reference.
Continuation and early rejection#
next.run(exchange) is a single-use continuation. Call it once and await its result. Returning a rejection without calling it ends the request early. Normal return proceeds in reverse registration order.
ExecutionCancellation signals your long-running work to stop. Do not treat code after next as the completion point for streaming or as cleanup for every interruption. Shutdown explains the abnormal termination hook.
Sources for this page v0.1.0
This guide is based on the reviewed framework commit.