1use crate::extract::Request;
2use crate::extract::{rejection::*, FromRequest};
3use axum_core::extract::OptionalFromRequest;
4use axum_core::response::{IntoResponse, Response};
5use bytes::{BufMut, Bytes, BytesMut};
6use http::{
7 header::{self, HeaderMap, HeaderValue},
8 StatusCode,
9};
10use serde_core::{de::DeserializeOwned, Serialize};
11
12#[derive(Debug, Clone, Copy, Default)]
95#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
96#[must_use]
97pub struct Json<T>(pub T);
98
99impl<T, S> FromRequest<S> for Json<T>
100where
101 T: DeserializeOwned,
102 S: Send + Sync,
103{
104 type Rejection = JsonRejection;
105
106 async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
107 if !json_content_type(req.headers()) {
108 return Err(MissingJsonContentType.into());
109 }
110
111 let bytes = Bytes::from_request(req, state).await?;
112 Self::from_bytes(&bytes)
113 }
114}
115
116impl<T, S> OptionalFromRequest<S> for Json<T>
117where
118 T: DeserializeOwned,
119 S: Send + Sync,
120{
121 type Rejection = JsonRejection;
122
123 async fn from_request(req: Request, state: &S) -> Result<Option<Self>, Self::Rejection> {
124 let headers = req.headers();
125 if headers.get(header::CONTENT_TYPE).is_some() {
126 if json_content_type(headers) {
127 let bytes = Bytes::from_request(req, state).await?;
128 Ok(Some(Self::from_bytes(&bytes)?))
129 } else {
130 Err(MissingJsonContentType.into())
131 }
132 } else {
133 Ok(None)
134 }
135 }
136}
137
138fn json_content_type(headers: &HeaderMap) -> bool {
139 let Some(content_type) = headers.get(header::CONTENT_TYPE) else {
140 return false;
141 };
142
143 let Ok(content_type) = content_type.to_str() else {
144 return false;
145 };
146
147 let Ok(mime) = content_type.parse::<mime::Mime>() else {
148 return false;
149 };
150
151 let is_json_content_type = mime.type_() == "application"
152 && (mime.subtype() == "json" || mime.suffix().is_some_and(|name| name == "json"));
153
154 is_json_content_type
155}
156
157axum_core::__impl_deref!(Json);
158
159impl<T> From<T> for Json<T> {
160 fn from(inner: T) -> Self {
161 Self(inner)
162 }
163}
164
165impl<T> Json<T>
166where
167 T: DeserializeOwned,
168{
169 pub fn from_bytes(bytes: &[u8]) -> Result<Self, JsonRejection> {
173 fn make_rejection(err: serde_path_to_error::Error<serde_json::Error>) -> JsonRejection {
175 match err.inner().classify() {
176 serde_json::error::Category::Data => JsonDataError::from_err(err).into(),
177 serde_json::error::Category::Syntax | serde_json::error::Category::Eof => {
178 JsonSyntaxError::from_err(err).into()
179 }
180 serde_json::error::Category::Io => {
181 if cfg!(debug_assertions) {
182 unreachable!()
185 } else {
186 JsonSyntaxError::from_err(err).into()
187 }
188 }
189 }
190 }
191
192 let mut deserializer = serde_json::Deserializer::from_slice(bytes);
193
194 serde_path_to_error::deserialize(&mut deserializer)
195 .map_err(make_rejection)
196 .and_then(|value| {
197 deserializer
198 .end()
199 .map(|()| Self(value))
200 .map_err(|err| JsonSyntaxError::from_err(err).into())
201 })
202 }
203}
204
205impl<T> IntoResponse for Json<T>
206where
207 T: Serialize,
208{
209 fn into_response(self) -> Response {
210 fn make_response(buf: BytesMut, ser_result: serde_json::Result<()>) -> Response {
212 match ser_result {
213 Ok(()) => (
214 [(
215 header::CONTENT_TYPE,
216 HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()),
217 )],
218 buf.freeze(),
219 )
220 .into_response(),
221 Err(err) => (
222 StatusCode::INTERNAL_SERVER_ERROR,
223 [(
224 header::CONTENT_TYPE,
225 HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()),
226 )],
227 err.to_string(),
228 )
229 .into_response(),
230 }
231 }
232
233 let mut buf = BytesMut::with_capacity(128).writer();
236 let res = serde_json::to_writer(&mut buf, &self.0);
237 make_response(buf.into_inner(), res)
238 }
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244 use crate::{routing::post, test_helpers::*, Router};
245 use serde::Deserialize;
246 use serde_json::{json, Value};
247
248 #[crate::test]
249 async fn deserialize_body() {
250 #[derive(Debug, Deserialize)]
251 struct Input {
252 foo: String,
253 }
254
255 let app = Router::new().route("/", post(|input: Json<Input>| async { input.0.foo }));
256
257 let client = TestClient::new(app);
258 let res = client.post("/").json(&json!({ "foo": "bar" })).await;
259 let body = res.text().await;
260
261 assert_eq!(body, "bar");
262 }
263
264 #[crate::test]
265 async fn consume_body_to_json_requires_json_content_type() {
266 #[derive(Debug, Deserialize)]
267 struct Input {
268 foo: String,
269 }
270
271 let app = Router::new().route("/", post(|input: Json<Input>| async { input.0.foo }));
272
273 let client = TestClient::new(app);
274 let res = client.post("/").body(r#"{ "foo": "bar" }"#).await;
275
276 let status = res.status();
277
278 assert_eq!(status, StatusCode::UNSUPPORTED_MEDIA_TYPE);
279 }
280
281 #[crate::test]
282 async fn json_content_types() {
283 async fn valid_json_content_type(content_type: &str) -> bool {
284 println!("testing {content_type:?}");
285
286 let app = Router::new().route("/", post(|Json(_): Json<Value>| async {}));
287
288 let res = TestClient::new(app)
289 .post("/")
290 .header("content-type", content_type)
291 .body("{}")
292 .await;
293
294 res.status() == StatusCode::OK
295 }
296
297 assert!(valid_json_content_type("application/json").await);
298 assert!(valid_json_content_type("application/json; charset=utf-8").await);
299 assert!(valid_json_content_type("application/json;charset=utf-8").await);
300 assert!(valid_json_content_type("application/cloudevents+json").await);
301 assert!(!valid_json_content_type("text/json").await);
302 }
303
304 #[crate::test]
305 async fn invalid_json_syntax() {
306 let app = Router::new().route("/", post(|_: Json<serde_json::Value>| async {}));
307
308 let client = TestClient::new(app);
309 let res = client
310 .post("/")
311 .body("{")
312 .header("content-type", "application/json")
313 .await;
314
315 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
316 }
317
318 #[crate::test]
319 async fn extra_chars_after_valid_json_syntax() {
320 #[derive(Debug, Deserialize)]
321 struct Input {
322 foo: String,
323 }
324
325 let app = Router::new().route("/", post(|input: Json<Input>| async { input.0.foo }));
326
327 let client = TestClient::new(app);
328 let res = client
329 .post("/")
330 .body(r#"{ "foo": "bar" } baz "#)
331 .header("content-type", "application/json")
332 .await;
333
334 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
335 let body_text = res.text().await;
336 assert_eq!(
337 body_text,
338 "Failed to parse the request body as JSON: trailing characters at line 1 column 18"
339 );
340 }
341
342 #[derive(Deserialize)]
343 struct Foo {
344 #[allow(dead_code)]
345 a: i32,
346 #[allow(dead_code)]
347 b: Vec<Bar>,
348 }
349
350 #[derive(Deserialize)]
351 struct Bar {
352 #[allow(dead_code)]
353 x: i32,
354 #[allow(dead_code)]
355 y: i32,
356 }
357
358 #[crate::test]
359 async fn invalid_json_data() {
360 let app = Router::new().route("/", post(|_: Json<Foo>| async {}));
361
362 let client = TestClient::new(app);
363 let res = client
364 .post("/")
365 .body("{\"a\": 1, \"b\": [{\"x\": 2}]}")
366 .header("content-type", "application/json")
367 .await;
368
369 assert_eq!(res.status(), StatusCode::UNPROCESSABLE_ENTITY);
370 let body_text = res.text().await;
371 assert_eq!(
372 body_text,
373 "Failed to deserialize the JSON body into the target type: b[0]: missing field `y` at line 1 column 23"
374 );
375 }
376}