Skip to main content

alun_web/
extract.rs

1//! 请求提取器:ValidatedJson —— 对标 aifei 的 Argument + Validate
2//!
3//! 设计要点:
4//! - 自动校验 JSON 请求体的字段合法性
5//! - 校验失败返回 422 Unprocessable Entity
6//! - 若目标类型实现 Validate trait,则自动调用 validate()
7//! - 也可使用 alun_utils::valid::Valid 独立校验
8
9use axum::{
10    extract::{FromRequest, Json},
11    response::{IntoResponse, Response},
12};
13use alun_core::api::ApiError;
14use serde::de::DeserializeOwned;
15
16/// 带自动校验的 JSON 提取器
17///
18/// # 示例
19///
20/// ```ignore
21/// #[derive(Debug, Deserialize, Validate)]
22/// pub struct CreateUserReq {
23///     #[validate(length(min = 2, max = 50))]
24///     pub username: String,
25///     #[validate(email)]
26///     pub email: String,
27/// }
28///
29/// async fn create_user(ValidatedJson(req): ValidatedJson<CreateUserReq>) -> Res<String> {
30///     Res::ok(req.username)
31/// }
32/// ```
33#[derive(Debug, Clone, Copy, Default)]
34pub struct ValidatedJson<T>(pub T);
35
36/// 实现 axum 的 `FromRequest` 提取器,将 JSON 请求体解析为 `T` 类型。
37///
38/// 解析失败时返回 `ValidatedJsonRejection`,该 rejection 会转换为 HTTP 400 响应。
39impl<T, S> FromRequest<S> for ValidatedJson<T>
40where
41    T: DeserializeOwned,
42    S: Send + Sync,
43{
44    type Rejection = ValidatedJsonRejection;
45
46    async fn from_request(req: axum::extract::Request, state: &S) -> Result<Self, Self::Rejection> {
47        match Json::<T>::from_request(req, state).await {
48            Ok(Json(value)) => Ok(ValidatedJson(value)),
49            Err(rejection) => {
50                let msg = format!("请求体格式错误: {}", rejection.body_text());
51                Err(ValidatedJsonRejection(ApiError::bad_request(msg)))
52            }
53        }
54    }
55}
56
57/// ValidatedJson 的校验便捷方法 —— 对实现了 `validator::Validate` 的类型调用 `validate()`
58impl<T: validator::Validate> ValidatedJson<T> {
59    /// 执行字段级校验
60    ///
61    /// 校验失败返回 `ApiError::unprocessable_entity`,包含格式化的校验错误列表。
62    pub fn validate(self) -> Result<Self, ApiError> {
63        self.0.validate().map_err(|e| {
64            ApiError::unprocessable_entity(
65                alun_utils::valid::Valid::format_validation_errors(&e)
66            )
67        })?;
68        Ok(self)
69    }
70}
71
72impl<T> std::ops::Deref for ValidatedJson<T> {
73    type Target = T;
74
75    fn deref(&self) -> &Self::Target {
76        &self.0
77    }
78}
79
80impl<T> std::ops::DerefMut for ValidatedJson<T> {
81    fn deref_mut(&mut self) -> &mut Self::Target {
82        &mut self.0
83    }
84}
85
86/// ValidatedJson 提取失败时的错误类型
87#[derive(Debug)]
88pub struct ValidatedJsonRejection(pub ApiError);
89
90impl IntoResponse for ValidatedJsonRejection {
91    fn into_response(self) -> Response {
92        self.0.into_response()
93    }
94}
95
96impl From<ValidatedJsonRejection> for ApiError {
97    fn from(rejection: ValidatedJsonRejection) -> Self {
98        rejection.0
99    }
100}