rust 58 lines · 7 steps

A strict JSON extractor in Axum

Building a custom Axum extractor that rejects requests unless they carry a real JSON Content-Type.

Explained by highlit
1use axum::{
2 async_trait,
3 body::Bytes,
4 extract::FromRequest,
5 http::{header::CONTENT_TYPE, Request, StatusCode},
6 response::{IntoResponse, Response},
7 Json,
8};
9use serde::de::DeserializeOwned;
10 
11pub struct StrictJson<T>(pub T);
12 
13#[async_trait]
14impl<T, S, B> FromRequest<S, B> for StrictJson<T>
15where
16 T: DeserializeOwned,
17 B: axum::body::HttpBody + Send + 'static,
18 B::Data: Send,
19 B::Error: std::error::Error + Send + Sync + 'static,
20 S: Send + Sync,
21{
22 type Rejection = Response;
23 
24 async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
25 let content_type = req
26 .headers()
27 .get(CONTENT_TYPE)
28 .and_then(|value| value.to_str().ok())
29 .ok_or_else(|| {
30 (StatusCode::BAD_REQUEST, "missing Content-Type header").into_response()
31 })?;
32 
33 let mime: mime::Mime = content_type.parse().map_err(|_| {
34 (StatusCode::BAD_REQUEST, "malformed Content-Type header").into_response()
35 })?;
36 
37 let is_json = mime.type_() == mime::APPLICATION
38 && (mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON));
39 
40 if !is_json {
41 return Err((
42 StatusCode::UNSUPPORTED_MEDIA_TYPE,
43 "expected Content-Type: application/json",
44 )
45 .into_response());
46 }
47 
48 let bytes = Bytes::from_request(req, state)
49 .await
50 .map_err(IntoResponse::into_response)?;
51 
52 let value = serde_json::from_slice(&bytes).map_err(|err| {
53 (StatusCode::UNPROCESSABLE_ENTITY, format!("invalid JSON body: {err}")).into_response()
54 })?;
55 
56 Ok(StrictJson(value))
57 }
58}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Implementing FromRequest lets you plug custom validation directly into a handler's argument list.
  2. 2Parsing the Content-Type as a real MIME type is safer than a naive string equality check.
  3. 3Returning Response as the Rejection type gives you full control over each failure's status code and message.

Related explainers

Share this explainer

Here's the card — post it anywhere.

A strict JSON extractor in Axum — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code