Skip to content

Commit

Permalink
Json: add from_bytes method, use that in 'impl FromRequest'
Browse files Browse the repository at this point in the history
Signed-off-by: wayne warren <[email protected]>
  • Loading branch information
waynr committed Sep 28, 2023
1 parent c8cf147 commit ceab243
Showing 1 changed file with 42 additions and 25 deletions.
67 changes: 42 additions & 25 deletions axum/src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,31 +103,7 @@ where
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
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())
}
Expand Down Expand Up @@ -167,6 +143,47 @@ impl<T> From<T> for Json<T> {
}
}

impl<T> Json<T>
where
T: DeserializeOwned,
{
/// Construct a `Json<T>` 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<T>`.
///
/// 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<Self, JsonRejection> {
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<T> IntoResponse for Json<T>
where
T: Serialize,
Expand Down

0 comments on commit ceab243

Please sign in to comment.