chuanyue-service/library/src/extractor/body_extractor.rs
2024-11-06 10:39:37 +08:00

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)))
}
}