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)]
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 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 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 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}