use axum::{
extract::{FromRequest, OptionalFromRequest, Request, rejection::JsonRejection},
response::IntoResponse,
};
use http::StatusCode;
use serde::{Serialize, de::DeserializeOwned};
pub struct Json<T>(pub T);
impl<T: Serialize> IntoResponse for Json<T> {
fn into_response(self) -> axum::response::Response {
let Self(value) = self;
axum::Json(value).into_response()
}
}
impl<T, S> FromRequest<S> for Json<T>
where
T: DeserializeOwned,
S: Send + Sync,
{
type Rejection = StatusCode;
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
<axum::Json<_> as FromRequest<S>>::from_request(req, state)
.await
.map_err(to_unprocessable_entity)
.map(|value| Self(value.0))
}
}
impl<T, S> OptionalFromRequest<S> for Json<T>
where
T: DeserializeOwned,
S: Send + Sync,
{
type Rejection = StatusCode;
async fn from_request(req: Request, state: &S) -> Result<Option<Self>, Self::Rejection> {
<axum::Json<_> as OptionalFromRequest<S>>::from_request(req, state)
.await
.map_err(to_unprocessable_entity)
.map(|value| value.map(|value| Self(value.0)))
}
}
#[track_caller]
fn to_unprocessable_entity(value: JsonRejection) -> StatusCode {
log::warn!(
"request contained an unprocessable body ({}): {}",
value.status(),
value.body_text()
);
StatusCode::UNPROCESSABLE_ENTITY
}