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::{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 let bytes = Bytes::from_request(req, state).await?;
109 Self::from_bytes(&bytes)
110 } else {
111 Err(MissingJsonContentType.into())
112 }
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 content_type = if let Some(content_type) = headers.get(header::CONTENT_TYPE) {
140 content_type
141 } else {
142 return false;
143 };
144
145 let content_type = if let Ok(content_type) = content_type.to_str() {
146 content_type
147 } else {
148 return false;
149 };
150
151 let mime = if let Ok(mime) = content_type.parse::<mime::Mime>() {
152 mime
153 } else {
154 return false;
155 };
156
157 let is_json_content_type = mime.type_() == "application"
158 && (mime.subtype() == "json" || mime.suffix().is_some_and(|name| name == "json"));
159
160 is_json_content_type
161}
162
163axum_core::__impl_deref!(Json);
164
165impl<T> From<T> for Json<T> {
166 fn from(inner: T) -> Self {
167 Self(inner)
168 }
169}
170
171impl<T> Json<T>
172where
173 T: DeserializeOwned,
174{
175 pub fn from_bytes(bytes: &[u8]) -> Result<Self, JsonRejection> {
179 let deserializer = &mut serde_json::Deserializer::from_slice(bytes);
180
181 let value = match serde_path_to_error::deserialize(deserializer) {
182 Ok(value) => value,
183 Err(err) => {
184 let rejection = match err.inner().classify() {
185 serde_json::error::Category::Data => JsonDataError::from_err(err).into(),
186 serde_json::error::Category::Syntax | serde_json::error::Category::Eof => {
187 JsonSyntaxError::from_err(err).into()
188 }
189 serde_json::error::Category::Io => {
190 if cfg!(debug_assertions) {
191 unreachable!()
194 } else {
195 JsonSyntaxError::from_err(err).into()
196 }
197 }
198 };
199 return Err(rejection);
200 }
201 };
202
203 Ok(Json(value))
204 }
205}
206
207impl<T> IntoResponse for Json<T>
208where
209 T: Serialize,
210{
211 fn into_response(self) -> Response {
212 let mut buf = BytesMut::with_capacity(128).writer();
215 match serde_json::to_writer(&mut buf, &self.0) {
216 Ok(()) => (
217 [(
218 header::CONTENT_TYPE,
219 HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()),
220 )],
221 buf.into_inner().freeze(),
222 )
223 .into_response(),
224 Err(err) => (
225 StatusCode::INTERNAL_SERVER_ERROR,
226 [(
227 header::CONTENT_TYPE,
228 HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()),
229 )],
230 err.to_string(),
231 )
232 .into_response(),
233 }
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240 use crate::{routing::post, test_helpers::*, Router};
241 use serde::Deserialize;
242 use serde_json::{json, Value};
243
244 #[crate::test]
245 async fn deserialize_body() {
246 #[derive(Debug, Deserialize)]
247 struct Input {
248 foo: String,
249 }
250
251 let app = Router::new().route("/", post(|input: Json<Input>| async { input.0.foo }));
252
253 let client = TestClient::new(app);
254 let res = client.post("/").json(&json!({ "foo": "bar" })).await;
255 let body = res.text().await;
256
257 assert_eq!(body, "bar");
258 }
259
260 #[crate::test]
261 async fn consume_body_to_json_requires_json_content_type() {
262 #[derive(Debug, Deserialize)]
263 struct Input {
264 foo: String,
265 }
266
267 let app = Router::new().route("/", post(|input: Json<Input>| async { input.0.foo }));
268
269 let client = TestClient::new(app);
270 let res = client.post("/").body(r#"{ "foo": "bar" }"#).await;
271
272 let status = res.status();
273
274 assert_eq!(status, StatusCode::UNSUPPORTED_MEDIA_TYPE);
275 }
276
277 #[crate::test]
278 async fn json_content_types() {
279 async fn valid_json_content_type(content_type: &str) -> bool {
280 println!("testing {content_type:?}");
281
282 let app = Router::new().route("/", post(|Json(_): Json<Value>| async {}));
283
284 let res = TestClient::new(app)
285 .post("/")
286 .header("content-type", content_type)
287 .body("{}")
288 .await;
289
290 res.status() == StatusCode::OK
291 }
292
293 assert!(valid_json_content_type("application/json").await);
294 assert!(valid_json_content_type("application/json; charset=utf-8").await);
295 assert!(valid_json_content_type("application/json;charset=utf-8").await);
296 assert!(valid_json_content_type("application/cloudevents+json").await);
297 assert!(!valid_json_content_type("text/json").await);
298 }
299
300 #[crate::test]
301 async fn invalid_json_syntax() {
302 let app = Router::new().route("/", post(|_: Json<serde_json::Value>| async {}));
303
304 let client = TestClient::new(app);
305 let res = client
306 .post("/")
307 .body("{")
308 .header("content-type", "application/json")
309 .await;
310
311 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
312 }
313
314 #[derive(Deserialize)]
315 struct Foo {
316 #[allow(dead_code)]
317 a: i32,
318 #[allow(dead_code)]
319 b: Vec<Bar>,
320 }
321
322 #[derive(Deserialize)]
323 struct Bar {
324 #[allow(dead_code)]
325 x: i32,
326 #[allow(dead_code)]
327 y: i32,
328 }
329
330 #[crate::test]
331 async fn invalid_json_data() {
332 let app = Router::new().route("/", post(|_: Json<Foo>| async {}));
333
334 let client = TestClient::new(app);
335 let res = client
336 .post("/")
337 .body("{\"a\": 1, \"b\": [{\"x\": 2}]}")
338 .header("content-type", "application/json")
339 .await;
340
341 assert_eq!(res.status(), StatusCode::UNPROCESSABLE_ENTITY);
342 let body_text = res.text().await;
343 assert_eq!(
344 body_text,
345 "Failed to deserialize the JSON body into the target type: b[0]: missing field `y` at line 1 column 23"
346 );
347 }
348}