Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_text/tests/annealer_corpus/axum_json.rs

11.3 KiB, 1 run

created by r1870400018:11690, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1use crate::extract::Request;
2use crate::extract::{rejection::*, FromRequest};
3use axum_core::extract::OptionalFromRequest;
4use axum_core::response::{IntoResponse, IntoResponseFailed, Response};
5use bytes::{BufMut, Bytes, BytesMut};
6use http::{
7 header::{self, HeaderMap, HeaderValue},
8 StatusCode,
9};
10use serde_core::{de::DeserializeOwned, Serialize};
11
12/// JSON Extractor / Response.
13///
14/// When used as an extractor, it can deserialize request bodies into some type that
15/// implements [`serde::de::DeserializeOwned`]. The request will be rejected (and a [`JsonRejection`] will
16/// be returned) if:
17///
18/// - The request doesn't have a `Content-Type: application/json` (or similar) header.
19/// - The body doesn't contain syntactically valid JSON.
20/// - The body contains syntactically valid JSON, but it couldn't be deserialized into the target type.
21/// - Buffering the request body fails.
22///
23/// ⚠️ Since parsing JSON requires consuming the request body, the `Json` extractor must be
24/// *last* if there are multiple extractors in a handler.
25/// See ["the order of extractors"][order-of-extractors]
26///
27/// [order-of-extractors]: crate::extract#the-order-of-extractors
28///
29/// See [`JsonRejection`] for more details.
30///
31/// # Extractor example
32///
33/// ```rust,no_run
34/// use axum::{
35/// extract,
36/// routing::post,
37/// Router,
38/// };
39/// use serde::Deserialize;
40///
41/// #[derive(Deserialize)]
42/// struct CreateUser {
43/// email: String,
44/// password: String,
45/// }
46///
47/// async fn create_user(extract::Json(payload): extract::Json<CreateUser>) {
48/// // payload is a `CreateUser`
49/// }
50///
51/// let app = Router::new().route("/users", post(create_user));
52/// # let _: Router = app;
53/// ```
54///
55/// When used as a response, it can serialize any type that implements [`serde::Serialize`] to
56/// `JSON`, and will automatically set `Content-Type: application/json` header.
57///
58/// If the [`Serialize`] implementation decides to fail
59/// or if a map with non-string keys is used,
60/// a 500 response will be issued
61/// whose body is the error message in UTF-8.
62///
63/// # Response example
64///
65/// ```
66/// use axum::{
67/// extract::Path,
68/// routing::get,
69/// Router,
70/// Json,
71/// };
72/// use serde::Serialize;
73/// use uuid::Uuid;
74///
75/// #[derive(Serialize)]
76/// struct User {
77/// id: Uuid,
78/// username: String,
79/// }
80///
81/// async fn get_user(Path(user_id) : Path<Uuid>) -> Json<User> {
82/// let user = find_user(user_id).await;
83/// Json(user)
84/// }
85///
86/// async fn find_user(user_id: Uuid) -> User {
87/// // ...
88/// # unimplemented!()
89/// }
90///
91/// let app = Router::new().route("/users/{id}", get(get_user));
92/// # let _: Router = app;
93/// ```
94#[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 headers
140 .get(header::CONTENT_TYPE)
141 .and_then(|content_type| content_type.to_str().ok())
142 .and_then(|content_type| content_type.parse::<mime::Mime>().ok())
143 .is_some_and(|mime| {
144 mime.type_() == "application"
145 && (mime.subtype() == "json" || mime.suffix().is_some_and(|name| name == "json"))
146 })
147}
148
149axum_core::__impl_deref!(Json);
150
151impl<T> From<T> for Json<T> {
152 fn from(inner: T) -> Self {
153 Self(inner)
154 }
155}
156
157impl<T> Json<T>
158where
159 T: DeserializeOwned,
160{
161 /// Construct a `Json<T>` from a byte slice. Most users should prefer to use the `FromRequest` impl
162 /// but special cases may require first extracting a `Request` into `Bytes` then optionally
163 /// constructing a `Json<T>`.
164 pub fn from_bytes(bytes: &[u8]) -> Result<Self, JsonRejection> {
165 // Extracted into separate fn so it's only compiled once for all T.
166 fn make_rejection(err: serde_path_to_error::Error<serde_json::Error>) -> JsonRejection {
167 match err.inner().classify() {
168 serde_json::error::Category::Data => JsonDataError::from_err(err).into(),
169 serde_json::error::Category::Syntax | serde_json::error::Category::Eof => {
170 JsonSyntaxError::from_err(err).into()
171 }
172 serde_json::error::Category::Io => {
173 if cfg!(debug_assertions) {
174 // we don't use `serde_json::from_reader` and instead always buffer
175 // bodies first, so we shouldn't encounter any IO errors
176 unreachable!()
177 } else {
178 JsonSyntaxError::from_err(err).into()
179 }
180 }
181 }
182 }
183
184 let mut deserializer = serde_json::Deserializer::from_slice(bytes);
185
186 serde_path_to_error::deserialize(&mut deserializer)
187 .map_err(make_rejection)
188 .and_then(|value| {
189 deserializer
190 .end()
191 .map(|()| Self(value))
192 .map_err(|err| JsonSyntaxError::from_err(err).into())
193 })
194 }
195}
196
197impl<T> IntoResponse for Json<T>
198where
199 T: Serialize,
200{
201 fn into_response(self) -> Response {
202 // Extracted into separate fn so it's only compiled once for all T.
203 fn make_response(buf: BytesMut, ser_result: serde_json::Result<()>) -> Response {
204 match ser_result {
205 Ok(()) => (
206 [(
207 header::CONTENT_TYPE,
208 HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()),
209 )],
210 buf.freeze(),
211 )
212 .into_response(),
213 Err(err) => (
214 StatusCode::INTERNAL_SERVER_ERROR,
215 [(
216 header::CONTENT_TYPE,
217 HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()),
218 )],
219 IntoResponseFailed,
220 err.to_string(),
221 )
222 .into_response(),
223 }
224 }
225
226 // Use a small initial capacity of 128 bytes like serde_json::to_vec
227 // https://docs.rs/serde_json/1.0.82/src/serde_json/ser.rs.html#2189
228 let mut buf = BytesMut::with_capacity(128).writer();
229 let res = serde_json::to_writer(&mut buf, &self.0);
230 make_response(buf.into_inner(), res)
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237 use crate::{routing::post, test_helpers::*, Router};
238 use serde::Deserialize;
239 use serde_json::{json, Value};
240
241 #[crate::test]
242 async fn deserialize_body() {
243 #[derive(Debug, Deserialize)]
244 struct Input {
245 foo: String,
246 }
247
248 let app = Router::new().route("/", post(|input: Json<Input>| async { input.0.foo }));
249
250 let client = TestClient::new(app);
251 let res = client.post("/").json(&json!({ "foo": "bar" })).await;
252 let body = res.text().await;
253
254 assert_eq!(body, "bar");
255 }
256
257 #[crate::test]
258 async fn consume_body_to_json_requires_json_content_type() {
259 #[derive(Debug, Deserialize)]
260 struct Input {
261 foo: String,
262 }
263
264 let app = Router::new().route("/", post(|input: Json<Input>| async { input.0.foo }));
265
266 let client = TestClient::new(app);
267 let res = client.post("/").body(r#"{ "foo": "bar" }"#).await;
268
269 let status = res.status();
270
271 assert_eq!(status, StatusCode::UNSUPPORTED_MEDIA_TYPE);
272 }
273
274 #[crate::test]
275 async fn json_content_types() {
276 async fn valid_json_content_type(content_type: &str) -> bool {
277 println!("testing {content_type:?}");
278
279 let app = Router::new().route("/", post(|Json(_): Json<Value>| async {}));
280
281 let res = TestClient::new(app)
282 .post("/")
283 .header("content-type", content_type)
284 .body("{}")
285 .await;
286
287 res.status() == StatusCode::OK
288 }
289
290 assert!(valid_json_content_type("application/json").await);
291 assert!(valid_json_content_type("application/json; charset=utf-8").await);
292 assert!(valid_json_content_type("application/json;charset=utf-8").await);
293 assert!(valid_json_content_type("application/cloudevents+json").await);
294 assert!(!valid_json_content_type("text/json").await);
295 }
296
297 #[crate::test]
298 async fn invalid_json_syntax() {
299 let app = Router::new().route("/", post(|_: Json<serde_json::Value>| async {}));
300
301 let client = TestClient::new(app);
302 let res = client
303 .post("/")
304 .body("{")
305 .header("content-type", "application/json")
306 .await;
307
308 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
309 }
310
311 #[crate::test]
312 async fn extra_chars_after_valid_json_syntax() {
313 #[derive(Debug, Deserialize)]
314 struct Input {
315 foo: String,
316 }
317
318 let app = Router::new().route("/", post(|input: Json<Input>| async { input.0.foo }));
319
320 let client = TestClient::new(app);
321 let res = client
322 .post("/")
323 .body(r#"{ "foo": "bar" } baz "#)
324 .header("content-type", "application/json")
325 .await;
326
327 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
328 let body_text = res.text().await;
329 assert_eq!(
330 body_text,
331 "Failed to parse the request body as JSON: trailing characters at line 1 column 18"
332 );
333 }
334
335 #[derive(Deserialize)]
336 struct Foo {
337 #[allow(dead_code)]
338 a: i32,
339 #[allow(dead_code)]
340 b: Vec<Bar>,
341 }
342
343 #[derive(Deserialize)]
344 struct Bar {
345 #[allow(dead_code)]
346 x: i32,
347 #[allow(dead_code)]
348 y: i32,
349 }
350
351 #[crate::test]
352 async fn invalid_json_data() {
353 let app = Router::new().route("/", post(|_: Json<Foo>| async {}));
354
355 let client = TestClient::new(app);
356 let res = client
357 .post("/")
358 .body("{\"a\": 1, \"b\": [{\"x\": 2}]}")
359 .header("content-type", "application/json")
360 .await;
361
362 assert_eq!(res.status(), StatusCode::UNPROCESSABLE_ENTITY);
363 let body_text = res.text().await;
364 assert_eq!(
365 body_text,
366 "Failed to deserialize the JSON body into the target type: b[0]: missing field `y` at line 1 column 23"
367 );
368 }
369}