互联网技术 · 2024年2月20日

.NET的FluentValidation数据验证实现基础

这篇文章主要介绍了基于.NET的FluentValidation数据验证实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

学习地址:官网文档,更多更详细的内容可以看官方文档。

FluentValidation 是一个基于 .NET 开发的验证框架,开源免费,而且优雅,支持链式操作,易于理解,功能完善,还是可与 MVC5、WebApi2 和 ASP.NET CORE 深度集成,组件内提供十几种常用验证器,可扩展性好,支持自定义验证器,支持本地化多语言。

要使用验证框架, 需要在项目中添加对 FluentValidation.dll 的引用,支持 netstandard2.0 库和 .NET4.5 平台,支持.NET Core 平台,最简单的方法是使用 NuGet 包管理器引用组件。我这里安装的是9.2.0版本。

Install-Package FluentValidation

若要在 ASP.NET Core 中使用 FluentValidation 扩展,可引用以下包:

Install-Package FluentValidation.AspNetCore

若要在 ASP.NET MVC 5 或 WebApi 2 项目集成, 可以使用分别使用 FluentValidation.Mvc5 和 FluentValidation.WebApi 程序包。

Install-Package FluentValidation.Mvc5
Install-Package FluentValidation.WebApi

创建第一个验证程序

若要为特定对象定义一组验证规则, 您需要创建一个从 AbstractValidator 继承的类, 其中泛型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 NetCoreBasicLearning.Entity;

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

若要执行验证程序,我们通过定义好的 CustomerValidator 验证器传入实体类 Customer 即可。

该验证方法返回一个 ValidationResult 对象,表示验证结果,ValidationResult 包含两个属性:IsValid属性是布尔值, 它表示验证是否成功,Errors属性包含相关验证失败的详细信息。

public IActionResult Index(string id)
{
  Customer customer = new Customer();
  CustomerValidator validationRules = new CustomerValidator();
  ValidationResult validationResult = validationRules.Validate(customer);

  if (!validationResult.IsValid)
  {
    var str = new StringBuilder();
    foreach(var t in validationResult.Errors)
    {
      str.AppendLine($”属性:{t.PropertyName},错误信息:{t.ErrorMessage}”);
    }
    return Content(str.ToString());
  }
  return View();
}

链接规则写法

您可以将对象的同一属性用多个验证程序链在一起,以下代码将验证 Surname 属性不为 Null 的同时且不等于foo字符串。

using FluentValidation;
using NetCoreBasicLearning.Entity;

namespace NetCoreBasicLearning
{
  public class CustomerValidator : AbstractValidator
  {
    public CustomerValidator()
    {
      RuleFor(customer => customer.Surname).NotNull().NotEqual(“foo”);
    }
  }
}

引发异常

如果验证失败, 不想返回 ValidationResult 对象,而是想直接抛出异常,可通过调用验证器的 ValidateAndThrow 进行验证。

如果验证失败,将引发一个 ValidationException 类型的异常,这个异常可以被上层程序捕获,并从异常中找到详细错误信息。

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

集合

当针对一个集合进行验证时,只需要定义集合项类型的规则即可,以下规则将对集合中的每个元素运行 NotNull 检查。

public class Customer
{
  public List OtherAddress { get; set; }
}

public class CustomerValidator : AbstractValidator
{
  public CustomerValidator()
  {
    RuleForEach(customer => customer.OtherAddress).NotEmpty();
  }
}

如果要对list进行进行非空判断可以如下:

public class CustomerValidator : AbstractValidator
{
  public CustomerValidator()
  {
    RuleFor(customer => customer.OtherAddress).NotEmpty();
  }
}

复杂属性

验证程序可以用于复杂属性,假设您有两个类:客户和地址

public class Customer
{
  public Adress Address { get; set; }
}
public class Adress
{
  public string City { get; set; }
  public string Province { get; set; }
}

然后定义一个基于地址的 AddressValidator 验证器件:

public class AdressValidator : AbstractValidator
{
  public AdressValidator()
  {
    RuleFor(t => t.City).NotEmpty();
    RuleFor(t => t.Province).NotEmpty();
  }
}   

然后定义一个基于客户的 CustomerValidator 验证器件,对地址验证时使用地址验证器。

