Skip to content

FluentValidation 模型验证实践 ​

在业务开发中,数据模型的验证通常会充斥着大量的 if (model.Name == null)。FluentValidation 是 .NET 下最优雅的模型验证框架之一,支持链式操作和规则集分离,能有效将“业务数据校验逻辑”从 Controller 或 Service 内部剥离出来。

一、 为什么使用 FluentValidation ​

相比 .NET 自带的 DataAnnotation(在属性上打 [Required] 等标签),FluentValidation 的优势在于:

  1. 解耦:验证规则单独成类,不污染实体模型(Entity / DTO)。
  2. 灵活:可以为同一个模型在不同场景下写不同的验证规则。
  3. 链式调用:支持丰富的条件逻辑(如:当 A 字段等于某值时,B 字段才必填)。

二、 基础使用流程 ​

1. 引入依赖 ​

最简单的方法是通过 NuGet 安装:

bash
Install-Package FluentValidation

如果在 ASP.NET Core 中使用,建议安装扩展包,它能自动与 MVC 的模型绑定集成:

bash
Install-Package FluentValidation.AspNetCore

2. 创建验证器 ​

定义一个继承自 AbstractValidator<T> 的类,在构造函数中编写规则:

csharp
public class Customer
{
    public int Id { get; set; }
    public string Surname { get; set; }
}

public class CustomerValidator : AbstractValidator<Customer>
{
    public CustomerValidator()
    {
        // 规则链:不为空,且不等于特定字符串
        RuleFor(customer => customer.Surname)
            .NotNull().WithMessage("{PropertyName} 不能为空")
            .NotEqual("foo").WithMessage("不能使用 foo");
    }
}

3. 执行验证 ​

可以通过 Validate 方法获取验证结果对象:

csharp
Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();

ValidationResult result = validator.Validate(customer);

if (!result.IsValid)
{
    foreach(var failure in result.Errors)
    {
        Console.WriteLine($"属性 {failure.PropertyName} 错误: {failure.ErrorMessage}");
    }
}

也可以通过 ValidateAndThrow 快速失败(直接抛出 ValidationException):

csharp
validator.ValidateAndThrow(customer);

三、 常见高级场景 ​

1. 集合验证 (RuleForEach) ​

当需要对集合中的每一个元素进行检查时:

csharp
public class Person
{
    public List<string> AddressLines { get; set; } = new List<string>();
}

public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        // 验证集合里的每一个元素都不为 null
        RuleForEach(x => x.AddressLines).NotNull();
    }
}

2. 复杂对象与嵌套验证 (SetValidator) ​

当对象内部嵌套了另一个对象,可以复用另一个验证器:

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

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

如果是对象集合,也可以结合使用:

csharp
RuleFor(x => x.Orders).SetCollectionValidator(new OrderValidator());

3. 规则集 (RuleSet) ​

可以在一个验证器中分组,调用时指定只验证某一组规则:

csharp
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);
    }
}

// 调用时只验证 "Names" 组
var result = validator.Validate(person, ruleSet: "Names");
// 验证所有规则(包含默认和各组)
var resultAll = validator.Validate(person, ruleSet: "*");

四、 参考资料 ​

方舟 · Ark