由于本文是翻译,所以将原文原原本本的搬上来,大家看原文有什么不懂的也可以对照这里。

给出地址:https://fluentvalidation.net/

FluentValidation

fluentvalidation是一款用于模型验证的组件,尤其是在asp.net core中,我们通常使用attribute的验证方式,比如[Required(ErrorMessage="xxxxx")]这种,还有很多。这个组件的好处是可以对模型的验证规则进行集中的管理,不会让模型的验证散落到程序的各个角落,所以是一个好东西,下面来介绍他的使用方法。

安装

最简单的安装方式是通过nuget包管理器或者通过dotnet cli命令。

nuget:Install-Package FluentValidation

.net core cli:dotnet add package FluentValidation

当我们在asp.net core中使用的时候,还要安装另外一个包:Install-Package FluentValidation.AspNetCore

创建第一个验证器(validator)

要创建一组针对某个模型的验证器,首先你要写一个继承了AbstractValidator<T>的类。T就代表一个需要被验证的模型类。

打个比方,首先假定你有如下一个类:

public class Customer {
public int Id { get; set; }
public string Surname { get; set; }
public string Forename { get; set; }
public decimal Discount { get; set; }
public string Address { get; set; }
}

你需要通过继承AbstractValidator<Customer>来定义一个验证Customer的类:

using FluentValidation; 

public class CustomerValidator : AbstractValidator<Customer> {
}

验证类中的这些验证规则应该定义在验证类的Rule方法中,该方法必须被重写 。

要为一个类指定一组验证规则,需要调用RuleFor方法,为该方法传递一个lambda表达式,这个表达式指定了你想要验证的这个类中的某一个属性。打个比方,如果你不想让CUstomer类中的Surname为空,那么你的验证类看起来像这样:

using FluentValidation;

public class CustomerValidator : AbstractValidator<Customer> {
public CustomerValidator() {
RuleFor(customer => customer.Surname).NotNull();
}
}

重要的说明:像NotNull()这样的方法就是一个验证器,validator

要运行这个验证类,你需要实例化它并且调用validate方法,并将被验证的类对象传入:

Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator(); ValidationResult result = validator.Validate(customer);

Validate方法返回一个ValidationResult 类型的对象,这个对象包含了两个属性:

  • IsValid:一个布尔值,指明这个验证有没有通过。
  • Errors:是一个包含了ValidationFailure 类型对象的一个集合,这个集合中包含了所有验证失败的细节。

下面的代码将每个错误在Console中写出来:

Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator(); ValidationResult results = validator.Validate(customer); if(! results.IsValid) {
foreach(var failure in results.Errors) {
Console.WriteLine("Property " + failure.PropertyName + " failed validation. Error was: " + failure.ErrorMessage);
}
}

ValidationResult的ToString方法会将所有的错误结果都表示出来,默认情况下,没个错误都会另起一行,当然你也可以通过给ToString方法传入一些字符来自定义一个分隔符:

ValidationResult results = validator.Validate(customer);
string allMessages = results.ToString("~"); // In this case, each message will be separated with a `~`

注意:如果没有错误发生(验证通过),那么ToString方法会返回一个空字符串。

支持链式调用

你可以在同一个被验证的属性上进行链式的调用:

using FluentValidation;

public class CustomerValidator : AbstractValidator<Customer> {
public CustomerValidator()
RuleFor(customer => customer.Surname).NotNull().NotEqual("foo");
}
}

可抛出异常

除了调用Validate,你还可以调用ValidateAndThrow,这会在验证失败后抛出异常。

Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator(); validator.ValidateAndThrow(customer);

这个方法会抛出一个ValidationException异常,包含了Erros属性上的所有错误信息。ValidationException是一个扩展方法,请确保你引用了FluentValidation这个命名空间。

验证集合类型的属性

你可以通过RuleForEach这个方法来对一个集合属性上的所有成员实施一个相同的检测:

public class Person {
public List<string> AddressLines {get;set;} = new List<string>();
}
public class PersonValidator : AbstractValidator<Person> {
public PersonValidator() {
RuleForEach(x => x.AddressLines).NotNull();
}
}

上面这个代码会对AddressLines这个属性上的每一项都实施一个非空的检测。

不过你可以通过Where方法对集合中的某几项元素的检测进行跳过,主要Where方法必须放在RuleForeach方法后:

RuleForEach(x => x.Orders)
.Where(x => x.Cost != null)
.SetValidator(new OrderValidator());

上面这段代码在我这里没有通过,没有Where这么一个方法,但是完全可以调用Orders的Where扩展方法来挑选元素,这个没什么我觉得。由于本文是翻译,所以将原文原原本本的搬上来,大家看原文有什么不懂的也可以对照这里。

复杂类型的属性

对于复杂类型的属性,定义的规则可以复用。打个比方你有两个类:

