Cookbook / Requests and responses

Building responses

Status codes, headers, redirects, downloads and streaming, when a plain return value is not enough.

Most handlers return a value and let IntoResponse do the work. When you need control over headers or the body, build a Response.

ReturnResponse
&str, String200 text/plain
Json(value)200 application/json
Html(text)200 text/html
(status, body)body with the given status
Option<T>T, or 404 for None
()204 No Content
Result<T, E>T, or the error
Responsefull control
src/main.rs
use axeon::{Html, Response, Server};
use futures::stream;

fn main() {
    let mut app = Server::new();

    app.get("/page", |_req| async { Html("<h1>Hi</h1>") });

    // A download with a filename and cache headers.
    app.get("/report.csv", |_req| async {
        let mut res = Response::binary("text/csv", "id,name\n1,Ada\n2,Grace\n");
        res.attachment("report.csv").cache_control("no-store");
        res
    });

    // Builder style: status and header in one expression.
    app.post("/things", |_req| async {
        Response::text("made").with_status(201).with_header("Location", "/things/7")
    });

    app.get("/old", |_req| async { Response::permanent_redirect("/page") });
    app.get("/home", |_req| async { Response::redirect("/page") });

    // Send a body in chunks instead of building it in memory.
    app.get("/count", |_req| async {
        let chunks = stream::iter((1..=5).map(|n| Ok::<_, std::io::Error>(format!("{}\n", n))));
        Response::stream("text/plain", chunks)
    });

    app.listen("127.0.0.1:3000").expect("server failed to start");
}

Notes#

  • Response::json(&value) serialises any Serialize type. Use with_status to change the code.
  • Prefer Response::stream_with_length when you know the size, so clients see a Content-Length and can show progress.
  • Large files should be served by static files, which stream from disk and honour range requests.