use crate::{ body::{self, BoxBody}, extract::{rejection::*, FromRequest, RequestParts}, response::IntoResponse, BoxError, }; use async_trait::async_trait; use http::{ header::{self, HeaderValue}, StatusCode, }; use http_body::Full; use hyper::Response; use serde::{de::DeserializeOwned, Serialize}; use std::ops::{Deref, DerefMut}; /// JSON Extractor / Response. /// /// When used as an extractor, it can deserialize request bodies into some type that /// implements [`serde::Deserialize`]. If the request body cannot be parsed, or it does not contain /// the `Content-Type: application/json` header, it will reject the request and return a /// `400 Bad Request` response. /// /// # Extractor example /// /// ```rust,no_run /// use axum::{ /// extract, /// routing::post, /// Router, /// }; /// use serde::Deserialize; /// /// #[derive(Deserialize)] /// struct CreateUser { /// email: String, /// password: String, /// } /// /// async fn create_user(extract::Json(payload): extract::Json) { /// // payload is a `CreateUser` /// } /// /// let app = Router::new().route("/users", post(create_user)); /// # async { /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # }; /// ``` /// /// When used as a response, it can serialize any type that implements [`serde::Serialize`] to /// `JSON`, and will automatically set `Content-Type: application/json` header. /// /// # Response example /// /// ``` /// use axum::{ /// extract::Path, /// routing::get, /// Router, /// Json, /// }; /// use serde::Serialize; /// use uuid::Uuid; /// /// #[derive(Serialize)] /// struct User { /// id: Uuid, /// username: String, /// } /// /// async fn get_user(Path(user_id) : Path) -> Json { /// let user = find_user(user_id).await; /// Json(user) /// } /// /// async fn find_user(user_id: Uuid) -> User { /// // ... /// # unimplemented!() /// } /// /// let app = Router::new().route("/users/:id", get(get_user)); /// # async { /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # }; /// ``` #[derive(Debug, Clone, Copy, Default)] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub struct Json(pub T); #[async_trait] impl FromRequest for Json where T: DeserializeOwned, B: http_body::Body + Send, B::Data: Send, B::Error: Into, { type Rejection = JsonRejection; async fn from_request(req: &mut RequestParts) -> Result { if json_content_type(req)? { let bytes = bytes::Bytes::from_request(req).await?; let value = serde_json::from_slice(&bytes).map_err(InvalidJsonBody::from_err)?; Ok(Json(value)) } else { Err(MissingJsonContentType.into()) } } } fn json_content_type(req: &RequestParts) -> Result { let content_type = if let Some(content_type) = req .headers() .ok_or_else(HeadersAlreadyExtracted::default)? .get(header::CONTENT_TYPE) { content_type } else { return Ok(false); }; let content_type = if let Ok(content_type) = content_type.to_str() { content_type } else { return Ok(false); }; let mime = if let Ok(mime) = content_type.parse::() { mime } else { return Ok(false); }; let is_json_content_type = mime.type_() == "application" && (mime.subtype() == "json" || mime.suffix().filter(|name| *name == "json").is_some()); Ok(is_json_content_type) } impl Deref for Json { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Json { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl From for Json { fn from(inner: T) -> Self { Self(inner) } } impl IntoResponse for Json where T: Serialize, { fn into_response(self) -> Response { let bytes = match serde_json::to_vec(&self.0) { Ok(res) => res, Err(err) => { return Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) .header( header::CONTENT_TYPE, HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()), ) .body(body::boxed(Full::from(err.to_string()))) .unwrap(); } }; let mut res = Response::new(body::boxed(Full::from(bytes))); res.headers_mut().insert( header::CONTENT_TYPE, HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()), ); res } } #[cfg(test)] mod tests { use super::*; use crate::{routing::post, test_helpers::*, Router}; use serde::Deserialize; use serde_json::{json, Value}; #[tokio::test] async fn deserialize_body() { #[derive(Debug, Deserialize)] struct Input { foo: String, } let app = Router::new().route("/", post(|input: Json| async { input.0.foo })); let client = TestClient::new(app); let res = client.post("/").json(&json!({ "foo": "bar" })).send().await; let body = res.text().await; assert_eq!(body, "bar"); } #[tokio::test] async fn consume_body_to_json_requires_json_content_type() { #[derive(Debug, Deserialize)] struct Input { foo: String, } let app = Router::new().route("/", post(|input: Json| async { input.0.foo })); let client = TestClient::new(app); let res = client.post("/").body(r#"{ "foo": "bar" }"#).send().await; let status = res.status(); dbg!(res.text().await); assert_eq!(status, StatusCode::BAD_REQUEST); } #[tokio::test] async fn json_content_types() { async fn valid_json_content_type(content_type: &str) -> bool { println!("testing {:?}", content_type); let app = Router::new().route("/", post(|Json(_): Json| async {})); let res = TestClient::new(app) .post("/") .header("content-type", content_type) .body("{}") .send() .await; res.status() == StatusCode::OK } assert!(valid_json_content_type("application/json").await); assert!(valid_json_content_type("application/json; charset=utf-8").await); assert!(valid_json_content_type("application/json;charset=utf-8").await); assert!(valid_json_content_type("application/cloudevents+json").await); assert!(!valid_json_content_type("text/json").await); } }