diff --git a/axum/src/json.rs b/axum/src/json.rs index 74541c1e11..89e748343d 100644 --- a/axum/src/json.rs +++ b/axum/src/json.rs @@ -103,31 +103,7 @@ where async fn from_request(req: Request, state: &S) -> Result { if json_content_type(req.headers()) { let bytes = Bytes::from_request(req, state).await?; - let deserializer = &mut serde_json::Deserializer::from_slice(&bytes); - - let value = match serde_path_to_error::deserialize(deserializer) { - Ok(value) => value, - Err(err) => { - let rejection = match err.inner().classify() { - serde_json::error::Category::Data => JsonDataError::from_err(err).into(), - serde_json::error::Category::Syntax | serde_json::error::Category::Eof => { - JsonSyntaxError::from_err(err).into() - } - serde_json::error::Category::Io => { - if cfg!(debug_assertions) { - // we don't use `serde_json::from_reader` and instead always buffer - // bodies first, so we shouldn't encounter any IO errors - unreachable!() - } else { - JsonSyntaxError::from_err(err).into() - } - } - }; - return Err(rejection); - } - }; - - Ok(Json(value)) + Self::from_bytes(&bytes) } else { Err(MissingJsonContentType.into()) } @@ -167,6 +143,47 @@ impl From for Json { } } +impl Json +where + T: DeserializeOwned, +{ + /// Construct a `Json` from `&Bytes`. Most users should prefer to use the `FromRequest` impl + /// but special cases may require first extracting a `Request` into `Bytes` then optionally + /// constructing a `Json`. + /// + /// An example where this would be useful would be one in which a service needs to pass through + /// or otherwise preserve the exact byte representation of the request while also interrogating + /// it for metadata that may be useful in determining exactly what to do with the preserved + /// bytes. + fn from_bytes(bytes: &Bytes) -> Result { + let deserializer = &mut serde_json::Deserializer::from_slice(&bytes); + + let value = match serde_path_to_error::deserialize(deserializer) { + Ok(value) => value, + Err(err) => { + let rejection = match err.inner().classify() { + serde_json::error::Category::Data => JsonDataError::from_err(err).into(), + serde_json::error::Category::Syntax | serde_json::error::Category::Eof => { + JsonSyntaxError::from_err(err).into() + } + serde_json::error::Category::Io => { + if cfg!(debug_assertions) { + // we don't use `serde_json::from_reader` and instead always buffer + // bodies first, so we shouldn't encounter any IO errors + unreachable!() + } else { + JsonSyntaxError::from_err(err).into() + } + } + }; + return Err(rejection); + } + }; + + Ok(Json(value)) + } +} + impl IntoResponse for Json where T: Serialize,