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(pub T); #[async_trait] impl FromRequest for JsonBody where axum::Json: FromRequest, axum::Form: FromRequest, S: Send + Sync, T: Validate, { type Rejection = ResErr; async fn from_request(req: Request, state: &S) -> Result { 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::() // .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::::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::::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))) } }