public class Customer {
public string Name { get; set; }
public Address Address { get; set; }
} public class Address {
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Town { get; set; }
public string County { get; set; }
public string Postcode { get; set; }
}

然后你定义了一个AddressValidator:

public class AddressValidator : AbstractValidator<Address> {
public AddressValidator() {
RuleFor(address => address.Postcode).NotNull();
//etc
}
}

如果你要对Customer类进行验证,那么你可以在CustomerVaidator上面复用AddressValidator这个验证规则:

public class CustomerValidator : AbstractValidator<Customer> {
public CustomerValidator() {
RuleFor(customer => customer.Name).NotNull();
RuleFor(customer => customer.Address).SetValidator(new AddressValidator());
}
}

通过调用SetValidator这个方法来解决的。

所以如果你通过调用CustomerValidator对象的Validate方法时,会在两个验证类中执行验证的代码。

规则集允许你将验证规则分组在一起,这些规则可以作为一个组一起执行,而忽略其他规则:

打个比方,想像下在Person对象上的三个属性(Id,Surname,Forename),我们可以将Surname and Forename这两个的验证规则放在一个叫做Names的组中:

 public class PersonValidator : AbstractValidator<Person> {
public PersonValidator() {
RuleSet("Names", () => {
RuleFor(x => x.Surname).NotNull();
RuleFor(x => x.Forename).NotNull();
}); RuleFor(x => x.Id).NotEqual();
}
}

现在,针对Surname和Forename这两个属性的验证规则被放在一个叫做“Names”的验证集合中,当我们调用Validate这个扩展方法(重载后的)时,我们可以为这个扩展方法的ruleSet命名参数传递RuleSet的名字,然后我们验证的时候只验证这个RuleSet:

var validator = new PersonValidator();
var person = new Person();
var result = validator.Validate(person, ruleSet: "Names");//一个重载的Validate方法。

这允许你把一个复杂的验证定义划分为许多细小的隔离的验证规则,如果你调用Validate方法时没有传递一个ruleSet,那么验证只会执行那些不在RuleSet中的规则。你可以通过逗号分隔来指定多个ruleset:

validator.Validate(person, ruleSet: "Names,MyRuleSet,SomeOtherRuleSet")

你也可以通过传递一个“default”字面量来指定那些没有在任何ruleset的规则:

validator.Validate(person, ruleSet: "default,MyRuleSet")

上面的方法会执行MyRuleSet中的规则和那些没有在任何RuleSet中的规则。

你也可以通过 “*”这个字面量来指定所有的规则:

validator.Validate(person, ruleSet: "*")

引入/包含规则(INCLUDE)

你可以包含来自其他验证器的规则,前提是它们验证相同的类型,这意味着你可以将同一个类型中的验证规则划分成几个validator:

public class PersonAgeValidator : AbstractValidator<Person>  {
public PersonAgeValidator() {
RuleFor(x => x.DateOfBirth).Must(BeOver18);
} protected bool BeOver18(DateTime date) {
//...
}
} public class PersonNameValidator : AbstractValidator<Person> {
public PersonNameValidator() {
RuleFor(x => x.Surname).NotNull().Length(, );
RuleFor(x => x.Forename).NotNull().Length(, );
}
}

因为上面这两个验证规则的目标都是Person,所以你可以在一个验证规则中包含他们:

public class PersonValidator : AbstractValidator<Person> {
public PersonValidator() {
Include(new PersonAgeValidator());
Include(new PersonNameValidator());
}
}

你只能包含相同目标的验证规则。

配置

重写消息

你可以通过WithMessage 方法来重写验证错误的消息:

RuleFor(customer => customer.Surname).NotNull().WithMessage("Please ensure that you have entered your Surname");

在这个过程中你可以使用占位符来标志一些重要的信息,比如使用“{PropertyName}"可以显式你当前验证的哪个属性的名字:

RuleFor(customer => customer.Surname).NotNull().WithMessage("Please ensure you have entered your {PropertyName}");

在这个例子中,Surname会替代{PropertyName}这个占位符。

配置错误消息参数(占位符)

就像上面的例子中看到的一样,你可以在WithMessage方法中配置一些特殊的占位符,这些占位符会被一些特殊的值取代。每一个内置的验证规则(validator)都有一些占位符列表:

针对所有的验证规则:

  • ‘{PropertyName}’:被验证的属性的名字
  • ‘{PropertyValue}’:被验证的属性的值,包括那些断言验证(predicate validator)比如Must,email和regex验证。

针对比较验证规则(Equal, NotEqual, GreaterThan, GreaterThanOrEqual, etc.):

  • {ComparisonValue}:被用来和属性比较的值。

针对长度验证规则:

  • {MinLength} = 最小长度
  • {MaxLength} = 最大长度
  • {TotalLength} = 输入的字符数量

