Browse documentation
Documentation

Router: Content

Edit this guide on GitHub

Request extractors and response types for Hearth handlers.

A handler's parameters describe the request body it accepts. Its return type describes the response it sends. This module provides types for both roles.

Reading a request body

A page or route can accept one body parameter that implements FromRequest. For example, Json deserializes a JSON body into your type. Add cx: &Cx when the handler also needs request context.

// src/app/api/users.rs
use hearth::{
    Result,
    context::Cx,
    router::{content::Json, route},
};

#[route(POST)]
async fn create_user(cx: &Cx, Json(input): Json<CreateUser>) -> Result<String> {
    let _ = cx;
    Ok(format!("created {}", input.name))
}

The context and body parameters are optional and may appear in either order. There can be only one body parameter because the body can be consumed only once. Built-in extractors reject malformed input with 400 Bad Request. Wrap an extractor in Option to accept an absent body. Pages read bodies the same way and return a view.

The body limit

Extractors that buffer the body reject requests above the body limit with 413 Content Too Large. The default is 2 MiB. Register a BodyLimit layer to change it for the application or a path:

use hearth::router::{BodyLimit, module_router};

let router = module_router!()
    // Allow up to 32 MiB under /upload, keep the 2 MiB default elsewhere.
    .layer(BodyLimit::max(32 * 1024 * 1024).at("/upload"))
    .build();

Taking Body directly is not limited, because the handler streams the body instead of buffering it.

Implement FromRequest yourself for request parsing the built-in extractors do not cover, such as a body that is verified against a signature header before it is deserialized. Delegate the buffering to Bytes so the body limit stays applied.

Returning a response

A route returns Result<T>. Implement IntoResponse for a synchronous conversion, or AsyncIntoResponse if building the response needs to await work. A wrapper such as Json serializes its value and sets the content type. A string or byte buffer becomes the response body directly.

A tuple builds a response from several parts. The last element is the body, a leading StatusCode sets the status, and the elements in between attach headers or extensions:

// src/app/api/users.rs
use hearth::{
    Result,
    router::{StatusCode, content::Json, route},
};

#[route(POST)]
async fn create_user() -> Result<(StatusCode, Json<User>)> {
    let user = User {
        name: "Ada".to_string(),
    };
    Ok((StatusCode::CREATED, Json(user)))
}

Use Js or Wasm when serving JavaScript or WebAssembly bytes directly from a route. They set the content types required by browser module loaders.

Implement IntoResponse yourself for a type that should control its own status, headers, and body. A page sets its status and headers from inside the view! body instead; see the view! macro docs.

Multipart form data

multipart/form-data is the request format browsers use for forms that upload files. Behind the multipart feature, the Multipart extractor parses such a body and yields each form field in turn, streaming its data.

See the multipart module docs for reading fields and their metadata.

WebSockets

A WebSocket starts as an ordinary GET request that asks the server to switch protocols. Behind the websocket feature, a route serves one by taking a WebSocketUpgrade parameter and returning the response its on_upgrade builds; the callback then exchanges messages with the client for as long as the connection lives.

See the websocket module docs for the handshake, messages, subprotocols, and connection limits.

Server-sent events

Server-sent events push a one-way stream of events from the server to the client over a plain HTTP response. Behind the sse feature, a route becomes such a stream by returning Sse wrapping a stream of Events.

See the sse module docs for building events, keeping idle streams alive, and resuming after a reconnect.

Sitemaps

A sitemap lists the URLs of a site for crawlers. Behind the sitemap feature, a route serves one by building a Sitemap entry by entry and returning it; relative entries are resolved against the base URL registered on the router.

See the sitemap module docs for serving the sitemap at /sitemap.xml and the fields of an entry.