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