要查看完整的占位符列表请查看这个列表。每一个内置的规则器都有它自己的占位符列表。

实际上通过WithMessage 的另一个重载的方法,向这个方法传入一个lambda表达式就可以自定义你自己的验证消息。

//Using static values in a custom message:
RuleFor(customer => x.Surname).NotNull().WithMessage(customer => string.Format("This message references some constant values: {0} {1}", "hello", ));
//Result would be "This message references some constant values: hello 5" //Referencing other property values:
RuleFor(customer => customer.Surname)
.NotNull()
.WithMessage(customer => $"This message references some other properties: Forename: {customer.Forename} Discount: {customer.Discount}");
//Result would be: "This message references some other properties: Forename: Jeremy Discount: 100"

重写属性的名称

默认的验证规则中包含了属性的默认名称:

RuleFor(customer => customer.Surname).NotNull();

你可以重写属性名称:

RuleFor(customer => customer.Surname).NotNull().WithName("Last name");

这会导致{PropertyName}的值有所改变。

需要注意的是这个改变只会在错误消息中展示,在ValidationResult中,这个值仍然是属性原来的名称Surname而不是Last name。如果你想要完全改变属性名,你可以使用OverridePropertyName方法。这个方法将ValidationResult中Erros中的属性名也改了(没验证)。

这也是WithName的一个重载方法,大部分时候你使用这个方法意味着你在使用WithName。

条件

When和Unless验证器可以指定规则验证的条件。比如,CustomerDiscount 属性上的验证只有在IsPreferredCustomer为true时执行。

RuleFor(customer => customer.CustomerDiscount).GreaterThan().When(customer => customer.IsPreferredCustomer);

Unless正好相反。

如果你想在多个验证上面应用相同的条件,那你也可以使用When,不过用法是这样的:

When(customer => customer.IsPreferred, () => {
RuleFor(customer => customer.CustomerDiscount).GreaterThan();
RuleFor(customer => customer.CreditCardNumber).NotNull();
});

When这个时候是在顶层执行而不是链式的调用了。

设置链式调用的规则

当你链式的对一个属性应用多个规则时,你可以追加一个链式调用的模式/规则,这个规则用来说明在链式调用中的其中一个验证失败时应该如何处理。如下,你有两个验证器:

RuleFor(x => x.Surname).NotNull().NotEqual("foo");

上面这个表示有两个验证器作用于Surname属性上,第一个验证器验证是否为空,第二个验证是否等于foo。当第一个验证失败时,第二个仍然会执行,尽管注定也会失败。可以使用Cascade来改变这一默认行为:

RuleFor(x => x.Surname).Cascade(CascadeMode.StopOnFirstFailure).NotNull().NotEqual("foo");

现在,当第一个验证器失败时,第二个就不会执行了。这对于后一个验证器依赖于前一个验证器成功的情况下是非常有用的。CascadeMode有两种模式:①Continue(默认):总会全部执行。②StopOnFirstFailure:当第一个验证器失败时,第二个就不会执行了。

可以在验证规则中设置这个:

public class PersonValidator : AbstractValidator<Person> {
public PersonValidator() {
// First set the cascade mode
CascadeMode = CascadeMode.StopOnFirstFailure; RuleFor(...)
RuleFor(...)
}
}

依赖的规则

默认情况下FluentValidation 里面的所有规则都是互不影响的。这对于异步验证来说是有效的,并且针对异步验证来说这也是有意而为之。然而,在一些情况下,你需要确定在一些验证完成的情况下另一些验证才能开始执行,这通过DependentRules来完成。

将DependentRules添加在一个规则后面,DependentRules指定的这个规则依赖于前者。只有当前者执行完成后它才会执行:

RuleFor(x => x.Surname).NotNull().DependentRules(() => {
RuleFor(x => x.Forename).NotNull();
});

现在只有当Surname 的验证通过了才会执行Forname的验证。

通常情况下这个验证器不推荐使用,可以用When来替代它。

回调

验证失败后你可以用OnFailure或者OnAnyFailure来进行回调。

RuleFor(x => x.Surname).NotNull().Must(surname => surname != null && surname.Length <= )
.OnAnyFailure(x => {
Console.WriteLine("At least one validator in this rule failed");
})
RuleFor(x => x.Surname).NotNull().OnFailure(x => Console.WriteLine("Nonull failed"))
.Must(surname => surname != null && surname.Length <= )
.OnAnyFailure(x => Console.WriteLine("Must failed"));
 

