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 chrono::{DateTime, Duration, FixedOffset, Utc}; #[derive(Debug)] pub struct EventWindow {
Parsing and measuring time windows in Rust
datetime
error-handling
pattern-matching
Intermediate
7 steps
javascript
export function buildPaginationUrl(baseUrl, { page, perPage, filters = {}, sort } = {}) { const url = new URL(baseUrl); const params = url.searchParams;
Building and parsing pagination URLs
url-parsing
query-string
pagination
Intermediate
8 steps
php
<?php namespace App\Casts;
How a custom Eloquent cast wraps an Address in Laravel
value-objects
serialization
data-mapping
Intermediate
7 steps
rust
use axum::{extract::Multipart, http::StatusCode, Json}; use serde::Serialize; use tokio::io::AsyncWriteExt; use uuid::Uuid;
Streaming multipart uploads in Axum
multipart
streaming
async-io
Intermediate
10 steps
rust
use std::collections::HashMap; #[derive(Debug, Clone)] struct Record {
Batched aggregation with fold in Rust
iterators
fold
hashmap
Intermediate
9 steps
javascript
const crypto = require('crypto'); function securityHeaders(options = {}) { const {
A configurable security-headers middleware in Express
middleware
http-headers
content-security-policy
Intermediate
7 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.