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

为何Rocket框架的验证逻辑无法在Json类型参数上生效?

问题描述

在使用Rocket框架时,为CreateUserDto结构体字段添加的#[field(validate)]验证规则(含自定义邮箱验证、内置范围验证),仅在端点使用Form<CreateUserDto>作为参数时生效;改用Json<CreateUserDto>作为参数时,所有验证逻辑完全不触发。

相关代码

带验证规则的DTO定义

#[derive(Debug, Deserialize, FromForm)]
pub struct CreateUserDto<'r> {
    #[field(validate = range(2..10))]
    id: usize,
    #[field(validate = email_validation())]
    pub email: &'r str,
    pub password: &'r str,
}

fn email_validation(email: &str) -> Result<()> {
    let re: Regex = Regex::new(r"^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$").unwrap();

    if !re.is_match(email) {
        Err(Error::validation("invalid email address"))?;
    }

    Ok(())
}

验证不生效的JSON端点

#[post("/", data = "<input>")]
fn create_user(input: Json<CreateUserDto>) {
    let user = input.into_inner();
    println!("email {0}, password: {1}", user.email, user.password);
}

验证生效的Form端点

#[post("/", data = "<input>")]
fn create_user(input: Form<CreateUserDto>) {
    let user = input.into_inner();
    println!("email {0}, password: {1}", user.email, user.password);
}

解决方案

Rocket的#[field(validate)]注解是专为FromForm trait设计的,仅在解析表单数据(Form<T>类型)时触发验证;而Json<T>依赖serde完成反序列化,不会识别这类验证规则。要让验证逻辑对JSON请求生效,可采用以下方案:

方案1:端点内手动调用验证

为DTO实现统一的验证方法,在端点获取JSON数据后手动触发验证:

#[derive(Debug, Deserialize, FromForm)]
pub struct CreateUserDto<'r> {
    pub id: usize,
    pub email: &'r str,
    pub password: &'r str,
}

impl<'r> CreateUserDto<'r> {
    fn validate(&self) -> Result<(), Error> {
        // 验证id范围
        if !(2..10).contains(&self.id) {
            return Err(Error::validation("id must be between 2 and 9"));
        }
        // 验证邮箱格式
        let re = Regex::new(r"^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$").unwrap();
        if !re.is_match(self.email) {
            return Err(Error::validation("invalid email address"));
        }
        Ok(())
    }
}

#[post("/", data = "<input>")]
fn create_user(input: Json<CreateUserDto>) -> Result<(), Error> {
    let user = input.into_inner();
    user.validate()?;
    println!("email {0}, password: {1}", user.email, user.password);
    Ok(())
}

方案2:自定义serde反序列化嵌入验证

在JSON反序列化过程中直接嵌入验证逻辑,确保反序列化完成后数据必然合法:

use serde::{Deserialize, Deserializer};
use rocket::serde::de::Error as SerdeError;

#[derive(Debug, FromForm)]
pub struct CreateUserDto<'r> {
    pub id: usize,
    pub email: &'r str,
    pub password: &'r str,
}

impl<'de: 'r, 'r> Deserialize<'de> for CreateUserDto<'r> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        // 定义内部结构体完成基础反序列化
        #[derive(Deserialize)]
        struct InnerDto<'r> {
            id: usize,
            email: &'r str,
            password: &'r str,
        }

        let inner = InnerDto::deserialize(deserializer)?;

        // 验证id范围
        if !(2..10).contains(&inner.id) {
            return Err(SerdeError::custom("id must be between 2 and 9"));
        }
        // 验证邮箱格式
        let re = Regex::new(r"^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$").unwrap();
        if !re.is_match(inner.email) {
            return Err(SerdeError::custom("invalid email address"));
        }

        Ok(CreateUserDto {
            id: inner.id,
            email: inner.email,
            password: inner.password,
        })
    }
}

#[post("/", data = "<input>")]
fn create_user(input: Json<CreateUserDto>) {
    let user = input.into_inner();
    println!("email {0}, password: {1}", user.email, user.password);
}

方案3:使用第三方验证库扩展

引入rocket_validation这类第三方库,实现类似表单验证的注解式JSON验证:

  1. 添加依赖到Cargo.toml:
[dependencies]
rocket_validation = "0.1.0" # 请使用最新版本
  1. 修改DTO定义:
use rocket_validation::Validate;

#[derive(Debug, Deserialize, FromForm, Validate)]
pub struct CreateUserDto<'r> {
    #[validate(range(min = 2, max = 9))]
    pub id: usize,
    #[validate(email)]
    pub email: &'r str,
    pub password: &'r str,
}
  1. 端点内调用验证:
#[post("/", data = "<input>")]
fn create_user(input: Json<CreateUserDto>) -> Result<(), Error> {
    let user = input.into_inner();
    user.validate()?;
    println!("email {0}, password: {1}", user.email, user.password);
    Ok(())
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 06:22:45