Web

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 + Sync works: a function, a closure, or what Sessions::middleware(), csrf() and throttle(…) 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> and onclick= are blocked, so put scripts in resources/js. A script that must run before the page paints, such as a theme switch, goes in public/ 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, and Permissions-Policy turning off camera, microphone and location.