rust 46 lines · 8 steps

Content negotiation in an Axum handler

An Axum handler inspects the Accept header and returns JSON, HTML, or a 406 from the same route.

Explained by highlit
1use axum::{
2 extract::Path,
3 http::{header::ACCEPT, HeaderMap, StatusCode},
4 response::{Html, IntoResponse, Json, Response},
5};
6use serde::Serialize;
7 
8#[derive(Serialize)]
9struct Product {
10 id: u64,
11 name: String,
12 price_cents: u32,
13}
14 
15pub async fn show_product(
16 Path(id): Path<u64>,
17 headers: HeaderMap,
18) -> Response {
19 let product = Product {
20 id,
21 name: "Aeron Chair".into(),
22 price_cents: 149900,
23 };
24 
25 let accept = headers
26 .get(ACCEPT)
27 .and_then(|v| v.to_str().ok())
28 .unwrap_or("*/*");
29 
30 if accept.contains("application/json") {
31 Json(product).into_response()
32 } else if accept.contains("text/html") || accept.contains("*/*") {
33 let body = format!(
34 "<article>\n <h1>{}</h1>\n <p>${:.2}</p>\n</article>",
35 product.name,
36 product.price_cents as f64 / 100.0,
37 );
38 Html(body).into_response()
39 } else {
40 (
41 StatusCode::NOT_ACCEPTABLE,
42 "Supported types: application/json, text/html",
43 )
44 .into_response()
45 }
46}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A single handler can serve multiple representations by branching on the Accept header.
  2. 2Returning the erased Response type lets one function yield JSON, HTML, or an error uniformly.
  3. 3Always provide a fallback so an unmatched Accept value produces a clear 406 rather than a surprise.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Content negotiation in an Axum handler — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code