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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A single handler can serve multiple representations by branching on the Accept header.
- 2Returning the erased Response type lets one function yield JSON, HTML, or an error uniformly.
- 3Always provide a fallback so an unmatched Accept value produces a clear 406 rather than a surprise.
Related explainers
rust
use rand::distributions::{Alphanumeric, DistString}; use rand::rngs::OsRng; #[derive(Debug, Clone, PartialEq, Eq)]
A newtype wrapper for API tokens in Rust
newtype-pattern
randomness
encapsulation
Intermediate
6 steps
rust
pub fn normalize_path(input: &str) -> String { let is_absolute = input.starts_with('/'); let has_trailing_slash = input.len() > 1 && input.ends_with('/'); let mut stack: Vec<&str> = Vec::new();
Normalizing filesystem paths in Rust
string-processing
stack
path-manipulation
Intermediate
8 steps
rust
use axum::{ extract::Query, response::IntoResponse, Json,
Parsing query strings in Axum handlers
deserialization
query-parameters
defaults
Intermediate
7 steps
rust
use axum::{ body::{Body, Bytes}, extract::Request, http::StatusCode,
Logging request and response sizes in Axum
middleware
http
streaming-bodies
Advanced
8 steps
rust
use std::borrow::Cow; pub fn escape_html(input: &str) -> Cow<'_, str> { let needs_escape = input
Zero-copy HTML escaping with Cow in Rust
clone-on-write
zero-copy
string-escaping
Intermediate
6 steps
rust
use once_cell::sync::Lazy; use regex::Regex; static CREDIT_CARD: Lazy<Regex> = Lazy::new(|| {
Redacting sensitive data from logs in Rust
regex
lazy-initialization
checksum-validation
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/content-negotiation-in-an-axum-handler-explained-rust-8d85/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.