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的值。

[Route("api/[controller]")]
    [ApiController]
    public class DemoController : ControllerBase
    {
        [HttpPost]
        public IActionResult Post(Customer customer)
        {
            return NoContent();
        }
    }

版权声明:本文为ZaraNet原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/ZaraNet/p/10481276.html