#validation #api #macro-derive

validator_derive

Macros 1.1实现#[derive(Validate)]

28个版本 (17个重大更改)

0.18.1 2024年4月11日
0.17.0 2024年3月4日
0.16.0 2022年7月25日
0.14.0 2021年6月29日
0.1.0 2016年12月28日

#538 in 过程宏

Download history 144682/week @ 2024-04-26 155194/week @ 2024-05-03 163022/week @ 2024-05-10 173746/week @ 2024-05-17 169510/week @ 2024-05-24 189730/week @ 2024-05-31 174150/week @ 2024-06-07 158751/week @ 2024-06-14 177757/week @ 2024-06-21 169360/week @ 2024-06-28 172601/week @ 2024-07-05 184446/week @ 2024-07-12 183956/week @ 2024-07-19 180226/week @ 2024-07-26 167472/week @ 2024-08-02 159901/week @ 2024-08-09

728,440 每月下载量
用于 257 个库 (16个直接使用)

MIT 许可证

55KB
1K SLoC

validator

受 marshmallow 和 Django 验证器启发的结构验证宏 1.1 实现以简化宏 derive

最低支持版本是 Rust 1.42。

安装

[dependencies]
validator = { version = "0.16", features = ["derive"] }

简短示例

use serde::Deserialize;

// A trait that the Validate derive will impl
use validator::{Validate, ValidationError};

#[derive(Debug, Validate, Deserialize)]
struct SignupData {
    #[validate(email)]
    mail: String,
    #[validate(url)]
    site: String,
    #[validate(length(min = 1), custom(function = "validate_unique_username"))]
    #[serde(rename = "firstName")]
    first_name: String,
    #[validate(range(min = 18, max = 20))]
    age: u32,
    #[validate(range(exclusive_min = 0.0, max = 100.0))]
    height: f32,
}

fn validate_unique_username(username: &str) -> Result<(), ValidationError> {
    if username == "xXxShad0wxXx" {
        // the value of the username will automatically be added later
        return Err(ValidationError::new("terrible_username"));
    }

    Ok(())
}

match signup_data.validate() {
  Ok(_) => (),
  Err(e) => return e;
};

Option<_> 字段上执行验证,如果选项是 Some,则将执行包含类型的验证。 validate() 方法返回一个 Result<(), ValidationErrors>。在无效结果的情况下,ValidationErrors 实例包括一个以结构字段名称为键的错误映射。错误可以以三种方式表示,如 ValidationErrorsKind 枚举中所述

#[derive(Debug, Serialize, Clone, PartialEq)]
#[serde(untagged)]
pub enum ValidationErrorsKind {
    Struct(Box<ValidationErrors>),
    List(BTreeMap<usize, Box<ValidationErrors>>),
    Field(Vec<ValidationError>),
}

在上面的简单示例中,任何错误都将具有 Field(Vec<ValidationError>) 类型,其中单个 ValidationError 的结构如下

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct ValidationError {
  pub code: Cow<'static, str>,
  pub message: Option<Cow<'static, str>>,
  pub params: HashMap<Cow<'static, str>, Value>,
}

字段的值将自动添加到具有 value 键的参数中。

请注意,validator 与 serde 一起使用:在示例中,我们可以看到 first_name 字段已被重命名为 firstName。该字段的任何错误都将出现在 hashmap 的 firstName 键中,而不是 first_name

另外两种 ValidationErrorsKind 类型表示在嵌套(结构体向量)中发现的错误,如本例所述。

use serde::Deserialize;
// A trait that the Validate derive will impl
use validator::Validate;

#[derive(Debug, Validate, Deserialize)]
struct SignupData {
   #[validate]
   contact_details: ContactDetails,
   #[validate]
   preferences: Vec<Preference>,
   #[validate(required)]
   allow_cookies: Option<bool>,
}

#[derive(Debug, Validate, Deserialize)]
struct ContactDetails {
   #[validate(email)]
   mail: String,
}

#[derive(Debug, Validate, Deserialize)]
struct Preference {
   #[validate(length(min = 4))]
   name: String,
   value: bool,
}

match signup_data.validate() {
 Ok(_) => (),
 Err(e) => return e;
};

