use axum::{
extract::{FromRequest, Json},
response::{IntoResponse, Response},
};
use alun_core::api::ApiError;
use serde::de::DeserializeOwned;
#[derive(Debug, Clone, Copy, Default)]
pub struct ValidatedJson<T>(pub T);
impl<T, S> FromRequest<S> for ValidatedJson<T>
where
T: DeserializeOwned,
S: Send + Sync,
{
type Rejection = ValidatedJsonRejection;
async fn from_request(req: axum::extract::Request, state: &S) -> Result<Self, Self::Rejection> {
match Json::<T>::from_request(req, state).await {
Ok(Json(value)) => Ok(ValidatedJson(value)),
Err(rejection) => {
let msg = format!("请求体格式错误: {}", rejection.body_text());
Err(ValidatedJsonRejection(ApiError::bad_request(msg)))
}
}
}
}
impl<T: validator::Validate> ValidatedJson<T> {
pub fn validate(self) -> Result<Self, ApiError> {
self.0.validate().map_err(|e| {
ApiError::unprocessable_entity(
alun_utils::valid::Valid::format_validation_errors(&e)
)
})?;
Ok(self)
}
}
impl<T> std::ops::Deref for ValidatedJson<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> std::ops::DerefMut for ValidatedJson<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Debug)]
pub struct ValidatedJsonRejection(pub ApiError);
impl IntoResponse for ValidatedJsonRejection {
fn into_response(self) -> Response {
self.0.into_response()
}
}
impl From<ValidatedJsonRejection> for ApiError {
fn from(rejection: ValidatedJsonRejection) -> Self {
rejection.0
}
}