FluentValidation 是一个基于 .NET 开发的验证框架,开源免费,而且优雅,支持链式操作,易于理解,功能完善,还是可与 MVC5、WebApi 和 ASP.NET Core深度集成,组件内提供十几种常用验证器,可扩展性好,支持自定义验证器,支持本地化多语言。
要使用验证框架,需要在项目中添加对 FluentValidation.dll 的引用,支持 netstandard2.0 库和 .NET4.5 平台,支持.NET Core 平台,最简单的方法是使用 NuGet 包管理器引用组件。
Install-Package FluentValidation
若要在 ASP.NET Core 中使用 FluentValidation 扩展,可引用以下包:
Install-Package FluentValidation.AspNetCore
若要在 ASP.NET MVC 5 或 WebApi 项目集成, 可以使用分别使用 FluentValidation.Mvc5 和 FluentValidation.WebApi 程序包。
Install-Package FluentValidation.Mvc5 Install-Package FluentValidation.WebApi
若要为特定对象定义一组验证规则, 您需要创建一个从 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 泛型类的验证器,然后在构造函数中使用 LINQ 表达式编写 RuleFor 验证规则。
using FluentValidation; using FluentValidation.Model; public class CustomerValidator : AbstractValidator<Customer> { /// <summary> /// 对客户类进行扩展并添加限制规则 /// </summary> public CustomerValidator() { RuleFor(customer => customer.Surname).NotNull(); } }
验证模型验证是否通过,需要CustomerValidator实例里的验证方法,参数是你的自定义模型,其中有两个属性,一个是错误还有一个是布尔类型是否验证通过。
class Program { static void Main(string[] args) { Customer customer = new Customer(); //false //Customer customer = new Customer() { Id=1, Address="1", Discount =250, Forename = "rerer", Surname = "zara"}; true CustomerValidator validationRules = new CustomerValidator(); ValidationResult results = validationRules.Validate(customer); System.Console.WriteLine(results.IsValid); if (!results.IsValid) { foreach (var failure in results.Errors) { System.Console.WriteLine ("Property " + failure.PropertyName + " Error was: " + failure.ErrorMessage); } } } }
上面我们可以判定是否通过,它不会发生异常,如果想故意发生异常,类似下方的:
validationRules.ValidateAndThrow(customer);
在定义验证规则中,FluentValidation中还有链接规则写法,类似下方的:
public CustomerValidator() { RuleFor(customer => customer.Surname).NotNull().NotEmpty().NotEqual("zara") .Length(1,500).MaximumLength(20).MinimumLength(100).EmailAddress() .Matches("*&^&%(").GreaterThan("giao"); }
我们现在只能对单对象进行验证,那如何对集合进行验证呢,你可能会想到迭代...,已经提供好了(那绝逼还是迭代,手动滑稽),类似下方的:
public class Person { public List<string> addressLines { get; set; } = new List<string>(); }
public class PersonValidator :AbstractValidator<Person> { public PersonValidator() { RuleForEach(x => x.addressLines).NotNull(); } }
如果面向与OOP,一定会有模型和模型之间的字段关系,那么我们可以这样去实现,类似下方的:
public class classly { public IList<student> students { get; set; } } public class student { public int classlyid { get; set; } public string studentName { get; set; } }
定义了一个 StudentValidator验证器件:
public class studentValidator : AbstractValidator<student> { public studentValidator() { RuleFor(x => x.studentName).NotNull(); RuleFor(x => x.classlyid).GreaterThan(0); } }
此验证程序可在 CustomerValidator 中通过 SetCollectionValidator 方法使用:
public class classlyValidator : AbstractValidator<classly> { public classlyValidator() { RuleFor(x => x.students).SetCollectionValidator(new studentValidator()); } }
规则集允许您将验证规则组合在一起, 作为一个组一起执行, 同时忽略其他规则,假如:Person 类有3个属性分别是 Id、Surname 和 Forename,我们将 Id 单独验证, Surname 和 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(0); } }
然后,我们可以使用验证器提供的扩展方法,针对指定的规则集执行验证,以下代码将不验证 Id 属性。
var validator = new PersonValidator(); var person = new Person(); var result = validator.Validate(person, ruleSet: "Names");
执行多个规则集验证,可以使用逗号分隔的字符串列表。
validator.Validate(person, ruleSet: "Names,MyRuleSet,SomeOtherRuleSet")
通过*号匹配所有规则, 可以强制执行所有规则, 不管它们是否在规则集中:
validator.Validate(person, ruleSet: "*")
如果您想灵活控制可重用的验证器, 则可以使用 Must 方法编写自定义规则,此方法允许您手动创建与验证错误关联的实例。
public classLyValidator() { RuleFor(x => x.students).Custom((list, context) => { if (list.Count > 10) { context.AddFailure("The list must contain 10 items or fewer"); } }); }
关于上下文添加错误,也可以定义多个信息,这需要引入 using FluentValidation.Results;
RuleFor(x => x.students).Custom((list, context) => { if (list.Count > 10) { context.AddFailure(new ValidationFailure("SomeOtherProperty","This is first")); context.AddFailure(new ValidationFailure("SomeOtherProperty", "This is second")); } });
在某些情况下, 针对某些属性的验证逻辑非常复杂, 我们希望将基于属性的自定义逻辑移动到单独的类中,可通过重写 PropertyValidator 类来完成。
public class SomeCountValidator<T> : PropertyValidator { private int _max; public SomeCountValidator(int max) : base("{PropertyName} must contain fewer than {MaxElements} items.") { _max = max; } protected override bool IsValid(PropertyValidatorContext context) { var list = context.PropertyValue as IList<T>; if (list != null && list.Count >= _max) { context.MessageFormatter.AppendArgument("MaxElements", _max); return false; } return true; } }
继承 PropertyValidator 时, 必须重写 IsValid 方法,此方法接受一个对象, 并返回一个布尔值, 指示验证是否成功。
在上下文中可以获取以下属性,其用处在以下代码中:
public class PropertyValidatorContext : IValidationContext { public PropertyValidatorContext(ValidationContext parentContext, PropertyRule rule, string propertyName); public PropertyValidatorContext(ValidationContext parentContext, PropertyRule rule, string propertyName, object propertyValue); public ValidationContext ParentContext { get; } public PropertyRule Rule { get; } public string PropertyName { get; } //对象名称 public string DisplayName { get; } public object Instance { get; } //正在验证的对象 public MessageFormatter MessageFormatter { get; } public object PropertyValue { get; }//正在验证的属性值 }
若要使用自定义的属性验证程序, 可以在定义验证规则时调用:
public class classLyValidator : AbstractValidator<classly> { public classLyValidator() { RuleFor(Person => Person.students).SetValidator(new SomeCountValidator<Person>(10)); } }
WebApi + FluentValidation小实战:
在ASP.NET Web Api中请安装FluentValidation.AspNetCore版本
创建一个需要验证的Model
public class Product { public string name { get; set; } public string des { get; set; } public string place { get; set; } }
配置FluentValidation,需要继承AbstractValidator类,并添加对应的验证规则
public class ProductValidator: AbstractValidator<Product> { public ProductValidator() { RuleFor(x => x.name) .NotEmpty() .WithMessage("name is not empty."); } }
现在我们应当注入验证服务
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // 添加验证器 services.AddSingleton<IValidator<Product>, ProductValidator>(); //mvc + validating services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1) .AddFluentValidation(); }
但现在还没有结束,因为Web Api中会自己给我们返回错误信息,也就找不到验证我们自定义的错误消息,所以我们要覆盖ModelState管理的默认行为(ApiBehaviorOptions)
public void ConfigureServices(IServiceCollection services) { // 添加验证器 services.AddSingleton<IValidator<Product>, ProductValidator>(); //mvc + validating services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1) .AddFluentValidation();// override modelstate services.Configure<ApiBehaviorOptions>(options => { options.InvalidModelStateResponseFactory = (context) => { var errors = context.ModelState .Values .SelectMany(x => x.Errors .Select(p => p.ErrorMessage)) .ToList(); var result = new { Code = "00009", Message = "Validation errors", Errors = errors }; return new BadRequestObjectResult(result); }; }); }
创建一个控制器,如果不符合规则就会曝出WithMessage的值,这里可能是我的sdk的问题(出现了error是空的情况,如果你哪里没有问题,给我说一下,谢谢。)
[Route("api/[controller]")] [ApiController] public class DemoController : ControllerBase { [HttpPost] public IActionResult Post(Customer customer) { return NoContent(); } }