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#[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 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 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 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}