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