5个版本 (重大变更)
0.16.0 | 2022年7月25日 |
---|---|
0.15.0 | 2022年5月3日 |
0.14.0 | 2021年6月29日 |
0.12.0 | 2020年11月26日 |
0.11.0 | 2020年9月9日 |
在Rust模式中排名1656
每月下载量616,443
6KB
88 行
validator
Macros 1.1自定义 derive 用于简化结构体验证,灵感来源于marshmallow和Django验证器。
最低支持的 Rust 版本是 1.42。
安装
[dependencies]
validator = { version = "0.15", 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(phone)]
phone: String,
#[validate(url)]
site: String,
#[validate(length(min = 1), custom = "validate_unique_username")]
#[serde(rename = "firstName")]
first_name: String,
#[validate(range(min = 18, max = 20))]
age: u32,
}
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
。该字段的任何错误都将出现在哈希表的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,
#[validate(phone)]
phone: String
}
#[derive(Debug, Validate, Deserialize)]
struct Preference {
#[validate(length(min = 4))]
name: String,
value: bool,
}
match signup_data.validate() {
Ok(_) => (),
Err(e) => return e;
};
在这里,ContactDetails
和 Preference
结构体嵌套在父 SignupData
结构体内部。因为这些子类型也派生了 Validate
,所以它们出现的字段可以被标记为包含在父结构体的验证方法中。
在单个嵌套结构体(本例中的 contact_details
字段)中找到的任何错误都会作为父结构体的 ValidationErrors
结果中的 Struct(Box<ValidationErrors>>)
类型返回。
在嵌套结构体数组(本例中的 preferences
字段)中找到的任何错误都会作为父结构体的 ValidationErrors
结果中的 List(BTreeMap<usize, Box<ValidationErrors>>)
类型返回,其中映射是以无效向量条目的索引为键。
使用方法
您需要导入 Validate
特性。
validator
插件也可以不使用自定义派生使用,因为它公开了所有验证函数和类型。
验证器
插件附带了一些内置验证器,并且可以为特定字段拥有多个验证器。
测试字符串是否为有效的电子邮件地址,根据 HTML5 正则表达式,这意味着它将一些古怪的电子邮件标记为无效,这些电子邮件在 email
输入中也不有效。此验证器不接收任何参数:#[validate(email)]
。
url
测试字符串是否为有效的 URL。此验证器不接收任何参数:#[validate(url)]
;
length
测试字符串或 Vec 是否符合给定的长度要求。 length
有 3 个整数参数
- min
- max
- equal
使用 equal
排除 min
或 max
,如果它们被发现,将会导致编译错误。
至少需要一个参数,最多可以有 2 个(同时拥有 min
和 max
)。
示例
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 个参数 min
和 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"))]
must_match
测试两个字段是否相等。 must_match
需要 1 个字符串参数。如果提到的字段缺失或与属性所在的字段类型不同,将引发错误。
示例
#[validate(must_match = "password2")]
#[validate(must_match(other = "password2"))]
contains
测试字符串是否包含给定的子字符串或 hashmap 中是否存在键。 contains
需要 1 个字符串参数。
示例
#[validate(contains = "gmail")]
#[validate(contains(pattern = "gmail"))]
does_not_contain
基本上与 contains 相反,仅为了方便使用。测试容器是否不包含给定的子字符串(如果它是字符串)或在 hashmap 中不存在键。 does_not_contain
需要 1 个字符串参数。
示例
#[validate(does_not_contain = "gmail")]
#[validate(does_not_contain(pattern = "gmail"))]
regex
检测字符串是否与提供的正则表达式匹配。regex
接受 1 个字符串参数:静态 Regex 实例的路径。
示例
lazy_static! {
static ref RE_TWO_CHARS: Regex = Regex::new(r"[a-z]{2}$").unwrap();
}
#[validate(regex = "RE_TWO_CHARS")]
#[validate(regex(path = "RE_TWO_CHARS"))]
credit_card
测试字符串是否是有效的信用卡号码。
示例
#[validate(credit_card)]
phone
测试字符串是否是有效的电话号码(国际格式,即包含国家代码,如 +14152370800
表示美国号码 —— 其中 4152370800
是国家号码等效,被视为无效)。要使用此验证器,您必须为 validator
包启用 phone
功能。此验证器不接受任何参数:#[validate(phone)]
;
custom
调用您的其中一个函数以执行自定义验证。字段引用将作为参数传递给函数,该函数应返回一个 Result<(), ValidationError>
。
示例
#[validate(custom = "validate_something")]
#[validate(custom = "::utils::validate_something")]
#[validate(custom(function = "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.rs
和 validator_derive_tests/tests/custom_args.rs
了解更多示例。
nested
对实现 Validate 特性(或此类类型的向量)的类型字段执行验证。
示例
#[validate]
non_control_character
测试字符串是否包含任何 utf-8 控制字符,如果有则验证失败。要使用此验证器,您必须为 validator
包启用 unic
功能。此验证器不接受任何参数:#[validate(non_control_character)]
;
required
测试 Option<T>
字段是否为 Some
;
required_nested
测试 Option<T>
字段是否为 Some
并执行与 nested
一样的验证;
结构体级别验证
通常,一些错误验证只能在查看整个结构体时应用,这里是如何实现的。
#[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__
中。
消息和代码
除了它们自己的参数外,每个验证器还可以接受2个可选参数
message
:与错误相关的消息,例如如果您想进行国际化(i18n)code
:每个验证器都有一个默认的错误代码(例如,正则表达式验证器的代码是regex
),但必要时可以覆盖,主要用于custom
验证器
请注意,这些参数不能应用于带有 #[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"))]
依赖关系
~1.5MB
~35K SLoC