public class CustomerValidator : AbstractValidator
{
  public CustomerValidator()
  {
    RuleFor(t => t.Address).SetValidator(new AdressValidator());
  }

如果不想要创建另一个验证器可以直接内联定义子规则:

RuleFor(t => t.Address.City).NotEmpty();

另外,还可以在集合属性上使用验证程序,假设客户对象包含地址集合属性:

public class Customer
{
  public List Address { get; set; }
 }

此验证程序可在 CustomerValidator 中通过 SetCollectionValidator(在8.0版本以前) 方法使用,在8.0版本以及之后被弃用需要使用RuleForEach来验证:

public class CustomerValidator : AbstractValidator
{
   public CustomerValidator()
  {
    RuleForEach(t => t.Address).SetValidator(new AdressValidator());
    //RuleFor(x => x.Address).SetCollectionValidator(new AdressValidator());在8.0版本及以后弃用
  }
}

在编写验证规则时,可以通过 Where 关键字排除或者筛选不需要验证的对象。

public class CustomerValidator : AbstractValidator
{
  public CustomerValidator()
  {
    RuleForEach(t => t.Address).Where(t => !string.IsNullOrEmpty(t.City)).SetValidator(new AdressValidator());
    //RuleFor(x => x.Address).SetCollectionValidator(new AdressValidator()).Where(t => !string.IsNullOrEmpty(t.City));在8.0版本及以后弃用
  }

从FluentValidation 8.5开始,您还可以使用以下ChildRules方法在线定义子集合元素的规则,从而不用再定义另一个验证器:

RuleForEach(t => t.Address).ChildRules(adderss => {
  adderss.RuleFor(t => t.City).NotEmpty();
  adderss.RuleFor(t => t.Province).NotEmpty();
}).NotEmpty();

支持规则集

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

我们可以把“姓”和“名”统一加在一个姓名规则集中。

public class CustomerValidator : AbstractValidator
{
  public CustomerValidator()
  {
    RuleSet(“Name”, () =>
    {
      RuleFor(t => t.Surname).NotEmpty();
      RuleFor(t => t.Forename).NotEmpty();
    });
  }
}

规则集通过一般的 Validate 方法是不会执行验证的,需要用如下方法进行单独的验证,这将复杂的验证器定义分解为较小的部分进行验证,IncludeRuleSets 中可以传入多个规则集名称来执行多个规则集的验证:

Customer customer = new Customer();
CustomerValidator validationRules = new CustomerValidator();
ValidationResult validationResult = validationRules.Validate(customer, options => options.IncludeRuleSets(“Name”));

规则集通过一般的 Validate 方法是不会执行验证的,需要用如下方法进行单独的验证,这将复杂的验证器定义分解为较小的部分进行验证,IncludeRuleSets 中可以传入多个规则集名称来执行多个规则集的验证:

还可以通过 IncludeRulesNotInRuleSet 方法或使用特殊名称“默认”(不区分大小写)来执行验证所有不属于规则集的规则:

ValidationResult validationResult = validationRules.Validate(customer, options =>{ options.IncludeRulesNotInRuleSet(); options.IncludeRuleSets(“Name”); } );

可以通过调用强制执行所有规则,而不管它们是否在规则集中 IncludeAllRuleSets(这等效于using IncludeRuleSets(“*”))。

同个类型的多个验证器

一个验证器可以包含多个其他的验证器,只要这些验证器都是验证统一类型的即可。这样就可以拆分验证器,通过不同的需求组合在一起:

public class CustomerValidator : AbstractValidator
{
  public CustomerValidator()
  {
    Include(new CustomerSurnameValidator());
    Include(new CustomerForenameValidator());
  }
}
public class CustomerSurnameValidator : AbstractValidator
{
  public CustomerSurnameValidator()
  {
    RuleFor(t => t.Surname).NotEmpty();
  }
}
public class CustomerForenameValidator : AbstractValidator
{
  public CustomerForenameValidator()
  {
    RuleFor(t => t.Forename).NotEmpty();
  }
}

继承验证

从FluentValidation 9.2开始,如果您的对象属性作为其他类的基类或接口,则可以为各个子类/实现器设置特定的子验证器,来验证这个属性。我们的类设置如下:

public class Store
{
  public People Peoples { get; set; }
}
public class Customer : People
{
  public string Address { get; set; }
}
public class People
{
  public string Name { get; set; }
}

验证器如下:

public class StoreValidator : AbstractValidator
{
  public StoreValidator()
  {
    RuleFor(t => t.Peoples).NotNull().SetInheritanceValidator(t =>
    {
       t.Add(new CustomerValidator());
    });
  }
}
 public class CustomerValidator : AbstractValidator
{
  public CustomerValidator()
  {
    RuleFor(t => t.Address).NotEmpty();
  }
}

覆盖消息

通过在验证程序上调用 WithMessage 方法, 可以覆盖验证程序的默认验证错误消息。错误提示中,可以通过 {PropertyName} 占位符替换属性名。除了 {PropertyName} 占位符,框架还内置了:{PropertyValue}、{ComparisonValue}、{MinLength}、{MaxLength}和{TotalLength} 占位符,关于更多内置占位符,可以参阅官方文档。

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

验证程序支持通过 WithName 方法来指定属性别名,以下代码输出姓名不能为空。请注意,这仅替换错误消息中属性的名称。当您检查上的Errors集合时ValidationResult,此错误仍将与一个名为的属性相关联Surname。如果要完全重命名该属性,则可以使用OverridePropertyName方法。

RuleFor(customer => customer.Surname).NotNull().WithName(“姓名”);

条件

When 和 Unless方法可用于执行满足指定条件情况下的规则,例如只当Surname属性不为空的时候,才执行前面的Name属性的非空验证(Unless和When是相反的所以这边只讲When就行啦!):

RuleFor(t => t.Name).NotEmpty().When(t => !string.IsNullOrEmpty(t.Surname));

如果需要指定多个规则在相同的条件下才执行验证,可以直接用顶级的When方法:

public class CustomerValidator : AbstractValidator
{
  public CustomerValidator()
  {
    When(t => !string.IsNullOrEmpty(t.Surname), () => { RuleFor(t => t.Name).NotEmpty();//其他验证规则 });
  }
}

通过在

OpenMagic API

Need more than content? Move into the product flow.

If you are here for model access, pricing, developer docs, or the future API console, the dedicated product path now lives on api.openmagic.ai.

登录免费注册