Middleware
Middleware wraps handlers. It is a function that receives the request and next, the rest of the chain: call next to continue, or return a response to stop the request right there.
Writing middleware#
use rustclamp::log::Log;
use rustclamp::web::{Next, Request, Response, error};
/// Logs every request and its status at debug level.
pub fn request_log(request: &Request, next: Next) -> Response {
let response = next(request);
Log::debug(format_args!("{} {} {}", request.method, request.path, response.status));
response
}
/// Stops the request unless it carries the admin token.
pub fn admin_only(request: &Request, next: Next) -> Response {
match request.header("authorization") {
Some("Bearer secret-token") => next(request),
_ => error(401),
}
}
Global middleware and groups#
Router::new()
.middleware(security_headers) // every request, static files and 404s included
.middleware(request_log)
.view("/", "welcome")
.group(|admin| {
admin.middleware(admin_only) // only the routes declared in this group
.get("/admin", |_| view("admin"))
})
- Order. The first middleware added runs first. Global middleware runs before a group's.
- Groups are how you give pages sessions and CSRF and give the API a rate limit, without either leaking into the other.
- Anything callable with
Fn(&Request, Next) -> Response + Send + Syncworks: a function, a closure, or whatSessions::middleware(),csrf()andthrottle(…)return.
Security headers#
security_headers adds these to every response. The web template adds it globally; keep it first.
Content-Security-Policy(CONTENT_SECURITY_POLICY): scripts only from your own files, so injected markup cannot run. Inline<script>andonclick=are blocked, so put scripts inresources/js. A script that must run before the page paints, such as a theme switch, goes inpublic/as<script src="/theme.js" vite-ignore>. Inline styles and Google Fonts are allowed. A response that sets its own policy keeps it.Strict-Transport-Security: max-age=31536000: HTTPS only for a year. Browsers ignore it over plain HTTP, so local development is unaffected.X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Referrer-Policy: strict-origin-when-cross-origin, andPermissions-Policyturning off camera, microphone and location.