86 lines
3.3 KiB
Rust
86 lines
3.3 KiB
Rust
use axum::{
|
|
async_trait,
|
|
extract::{
|
|
rejection::{FormRejection, JsonRejection},
|
|
FromRequest, Request,
|
|
},
|
|
};
|
|
use http::header::CONTENT_TYPE;
|
|
use i18n::{message, message_ids::MessageId};
|
|
use validator::Validate;
|
|
|
|
use crate::{context::Context, model::response::ResErr};
|
|
|
|
pub struct JsonBody<T>(pub T);
|
|
|
|
#[async_trait]
|
|
impl<S, T> FromRequest<S> for JsonBody<T>
|
|
where
|
|
axum::Json<T>: FromRequest<S, Rejection = JsonRejection>,
|
|
axum::Form<T>: FromRequest<S, Rejection = FormRejection>,
|
|
S: Send + Sync,
|
|
T: Validate,
|
|
{
|
|
type Rejection = ResErr;
|
|
|
|
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
|
|
let (parts, body) = req.into_parts();
|
|
|
|
// We can use other extractors to provide better rejection messages.
|
|
// For example, here we are using `axum::extract::MatchedPath` to
|
|
// provide a better error message.
|
|
//
|
|
// Have to run that first since `Json` extraction consumes the request.
|
|
// let path = parts
|
|
// .extract::<MatchedPath>()
|
|
// .await
|
|
// .map(|path| path.as_str().to_owned())
|
|
// .ok();
|
|
|
|
let context_parts = parts.clone();
|
|
let context: &Context = context_parts.extensions.get().unwrap();
|
|
let content_type_header = context_parts.headers.get(CONTENT_TYPE);
|
|
let content_type = content_type_header.and_then(|value| value.to_str().ok());
|
|
|
|
let lang_tag = context.get_lang_tag();
|
|
let req = Request::from_parts(parts, body);
|
|
|
|
if let Some(content_type) = content_type {
|
|
if content_type.starts_with("application/json") {
|
|
match axum::Json::<T>::from_request(req, state).await {
|
|
Ok(value) => {
|
|
let data = value.0;
|
|
match crate::model::validator::validate_params(&data, lang_tag) {
|
|
Ok(_) => return Ok(Self(data)),
|
|
Err(err) => return Err(err),
|
|
}
|
|
}
|
|
// convert the error from `axum::Json` into whatever we want
|
|
Err(rejection) => {
|
|
tracing::error!("无效的json数据: {:?}", rejection);
|
|
return Err(ResErr::params(message!(lang_tag, MessageId::InvalidParams)));
|
|
}
|
|
}
|
|
} else if content_type.starts_with("application/x-www-form-urlencoded") {
|
|
match axum::Form::<T>::from_request(req, state).await {
|
|
Ok(value) => {
|
|
let data = value.0;
|
|
match crate::model::validator::validate_params(&data, lang_tag) {
|
|
Ok(_) => return Ok(Self(data)),
|
|
Err(err) => return Err(err),
|
|
}
|
|
}
|
|
// convert the error from `axum::Json` into whatever we want
|
|
Err(rejection) => {
|
|
tracing::error!("无效的json数据: {:?}", rejection);
|
|
return Err(ResErr::params(message!(lang_tag, MessageId::InvalidParams)));
|
|
}
|
|
}
|
|
}
|
|
return Err(ResErr::params(message!(lang_tag, MessageId::InvalidParams)));
|
|
}
|
|
|
|
Err(ResErr::params(message!(lang_tag, MessageId::InvalidParams)))
|
|
}
|
|
}
|