简介
FluentValidation是一个基于“流式API”(Fluent API)的.NET验证框架,用于在应用层对模型(DTO、ViewModel、Entity等)进行声明式验证。- 核心优势:
- 高可读性:通过链式方法配置验证规则,逻辑清晰;
- 可复用:将验证代码从业务逻辑中分离,易于单元测试;
- 丰富的内置规则:邮箱、长度、正则、多字段联动、集合验证等;
- 可扩展:支持自定义验证器、异步验证、跨属性验证。
- 适用场景:
Web API模型验证- 复杂业务规则验证
- 需要高度可定制验证逻辑的系统
- 多语言验证消息需求
- 需要测试覆盖的验证逻辑
安装与基础配置
NuGet包
1Install-Package FluentValidation 2Install-Package FluentValidation.DependencyInjectionExtensions 3
- 引用命名空间
1using FluentValidation; 2
核心用法
定义 Model 与 Validator
1public class UserDto 2{ 3 public string Username { get; set; } 4 public string Email { get; set; } 5 public int Age { get; set; } 6} 7 8public class UserDtoValidator : AbstractValidator<UserDto> 9{ 10 public UserDtoValidator() 11 { 12 // NotEmpty / NotNull 13 RuleFor(x => x.Username) 14 .NotEmpty().WithMessage("用户名不能为空") 15 .Length(3, 20).WithMessage("用户名长度须在3到20之间"); 16 17 // Email 格式 18 RuleFor(x => x.Email) 19 .NotEmpty().WithMessage("邮箱不能为空") 20 .EmailAddress().WithMessage("邮箱格式不正确"); 21 22 // 数值范围 23 RuleFor(x => x.Age) 24 .InclusiveBetween(18, 120) 25 .WithMessage("年龄须在18到120之间"); 26 } 27} 28
执行验证
1var user = new UserDto { Username = "", Email = "bad", Age = 10 }; 2var validator = new UserDtoValidator(); 3var result = validator.Validate(user); 4 5if (!result.IsValid) 6{ 7 foreach (var failure in result.Errors) 8 { 9 Console.WriteLine($"{failure.PropertyName}: {failure.ErrorMessage}"); 10 } 11} 12 13// ASP.NET Core 自动验证 14[HttpPost] 15public IActionResult CreateUser([FromBody] UserDto user) 16{ 17 // 模型绑定后自动验证 18 if (!ModelState.IsValid) 19 { 20 return BadRequest(ModelState); 21 } 22 // ... 23} 24
常用验证规则
| 方法 | 作用 |
|---|---|
| NotNull() / NotEmpty() | 非空或非空串 |
| Length(min, max) | 字符串长度范围 |
| EmailAddress() | 邮箱格式 |
| Matches(regex) | 正则匹配 |
| InclusiveBetween(min, max) | 数值范围 |
| GreaterThan(x) / LessThan(x) | 大小比较 |
| Must(predicate) | 自定义同步条件 |
| MustAsync(asyncPredicate) | 自定义异步条件 |
进阶特性
跨属性验证
1RuleFor(x => x.EndDate) 2 .GreaterThan(x => x.StartDate) 3 .WithMessage("结束时间必须晚于开始时间"); 4
条件验证
1RuleFor(x => x.Password) 2 .NotEmpty().When(x => x.RequirePassword) 3 .WithMessage("密码不能为空"); 4
集合与嵌套对象
1public class OrderDto { public List<OrderItemDto> Items { get; set; } } 2public class OrderItemDto { public int Quantity { get; set; } } 3 4public class OrderDtoValidator : AbstractValidator<OrderDto> 5{ 6 public OrderDtoValidator() 7 { 8 RuleForEach(x => x.Items) 9 .ChildRules(items => 10 { 11 items.RuleFor(i => i.Quantity) 12 .GreaterThan(0).WithMessage("数量须大于0"); 13 }); 14 } 15} 16
异步验证
1RuleFor(x => x.Username) 2 .MustAsync(async (name, ct) => !await userRepo.ExistsAsync(name)) 3 .WithMessage("用户名已存在"); 4
级联验证
使用 CascadeMode.Stop 提高性能
1RuleFor(x => x.Name).Cascade(CascadeMode.Stop).NotEmpty().MaximumLength(50); 2
验证规则组织
规则集(RuleSets)
1public class UserValidator : AbstractValidator<UserDto> 2{ 3 public UserValidator() 4 { 5 // 公共规则 6 RuleFor(user => user.Name).NotEmpty(); 7 8 // 创建规则集 9 RuleSet("Admin", () => 10 { 11 RuleFor(user => user.IsAdmin).Must(b => b == true) 12 .WithMessage("管理员用户必须设置管理员标志"); 13 }); 14 15 RuleSet("PaymentInfo", () => { 16 RuleFor(c => c.CreditCardNumber).NotEmpty(); 17 RuleFor(c => c.CreditCardExpiry).NotEmpty(); 18 }); 19 } 20} 21 22// 使用指定规则集 23var result = validator.Validate(user, options => 24{ 25 options.IncludeRuleSets("Admin", "PaymentInfo"); 26}); 27
继承与组合
1// 基础验证器 2public class PersonValidator : AbstractValidator<PersonDto> 3{ 4 public PersonValidator() 5 { 6 RuleFor(p => p.Name).NotEmpty(); 7 RuleFor(p => p.BirthDate).LessThan(DateTime.Now); 8 } 9} 10 11// 继承扩展 12public class EmployeeValidator : PersonValidator 13{ 14 public EmployeeValidator() 15 { 16 Include(new PersonValidator()); // 包含基础规则 17 RuleFor(e => e.EmployeeId).NotEmpty(); 18 RuleFor(e => e.Department).NotEmpty(); 19 } 20} 21 22// 组合验证 23public class AdvancedUserValidator : AbstractValidator<UserDto> 24{ 25 public AdvancedUserValidator() 26 { 27 Include(new UserValidator()); 28 RuleFor(u => u.SecurityLevel).InclusiveBetween(1, 5); 29 } 30} 31
自定义扩展
自定义验证器
1public static class CustomValidators 2{ 3 public static IRuleBuilderOptions<T, string> ValidIdCard<T>(this IRuleBuilder<T, string> ruleBuilder) 4 { 5 return ruleBuilder.Must(id => Regex.IsMatch(id, @"^[1-9]\d{16}[0-9X]$")) 6 .WithMessage("身份证格式不正确"); 7 } 8} 9 10// 使用 11RuleFor(x => x.IdCard).ValidIdCard(); 12
自定义属性比较器
1public class DateRangeValidator : PropertyValidator 2{ 3 public DateRangeValidator() : base("{PropertyName} 时间范围不合法") { } 4 5 protected override bool IsValid(PropertyValidatorContext context) 6 { 7 var dto = (MyDto)context.InstanceToValidate; 8 return dto.End > dto.Start; 9 } 10} 11 12// 在 Validator 中 13RuleFor(x => x.Start).SetValidator(new DateRangeValidator()); 14
ASP.NET Core 集成
注册服务(Program.cs)
1builder.Services 2 .AddControllers() 3 .AddFluentValidation(cfg => 4 { 5 // 自动注册当前程序集所有继承 AbstractValidator 的类型 6 cfg.RegisterValidatorsFromAssemblyContaining<Startup>(); 7 // 禁用 DataAnnotations 验证(可选) 8 cfg.RunDefaultMvcValidationAfterFluentValidationExecutes = false; 9 }); 10
自动触发
ASP.NET Core在模型绑定后会自动调用对应Validator,并将错误添加到ModelState。- 在
Controller中可直接检查if (!ModelState.IsValid)或依赖[ApiController]的自动返回行为。
自定义错误响应
1// 配置全局异常处理 2services.Configure<ApiBehaviorOptions>(options => 3{ 4 options.InvalidModelStateResponseFactory = context => 5 { 6 var errors = context.ModelState 7 .Where(e => e.Value.Errors.Count > 0) 8 .ToDictionary( 9 kvp => kvp.Key, 10 kvp => kvp.Value.Errors.Select(e => e.ErrorMessage).ToArray() 11 ); 12 13 return new BadRequestObjectResult(new 14 { 15 Code = 400, 16 Message = "请求验证失败", 17 Errors = errors 18 }); 19 }; 20}); 21
最佳实践
- 分层组织规则:
- 针对同一模型,可拆分多个
Validator(或使用Include()),保持单一职责。
- 针对同一模型,可拆分多个
- 复用规则集:
- 对于常见字段(如邮箱、手机号),可定义公共规则并通过扩展方法重用。
- 错误消息国际化:
- 将消息文本放入资源文件,使用
.WithMessage(x => Resources.FieldRequired)。
- 将消息文本放入资源文件,使用
- 性能考虑:
- 异步验证会序列化执行,若有多个异步规则,可合并或避免不必要的数据库调用。
- 测试验证器:
- 为每个
Validator编写单元测试,覆盖正常和边界情况,确保规则生效。
- 为每个
- 日志与监控:
- 在全局捕获验证失败日志,统计常见错误,优化用户体验。
资源与扩展
- GitHub:
https://github.com/FluentValidation/FluentValidation - 文档:
https://docs.fluentvalidation.net NuGet包:FluentValidation:核心验证库。FluentValidation.DependencyInjectionExtensions:ASP.NET Core集成。
- 扩展:
- 支持
Blazor、MVC和Web API。 - 提供多语言支持和客户端验证。
- 支持
