Middleware
Middleware is an async chain that wraps every request. Oxide ships a
Middleware trait + Next driver and lets you attach middleware
globally (all routes), per-group, or per-route.
Custom middleware
Each middleware is a struct that implements the Middleware trait.
Convention: put them under src/app/http/middleware/.
use std::sync::Arc;use std::time::Instant;
use async_trait::async_trait;use oxide_http::{Middleware, Next, Request, Response};
pub struct LogRequests;
impl LogRequests { pub fn new() -> Arc<dyn Middleware> { Arc::new(Self) }}
#[async_trait]impl Middleware for LogRequests { async fn handle(&self, req: Request, next: Next) -> Response { let method = req.method().clone(); let path = req.path().to_string(); let started = Instant::now();
let response = next.run(req).await;
tracing::info!( %method, %path, status = response.status_code().as_u16(), elapsed_ms = started.elapsed().as_millis(), "request" ); response }}Attaching middleware
// Per-group:app.middleware([LogRequests::new()]).group("/api", |r| { r.get("/health", health_handler);});
// Per-route (single-route group):app.middleware([auth_middleware::<User>()]) .get("/me", UserController::me);Global middleware
Wire middleware globally via a service provider using
app.use_middleware(mw). The middleware runs on every request —
including requests that would otherwise 404 or 405.
Oxide’s built-in CORS uses this pattern:
app.register(ConfigServiceProvider) .register(CorsServiceProvider) // adds Cors::... via app.use_middleware .register(DatabaseServiceProvider) .register(AppServiceProvider) .register(RouteServiceProvider) .boot();Built-in middleware
| Middleware | Purpose |
|---|---|
Cors::new() / Cors::builder() | Cross-origin headers; wired globally via CorsServiceProvider reading config/cors.toml. |
oxide_auth::auth_middleware::<U>() | Verify Bearer token; on success attach Authed<U> to the request. |
oxide_auth::abilities([...]) | Require all listed abilities on the current token. |
oxide_auth::ability([...]) | Require any of the listed abilities. |
Chain order
Execution is outer → inner:
app.middleware([A, B]).get("/x", handler);// Order: A.handle → B.handle → handler → B (post) → A (post)Global middleware registered via app.use_middleware runs outside
any per-route middleware.
Early return
Middleware short-circuits by returning a Response without calling
next.run(req).await. The inner chain and the handler never run:
#[async_trait]impl Middleware for RequireBearer { async fn handle(&self, req: Request, next: Next) -> Response { if req.header("authorization").is_none() { return Response::unauthorized(); } next.run(req).await }}