axum/
json.rs

1use crate::extract::Request;
2use crate::extract::{rejection::*, FromRequest};
3use async_trait::async_trait;
4use axum_core::response::{IntoResponse, Response};
5use bytes::{BufMut, Bytes, BytesMut};
6use http::{
7    header::{self, HeaderMap, HeaderValue},
8    StatusCode,
9};
10use serde::{de::DeserializeOwned, Serialize};
11
12/// JSON Extractor / Response.
13///
14/// When used as an extractor, it can deserialize request bodies into some type that
15/// implements [`serde::de::DeserializeOwned`]. The request will be rejected (and a [`JsonRejection`] will
16/// be returned) if:
17///
18/// - The request doesn't have a `Content-Type: application/json` (or similar) header.
19/// - The body doesn't contain syntactically valid JSON.
20/// - The body contains syntactically valid JSON, but it couldn't be deserialized into the target
21/// type.
22/// - Buffering the request body fails.
23///
24/// ⚠️ Since parsing JSON requires consuming the request body, the `Json` extractor must be
25/// *last* if there are multiple extractors in a handler.
26/// See ["the order of extractors"][order-of-extractors]
27///
28/// [order-of-extractors]: crate::extract#the-order-of-extractors
29///
30/// See [`JsonRejection`] for more details.
31///
32/// # Extractor example
33///
34/// ```rust,no_run
35/// use axum::{
36///     extract,
37///     routing::post,
38///     Router,
39/// };
40/// use serde::Deserialize;
41///
42/// #[derive(Deserialize)]
43/// struct CreateUser {
44///     email: String,
45///     password: String,
46/// }
47///
48/// async fn create_user(extract::Json(payload): extract::Json<CreateUser>) {
49///     // payload is a `CreateUser`
50/// }
51///
52/// let app = Router::new().route("/users", post(create_user));
53/// # let _: Router = app;
54/// ```
55///
56/// When used as a response, it can serialize any type that implements [`serde::Serialize`] to
57/// `JSON`, and will automatically set `Content-Type: application/json` header.
58///
59/// # Response example
60///
61/// ```
62/// use axum::{
63///     extract::Path,
64///     routing::get,
65///     Router,
66///     Json,
67/// };
68/// use serde::Serialize;
69/// use uuid::Uuid;
70///
71/// #[derive(Serialize)]
72/// struct User {
73///     id: Uuid,
74///     username: String,
75/// }
76///
77/// async fn get_user(Path(user_id) : Path<Uuid>) -> Json<User> {
78///     let user = find_user(user_id).await;
79///     Json(user)
80/// }
81///
82/// async fn find_user(user_id: Uuid) -> User {
83///     // ...
84///     # unimplemented!()
85/// }
86///
87/// let app = Router::new().route("/users/:id", get(get_user));
88/// # let _: Router = app;
89/// ```
90#[derive(Debug, Clone, Copy, Default)]
91#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
92#[must_use]
93pub struct Json<T>(pub T);
94
95#[async_trait]
96impl<T, S> FromRequest<S> for Json<T>
97where
98    T: DeserializeOwned,
99    S: Send + Sync,
100{
101    type Rejection = JsonRejection;
102
103    async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
104        if json_content_type(req.headers()) {
105            let bytes = Bytes::from_request(req, state).await?;
106            Self::from_bytes(&bytes)
107        } else {
108            Err(MissingJsonContentType.into())
109        }
110    }
111}
112
113fn json_content_type(headers: &HeaderMap) -> bool {
114    let content_type = if let Some(content_type) = headers.get(header::CONTENT_TYPE) {
115        content_type
116    } else {
117        return false;
118    };
119
120    let content_type = if let Ok(content_type) = content_type.to_str() {
121        content_type
122    } else {
123        return false;
124    };
125
126    let mime = if let Ok(mime) = content_type.parse::<mime::Mime>() {
127        mime
128    } else {
129        return false;
130    };
131
132    let is_json_content_type = mime.type_() == "application"
133        && (mime.subtype() == "json" || mime.suffix().map_or(false, |name| name == "json"));
134
135    is_json_content_type
136}
137
138axum_core::__impl_deref!(Json);
139
140impl<T> From<T> for Json<T> {
141    fn from(inner: T) -> Self {
142        Self(inner)
143    }
144}
145
146impl<T> Json<T>
147where
148    T: DeserializeOwned,
149{
150    /// Construct a `Json<T>` from a byte slice. Most users should prefer to use the `FromRequest` impl
151    /// but special cases may require first extracting a `Request` into `Bytes` then optionally
152    /// constructing a `Json<T>`.
153    pub fn from_bytes(bytes: &[u8]) -> Result<Self, JsonRejection> {
154        let deserializer = &mut serde_json::Deserializer::from_slice(bytes);
155
156        let value = match serde_path_to_error::deserialize(deserializer) {
157            Ok(value) => value,
158            Err(err) => {
159                let rejection = match err.inner().classify() {
160                    serde_json::error::Category::Data => JsonDataError::from_err(err).into(),
161                    serde_json::error::Category::Syntax | serde_json::error::Category::Eof => {
162                        JsonSyntaxError::from_err(err).into()
163                    }
164                    serde_json::error::Category::Io => {
165                        if cfg!(debug_assertions) {
166                            // we don't use `serde_json::from_reader` and instead always buffer
167                            // bodies first, so we shouldn't encounter any IO errors
168                            unreachable!()
169                        } else {
170                            JsonSyntaxError::from_err(err).into()
171                        }
172                    }
173                };
174                return Err(rejection);
175            }
176        };
177
178        Ok(Json(value))
179    }
180}
181
182impl<T> IntoResponse for Json<T>
183where
184    T: Serialize,
185{
186    fn into_response(self) -> Response {
187        // Use a small initial capacity of 128 bytes like serde_json::to_vec
188        // https://docs.rs/serde_json/1.0.82/src/serde_json/ser.rs.html#2189
189        let mut buf = BytesMut::with_capacity(128).writer();
190        match serde_json::to_writer(&mut buf, &self.0) {
191            Ok(()) => (
192                [(
193                    header::CONTENT_TYPE,
194                    HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()),
195                )],
196                buf.into_inner().freeze(),
197            )
198                .into_response(),
199            Err(err) => (
200                StatusCode::INTERNAL_SERVER_ERROR,
201                [(
202                    header::CONTENT_TYPE,
203                    HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()),
204                )],
205                err.to_string(),
206            )
207                .into_response(),
208        }
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use super::*;
215    use crate::{routing::post, test_helpers::*, Router};
216    use serde::Deserialize;
217    use serde_json::{json, Value};
218
219    #[crate::test]
220    async fn deserialize_body() {
221        #[derive(Debug, Deserialize)]
222        struct Input {
223            foo: String,
224        }
225
226        let app = Router::new().route("/", post(|input: Json<Input>| async { input.0.foo }));
227
228        let client = TestClient::new(app);
229        let res = client.post("/").json(&json!({ "foo": "bar" })).await;
230        let body = res.text().await;
231
232        assert_eq!(body, "bar");
233    }
234
235    #[crate::test]
236    async fn consume_body_to_json_requires_json_content_type() {
237        #[derive(Debug, Deserialize)]
238        struct Input {
239            foo: String,
240        }
241
242        let app = Router::new().route("/", post(|input: Json<Input>| async { input.0.foo }));
243
244        let client = TestClient::new(app);
245        let res = client.post("/").body(r#"{ "foo": "bar" }"#).await;
246
247        let status = res.status();
248
249        assert_eq!(status, StatusCode::UNSUPPORTED_MEDIA_TYPE);
250    }
251
252    #[crate::test]
253    async fn json_content_types() {
254        async fn valid_json_content_type(content_type: &str) -> bool {
255            println!("testing {content_type:?}");
256
257            let app = Router::new().route("/", post(|Json(_): Json<Value>| async {}));
258
259            let res = TestClient::new(app)
260                .post("/")
261                .header("content-type", content_type)
262                .body("{}")
263                .await;
264
265            res.status() == StatusCode::OK
266        }
267
268        assert!(valid_json_content_type("application/json").await);
269        assert!(valid_json_content_type("application/json; charset=utf-8").await);
270        assert!(valid_json_content_type("application/json;charset=utf-8").await);
271        assert!(valid_json_content_type("application/cloudevents+json").await);
272        assert!(!valid_json_content_type("text/json").await);
273    }
274
275    #[crate::test]
276    async fn invalid_json_syntax() {
277        let app = Router::new().route("/", post(|_: Json<serde_json::Value>| async {}));
278
279        let client = TestClient::new(app);
280        let res = client
281            .post("/")
282            .body("{")
283            .header("content-type", "application/json")
284            .await;
285
286        assert_eq!(res.status(), StatusCode::BAD_REQUEST);
287    }
288
289    #[derive(Deserialize)]
290    struct Foo {
291        #[allow(dead_code)]
292        a: i32,
293        #[allow(dead_code)]
294        b: Vec<Bar>,
295    }
296
297    #[derive(Deserialize)]
298    struct Bar {
299        #[allow(dead_code)]
300        x: i32,
301        #[allow(dead_code)]
302        y: i32,
303    }
304
305    #[crate::test]
306    async fn invalid_json_data() {
307        let app = Router::new().route("/", post(|_: Json<Foo>| async {}));
308
309        let client = TestClient::new(app);
310        let res = client
311            .post("/")
312            .body("{\"a\": 1, \"b\": [{\"x\": 2}]}")
313            .header("content-type", "application/json")
314            .await;
315
316        assert_eq!(res.status(), StatusCode::UNPROCESSABLE_ENTITY);
317        let body_text = res.text().await;
318        assert_eq!(
319            body_text,
320            "Failed to deserialize the JSON body into the target type: b[0]: missing field `y` at line 1 column 23"
321        );
322    }
323}