FluentValidation 地址:https://github.com/FluentValidation/FluentValidation
模型验证库,它使用 lambda 表达式来构建强类型验证规则。
具体使用方法,可以参考官方文档:https://docs.fluentvalidation.net/en/latest/index.html。
FlexibleCore 框架自动注入验证类,自动实现验证和返回验证信息,只需要按照规则编写验证类与验证规则,不需要自己定义 ValidationResult 接收验证结果,API 已经定义好统一的响应格式,并自动返回验证失败的错误信息。
创建一个类:
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; } }
创建验证器类 CustomerValidator,一般为了可读性,验证器类的命名为:需要验证的类的类名 + Validator,并继承 AbstractValidator<验证类的类名> 来定义验证规则。
using FluentValidation; public class CustomerValidator : AbstractValidator<Customer> { public CustomerValidator() { RuleFor(customer => customer.Surname).NotNull(); } }
自定义验证失败后的提示:
using FluentValidation; public class CustomerValidator : AbstractValidator<Customer> { public CustomerValidator() { RuleFor(customer => customer.Surname).NotNull().WithMessage("Surname 不能为空!"); } }
也可以将同一属性的多个验证器连接在一起:
RuleFor(customer => customer.Surname).NotNull().NotEqual("foo");
RuleFor(customer => customer.Surname).NotNull();
RuleFor(customer => customer.Surname).Null();
RuleFor(customer => customer.Surname).NotEmpty();
RuleFor(customer => customer.Surname).Empty();
RuleFor(customer => customer.Surname).Equal("Foo"); RuleFor(customer => customer.Surname).Equal(customer => customer.Forename);
RuleFor(customer => customer.Surname).NotEqual("Foo"); RuleFor(customer => customer.Surname).NotEqual(customer => customer.Forename);
RuleFor(customer => customer.Surname).Length(10, 250);
RuleFor(customer => customer.Surname).MaximumLength(250);
RuleFor(customer => customer.Surname).MinimumLength(10);
RuleFor(customer => customer.CreditLimit).GreaterThan(0); RuleFor(customer => customer.CreditLimit).GreaterThan(customer => customer.MinimumCreditLimit);
RuleFor(customer => customer.CreditLimit).GreaterThanOrEqualTo(1); RuleFor(customer => customer.CreditLimit).GreaterThanOrEqualTo(customer => customer.MinimumCreditLimit);
RuleFor(customer => customer.CreditLimit).LessThan(100); RuleFor(customer => customer.CreditLimit).LessThan(customer => customer.MaxCreditLimit);
RuleFor(customer => customer.CreditLimit).LessThanOrEqualTo(100); RuleFor(customer => customer.CreditLimit).LessThanOrEqualTo(customer => customer.MaxCreditLimit);
RuleFor(customer => customer.Email).EmailAddress();
RuleFor(customer => customer.Phone).Phone();
RuleFor(customer => customer.ZipCode).ZipCode();
RuleFor(customer => customer.UserName).UserName();
RuleFor(customer => customer.PassWord).PassWord();
RuleFor(customer => customer.PassWord).PassWord();
RuleFor(customer => customer.Email).Tel();
RuleFor(customer => customer.Company).Company();
RuleFor(customer => customer.Chinese).Chinese();
RuleFor(customer => customer.English).English();
RuleFor(customer => customer.Number).Number();
RuleFor(customer => customer.ChineseIdentification).ChineseIdentification();
RuleFor(customer => customer.Email).Matches("正则表达式");
public enum ErrorLevel { Error = 1, Warning = 2, Notice = 3 } RuleFor(x => x.ErrorLevel).IsInEnum();
//区分大小写 RuleFor(x => x.ErrorLevelName).IsEnumName(typeof(ErrorLevel)); //不区分大小写 RuleFor(x => x.ErrorLevelName).IsEnumName(typeof(ErrorLevel), caseSensitive: false);
RuleFor(x => x.Id).ExclusiveBetween(1,10);
RuleFor(x => x.Id).InclusiveBetween(1,10);
RuleFor(x => x.Amount).ScalePrecision(2, 4);
数据库级别验证,需要在数据库访问层(例如 Repository 层),不然拿不到调用数据库的方法,建议配合 AutoMapper 使用。
注意 AbstractValidator 的构造方法不可使用动态获取的语句,如果需要在运行时获取值,需在 Must 方法里。
public class InsertUserValidator : AbstractValidator<User> { public InsertUserValidator(Repository _repository) { //单个验证 RuleFor(x => x.Phone).Must(Phone => _repository.Db.Queryable<User>().Where(x => x.Phone == Phone).Any() == false).WithMessage("手机号不可重复"); //如果想要很多字段一起的验证 RuleFor(user => user.Phone).Must((user, phone, context) => { User u = _repository.Db.Queryable<User>().Where(x => x.Phone == user.Phone || x.Email == user.Email).First(); if (!u.IsNullOrEmpty()) { List<string> message = new List<string>(); if (u.Phone == user.Phone) message.Add("手机号不可重复"); if (u.Email == user.Email) message.Add("邮箱不可重复"); context.MessageFormatter.AppendArgument("MyMessage", message.ListStringToString()); return false; } else { return true; } }).WithMessage("{MyMessage}"); } }
实现自定义验证程序的最简单方法是使用方法 Must 方法。
public class Person { public IList<Person> Pets {get;set;} = new List<Person>(); } // 为了确保列表中小于等于10个元素, 我们可以这样做 Must里面False 抛出异常 RuleFor(x => x.Pets).Must(list => list.Count <= 10).WithMessage("The list must contain fewer than 10 items"); // 为了使这种逻辑可重用, 我们可以将其封装为扩展方法 public static IRuleBuilderOptions<T, IList<TElement>> ListMustContainFewerThan<T, TElement>(this IRuleBuilder<T, IList<TElement>> ruleBuilder, int num) { return ruleBuilder.Must(list => list.Count < num).WithMessage("The list contains too many items"); } // 在这里,我们通过为 IRuleBuilder 创建扩展方法实现可重用逻辑,使用方法很简单 RuleFor(x => x.Pets).ListMustContainFewerThan(10);
When 和 Unless 方法可用于指定条件控制在规则应该执行。例如,仅当 IsPreferredCustomer 为 true 时,才会执行 CustomerDiscount 属性上的此规则:
RuleFor(customer => customer.CustomerDiscount).GreaterThan(0).When(customer => customer.IsPreferredCustomer); //多个规则 When(customer => customer.IsPreferred, () => { RuleFor(customer => customer.CustomerDiscount).GreaterThan(0); RuleFor(customer => customer.CreditCardNumber).NotNull(); }); //带有Else When(customer => customer.IsPreferred, () => { RuleFor(customer => customer.CustomerDiscount).GreaterThan(0); RuleFor(customer => customer.CreditCardNumber).NotNull(); }).Otherwise(() => { RuleFor(customer => customer.CustomerDiscount).Equal(0); });
首先检查 Surname 属性是否不为 null,然后将检查其是否不等于字符串“ foo”。如果第一个验证器(NotNull)失败,则 NotEqual 仍将调用。可以通过指定级联模式来更改此设置 StopOnFirstFailure:
RuleFor(x => x.Surname).Cascade(CascadeMode.StopOnFirstFailure).NotNull().NotEqual("foo");
确保某些规则仅在另一个规则完成后才执行。可以 DependentRules 用来执行此操作。
RuleFor(x => x.Surname).NotNull().DependentRules(() => { RuleFor(x => x.Forename).NotNull(); });