【翻译】FluentValidation验证组件的使用的更多相关文章

  1. .NET平台开源项目速览(10)FluentValidation验证组件深入使用(二)

    在上一篇文章:.NET平台开源项目速览(6)FluentValidation验证组件介绍与入门(一) 中,给大家初步介绍了一下FluentValidation验证组件的使用情况.文章从构建间的验证器开 ...

  2. .NET平台开源项目速览(6)FluentValidation验证组件介绍与入门(一)

    在文章:这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧!(第二辑)中,给大家初步介绍了一下FluentValidation验证组件.那里只是概述了一下,并没有对其使用和强大功能做深入研究 ...

  3. NET平台开源项目速览(6)FluentValidation验证组件介绍与入门(转载)

    原文地址:http://www.cnblogs.com/asxinyu/p/dotnet_Opensource_project_FluentValidation_1.html 阅读目录 1.基本介绍 ...

  4. 模型验证组件 FluentValidation

    FluentValidation 是 .NET 下的模型验证组件,和 ASP.NET MVC 基于Attribute 声明式验证的不同处,其利用表达式语法链式编程,使得验证组件与实体分开.正如 Flu ...

  5. 模型验证组件——FluentValidation

    之前在博客园有幸从网友那里得知一个C#的模型验证组件(哈 不知道这样表述正确不),组件的功能比较简单,主要是实现了对Model的验证,例如验证用户名是否为空,密码长度是不是多余6个字符,当然还有其他更 ...

  6. .NET Core中的验证组件FluentValidation的实战分享

    今天有人问我能不能出一篇FluentValidation的教程,刚好今天在实现我们的.NET Core实战项目之CMS的修改密码部分的功能中有用到FluentValidation,所以就以修改用户密码 ...

  7. asp.net mvc 模型验证组件——FluentValidation

    asp.net mvc 模型验证组件——FluentValidation 示例 using FluentValidation; public class CustomerValidator: Abst ...

  8. 验证组件——FluentValidation

          FluentValidation FluentValidation是与ASP.NET DataAnnotataion Attribute验证实体不同的数据验证组件,提供了将实体与验证分离开 ...

  9. ASP.NET MVC中使用FluentValidation验证实体

    1.FluentValidation介绍 FluentValidation是与ASP.NET DataAnnotataion Attribute验证实体不同的数据验证组件,提供了将实体与验证分离开来的 ...

随机推荐

  1. 【Linux基础】VI命令模式下大小写转换

    [开始位置] ---- 可以指定开始的位置,默认是光标的当前位置 gu ---- 把选择范围全部小写 gU ---- 把选择范围全部大写 [结束位置] ---- 可以跟着类似的w,6G,gg等定位到错 ...

  2. 【汤鸿鑫 3D太极】5年目标规划(基本功、套路、实战搏击)

    [5年目标]在基本功的基础上,每年完成一个套路或实战搏击的学习研究. [中小学2年]三段九节 + 2套路. [高中的3年]太极十三势 + 1套路 + 推手 + 搏击. 1.中小学阶段-可自学 (1)基 ...

  3. 用惯图形界面的SVNer,如何突破Git----简单教程

    1.使用Git,首先安装好Git,它会赠送一个Git Bash给你 2.接下来,踩第一个坑----SSH连接,我们知道用Git关联本地仓库可以用SSH和HTTP两种方式,为什么不用HTTP,因为 不! ...

  4. 并发的HashMap为什么会引起死循环?

    转载:http://blog.csdn.net/zhuqiuhui/article/details/51849692 今天研读Java并发容器和框架时,看到为什么要使用ConcurrentHashMa ...

  5. linux下c程序 daemon、fork与创建pthread的顺序问题

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/shuyun123456789/article/details/34418875 近期发如今写linu ...

  6. 【转】ffmpeg 常用命令

    1. 视频转换 比如一个avi文件,想转为mp4,或者一个mp4想转为ts. ffmpeg -i input.avi output.mp4 ffmpeg -i input.mp4 output.ts ...

  7. 【ES6】=>含义

    =>是es6语法中的arrow function (x) => x + 6 相当于 function(x){ return x + 6; }; var ids = this.sels.ma ...

  8. linux运行级别和开机流程

    linux有七个运行级别 运行级别0:系统停机状态,系统默认运行级别不能设为0,否则不能正常启动 运行级别1:单用户工作状态,root权限,用于系统维护,禁止远程登陆 运行级别2:多用户状态(没有NF ...

  9. tomcat配置通过域名直接访问项目首页步骤

    假设www.ctool.top.ip:192.168.122.135 step 1 申请一个域名并做好DNS解析,或者在hosts文件做域名指向 #vim /etc/hosts www.ctool.t ...

  10. Flask 框架 重定向,捕获异常,钩子方法及使用jsonify在网页返回json数据

    Flask 框架中常用到重定向方法来实现路由的跳转 ,路由跳转又分为站内跳转和站外跳转 常用的站内跳转方法为url_for  而常用的站外跳转为redirect 在这里提示一下: 在使用两种方法是须调 ...