在这里,ContactDetailsPreference 结构体嵌套在父 SignupData 结构体中。因为这些子类型也派生了 Validate,所以它们出现的字段可以被标记为包含在父结构体的验证方法中。

在单个嵌套结构体(本例中的 contact_details 字段)中发现的任何错误都将作为父结构体 ValidationErrors 结果中的 Struct(Box<ValidationErrors>) 类型返回。

在嵌套结构体向量(本例中的 preferences 字段)中发现的任何错误将作为父结构体 ValidationErrors 结果中的 List(BTreeMap<usize, Box<ValidationErrors>>) 类型返回,其中映射是根据无效向量条目的索引键的。

用法

您需要导入 Validate 特性。

validator 包也可以在不使用自定义派生的前提下使用,因为它公开了所有验证函数和类型。

验证器

该包包含一些内置验证器,并且可以为给定字段有多个验证器。

email

测试字符串是否是有效的电子邮件地址,根据 HTML5 正则表达式,这意味着它将标记一些古怪电子邮件地址为无效,这些地址在 email 输入中也是无效的。此验证器不接受任何参数:#[validate(email)]

url

测试字符串是否是有效的 URL。此验证器不接受任何参数:#[validate(url)];

length

测试字符串或 Vec 是否匹配给定的长度要求。 length 有 3 个整数参数

  • min
  • max
  • equal

使用 equal 排除了 minmax,如果在它们中发现这些参数,将导致编译错误。

需要至少一个参数,最多两个参数(同时具有 minmax)。

示例

const MIN_CONST: u64 = 1;
const MAX_CONST: u64 = 10;

#[validate(length(min = 1, max = 10))]
#[validate(length(min = 1))]
#[validate(length(max = 10))]
#[validate(length(equal = 10))]
#[validate(length(min = "MIN_CONST", max = "MAX_CONST"))]

range

测试数字是否在给定的范围内。 range 需要 1 或 2 个参数,它们可以是普通(minmax)或排他性(exclusive_minexclusive_max、不可达限制)。这些可以是数字或值路径。

示例

const MAX_CONSTANT: i32 = 10;
const MIN_CONSTANT: i32 = 0;

#[validate(range(min = 1))]
#[validate(range(min = "MIN_CONSTANT"))]
#[validate(range(min = 1, max = 10))]
#[validate(range(min = 1.1, max = 10.8))]
#[validate(range(max = 10.8))]
#[validate(range(min = "MAX_CONSTANT"))]
#[validate(range(min = "crate::MAX_CONSTANT"))]
#[validate(range(exclusive_min = 0.0, max = 100.0))]
#[validate(range(exclusive_max = 10))]

must_match

测试两个字段是否相等。 must_match 需要 1 个字符串参数。如果提到的字段缺失或与属性所在的字段类型不同,则将引发错误。

示例

#[validate(must_match = "password2")]
#[validate(must_match(other = "password2"))]

contains

测试字符串是否包含给定的子字符串,或者键是否存在于哈希映射中。 contains 需要 1 个字符串参数。

示例

#[validate(contains = "gmail")]
#[validate(contains(pattern = "gmail"))]

does_not_contain

几乎与contains相反,仅为了方便使用。测试容器是否不包含给定的子字符串,如果是字符串,或者在一个哈希表中键是否不存在。does_not_contain需要一个字符串参数。

示例

#[validate(does_not_contain = "gmail")]
#[validate(does_not_contain(pattern = "gmail"))]

正则表达式

测试字符串是否与给定的正则表达式匹配。regex需要一个字符串参数:静态正则表达式实例的路径。

示例

use once_cell::sync::Lazy;

static RE_TWO_CHARS: Lazy<Regex> = Lazy::new(|| {
    Regex::new(r"[a-z]{2}$").unwrap()
});

#[validate(regex = "RE_TWO_CHARS")]
#[validate(regex(path = "RE_TWO_CHARS"))]

信用卡

测试字符串是否为有效的信用卡号码。

示例

#[validate(credit_card)]

自定义

调用您的函数之一以执行自定义验证。字段引用将作为参数传递给函数,该函数应返回一个Result<(), ValidationError>

示例

