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