You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Axum Rust中实现ValidatorRequest时遭遇Trait冲突问题

解决Axum中请求验证器的Trait冲突问题

问题背景

在Rust的Axum框架中实现请求体验证器时,遇到了FromRequest trait的冲突错误,需求是能自动验证带有请求体的用户请求。

原实现代码

use async_trait::async_trait;
use axum::extract::{rejection::JsonRejection, FromRequest};
use axum::{body::HttpBody, http::Request, BoxError, Json};
use axum::response::{IntoResponse, Response};
use serde::de::DeserializeOwned;
use thiserror::Error;
use validator::Validate;
use crate::v3::config::response::ApiErrorResponse;

#[derive(Debug, Error)]
pub enum RequestError {
    #[error(transparent)]
    ValidationError(#[from] validator::ValidationErrors),
    #[error(transparent)]
    JsonRejection(#[from] JsonRejection),
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ValidatedRequest<T>(pub T);

#[async_trait]
impl<T, S, B> FromRequest<S, B> for ValidatedRequest<T>
    where
        T: DeserializeOwned + Validate,
        S: Send + Sync,
        B: HttpBody + Send + 'static,
        B::Data: Send,
        B::Error: Into<BoxError>,
{
    type Rejection = RequestError;

    async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
        let Json(value) = Json::<T>::from_request(req, state).await?;
        value.validate()?;
        Ok(ValidatedRequest(value))
    }
}

impl IntoResponse for RequestError {
    fn into_response(self) -> Response {
        match self {
            RequestError::ValidationError(_) => {
                ApiErrorResponse::send(400, Some(self.to_string().replace('\n', ", ")))
            }
            RequestError::JsonRejection(_) => ApiErrorResponse::send(400, Some(self.to_string())),
        }
    }
}

错误信息

error[E0119]: conflicting implementations of trait `FromRequest<_, axum_core::extract::private::ViaParts>` for type `ValidatedRequest<_>`
  --> src/v3/error/request.rs:21:1
   |
21 | / impl<T, S, B> FromRequest<S, B> for ValidatedRequest<T>
22 | |     where
23 | |         T: DeserializeOwned + Validate,
24 | |         S: Send + Sync,
25 | |         B: HttpBody + Send + 'static,
26 | |         B::Data: Send,
27 | |         B::Error: Into<BoxError>,
   | |_________________________________^
   |
   = note: conflicting implementation in crate `axum_core`:
           - impl<S, T> FromRequest<S, axum_core::extract::private::ViaParts> for T
             where S: Send, S: Sync, T: FromRequestParts<S>;;
   = note: downstream crates may implement trait `axum::extract::FromRequestParts<_>` for type `v3::error::request::ValidatedRequest<_>`
   = note: upstream crates may add a new impl of trait `axum::body::HttpBody` for type `axum_core::extract::private::ViaParts` in future versions

问题原因

冲突源于Axum的axum_core crate中的一个泛型 blanket 实现:当类型T实现了FromRequestParts时,自动为其实现针对ViaParts类型的FromRequest。我们的ValidatedRequest<T>的泛型FromRequest实现覆盖了所有HttpBody类型,包括ViaParts,导致编译器无法区分两个可行的实现。

解决方法

方案一:排除冲突类型

通过引入Axum内部的Sealed trait(ViaParts实现了该trait,且外部类型无法实现),在FromRequest的约束中排除这类内部类型:

修改后的代码:

use async_trait::async_trait;
use axum::extract::{rejection::JsonRejection, FromRequest};
use axum::{body::HttpBody, http::Request, BoxError, Json};
use axum::response::{IntoResponse, Response};
use serde::de::DeserializeOwned;
use thiserror::Error;
use validator::Validate;
use crate::v3::config::response::ApiErrorResponse;
// 引入Axum内部的Sealed trait
use axum_core::extract::private::Sealed;

#[derive(Debug, Error)]
pub enum RequestError {
    #[error(transparent)]
    ValidationError(#[from] validator::ValidationErrors),
    #[error(transparent)]
    JsonRejection(#[from] JsonRejection),
}

#[derive(Debug, Clone, Copy, Default)]
pub struct ValidatedRequest<T>(pub T);

#[async_trait]
impl<T, S, B> FromRequest<S, B> for ValidatedRequest<T>
where
    T: DeserializeOwned + Validate,
    S: Send + Sync,
    B: HttpBody + Send + 'static + !Sealed, // 添加约束排除ViaParts类型
    B::Data: Send,
    B::Error: Into<BoxError>,
{
    type Rejection = RequestError;

    async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
        let Json(value) = Json::<T>::from_request(req, state).await?;
        value.validate()?;
        Ok(ValidatedRequest(value))
    }
}

impl IntoResponse for RequestError {
    fn into_response(self) -> Response {
        match self {
            RequestError::ValidationError(_) => {
                ApiErrorResponse::send(400, Some(self.to_string().replace('\n', ", ")))
            }
            RequestError::JsonRejection(_) => ApiErrorResponse::send(400, Some(self.to_string())),
        }
    }
}

方案二:handler内手动验证

如果不想依赖Axum内部私有trait,可直接在handler中使用Json<T>并手动触发验证:

// 假设User是实现了Validate的结构体
async fn create_user(Json(user): Json<User>) -> Result<impl IntoResponse, RequestError> {
    user.validate()?;
    // 业务逻辑处理
    Ok(Json(UserResponse { id: 1 }))
}

方案一更适合需要在多个handler中复用验证逻辑的场景,方案二更简洁直接。

内容的提问来源于stack exchange,提问作者Dan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 11:12:16