#[validate(custom(function = "validate_something"))]
#[validate(custom(function = "::utils::validate_something"))]

您还可以通过设置arg参数从验证函数解析参数到自定义验证。 arg只能设置为一个类型,但您可以将它设置为一个元组一次传递多个类型。定义arg参数将实现具有相应函数类型的ValidateArgs特质,如下所示

use validator::{Validate, ValidateArgs, ValidationError};

fn validate(value: &str, arg: (i64, i64)) -> Result<(), ValidationError> {
    [...]
}

#[derive(Debug, Validate)]
struct TestStruct {
    #[validate(custom(function = "validate", arg = "(i64, i64)"))]
    value: String,
}

let test_struct: TestStruct = [...]
test_struct.validate_args((77, 555)).is_ok();

还可以通过使用生命周期'v_a来传递引用,请注意,这个生命周期应该只用于像这样的函数参数

fn validate_value(_: &str, arg: &mut Database) -> Result<(), ValidationError> {
    [...]
}

#[derive(Debug, Validate)]
struct TestStruct {
    //                                                     vvvv This is the lifetime for references
    #[validate(custom(function = "validate_value", arg = "&'v_a mut Database"))]
    value: String,
}

let mut database: Database = [...]
let test_struct: TestStruct = [...]
test_struct.validate_args(&mut database).is_ok();

具有参数的自定义验证不适用于嵌套验证。请参阅validator_derive_tests/tests/custom.rsvalidator_derive_tests/tests/custom_args.rs以获取更多示例。

嵌套

对实现Validate特质(或这些类型的向量)的类型字段执行验证。

示例

#[validate]

非控制字符

测试字符串是否包含任何utf-8控制字符,如果包含则验证失败。要使用此验证器,您必须为validator包启用unic功能。此验证器不接收任何参数:#[validate(non_control_character)];

必需的

测试Option<T>字段是否为Some;

结构级验证

通常,某些错误验证只能在查看整个结构时应用,这里是如何实现的

#[derive(Debug, Validate, Deserialize)]
#[validate(schema(function = "validate_category", skip_on_field_errors = false))]
struct CategoryData {
    category: String,
    name: String,
}

提到的函数应返回一个Result<(), ValidationError>,并在对所有字段完成验证后调用。

如果没有提供,skip_on_field_errors默认为true,这将确保如果结构字段验证过程中发生错误,则不会调用该函数。

结构级验证上的任何错误都将出现在错误哈希表的键__all__中。

消息和代码

每个验证器除了自己的参数外,还可以接受两个可选参数

  • message:一个与错误相关的消息,例如,如果您想进行i18n
  • code:每个验证器都有一个默认的错误代码(例如,正则表达式验证器的代码是regex),但在必要时可以覆盖,主要需要自定义验证器

请注意,这些参数不能应用于嵌套的带有 #[validate] 的验证调用。

例如,以下属性都有效

// code attribute
#[validate(email(code = "code_str"))]
#[validate(credit_card(code = "code_str"))]
#[validate(length(min = 5, max = 10, code = "code_str"))]

#[validate(regex(path = "static_regex", code = "code_str"))]
#[validate(custom(function = "custom_fn", code = "code_str"))]
#[validate(contains(pattern = "pattern_str", code = "code_str"))]
#[validate(does_not_contain(pattern = "pattern_str", code = "code_str"))]
#[validate(must_match(other = "match_value", code = "code_str"))]

// message attribute
#[validate(url(message = "message_str"))]
#[validate(length(min = 5, max = 10, message = "message_str"))]

#[validate(regex(path = "static_regex", message = "message_str"))]
#[validate(custom(function = "custom_fn", message = "message_str"))]
#[validate(contains(pattern = "pattern_str", message = "message_str"))]
#[validate(does_not_contain(pattern = "pattern_str", message = "message_str"))]
#[validate(must_match(other = "match_value", message = "message_str"))]

// both attributes
#[validate(url(message = "message", code = "code_str"))]
#[validate(email(code = "code_str", message = "message"))]
#[validate(custom(function = "custom_fn", code = "code_str", message = "message_str"))]

依赖项

~0.7–1.1MB
~25K SLoC