ASP.NET Core 中的 FluentValidation:为何每个请求一个验证器才是真正的最佳实践
发布: (2026年2月4日 GMT+8 09:02)
4 min read
原文: Dev.to
Source: Dev.to

什么是 FluentValidation?
FluentValidation 是一个流行的 .NET 库,允许你使用 流畅、强类型的语法 来定义验证规则,而不是使用属性或控制器逻辑。
不像这样:
[Required]
[EmailAddress]
public string Email { get; set; }
你可以在单独的验证器类中定义规则:
RuleFor(x => x.Email)
.NotEmpty()
.EmailAddress();
这使得 API 模型保持简洁,并将验证移到专门的验证层。
为什么验证不应该写在控制器里
把验证写在控制器中会导致:
- 逻辑重复
- 代码难以测试
- 控制器臃肿
- 业务规则泄漏到 API 层
FluentValidation 直接集成到 ASP.NET Core 管道中,在控制器执行之前自动运行验证。如果验证失败,框架会返回 400 Bad Request —— 控制器里不需要额外代码。
如何在 ASP.NET Core 中使用 FluentValidation
安装包
dotnet add package FluentValidation
dotnet add package FluentValidation.AspNetCore
创建请求 DTO
public class CreateUserRequest
{
public string FirstName { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
这只是一个请求 DTO —— 不是数据库实体。
创建验证器
public class CreateUserRequestValidator : AbstractValidator
{
public CreateUserRequestValidator()
{
RuleFor(x => x.FirstName)
.NotEmpty()
.MinimumLength(3);
RuleFor(x => x.Email)
.NotEmpty()
.EmailAddress();
RuleFor(x => x.Age)
.InclusiveBetween(18, 60);
}
}
注册 FluentValidation
builder.Services.AddControllers()
.AddFluentValidationAutoValidation();
builder.Services.AddValidatorsFromAssemblyContaining();
就这么简单。每个请求现在都会自动得到验证。
为什么不要使用通用验证器?
很多开发者会问:“我能为所有请求创建一个通用验证器吗?”
简短回答:不能。
原因:
- 每个请求都有不同的业务规则
- 通用验证器会变成上帝类
- 规则会变成
if/else混乱 - 失去类型安全
- 违背单一职责原则
验证不是通用的——它是针对具体用例的。
行业标准模式
CreateUserRequest → CreateUserRequestValidator
UpdateUserRequest → UpdateUserRequestValidator
CreateOrderRequest → CreateOrderRequestValidator
每个 API 合约都有自己的验证器,带来:
- 用例级别的清晰规则
- 便于单元测试
- 零重复代码
- 控制器保持简洁
- 可扩展的架构
在不重复的前提下共享规则
规则扩展
public static class ValidationExtensions
{
public static IRuleBuilderOptions ValidEmail(
this IRuleBuilder rule)
{
return rule.NotEmpty().EmailAddress();
}
}
使用方式
RuleFor(x => x.Email).ValidEmail();
这样既保持验证器的简洁和可复用,又不破坏架构。
最后思考
FluentValidation 不仅仅是一个验证工具——它是一种设计决策。通过使用:
- 请求 DTO
- 每个请求一个验证器
- 自动管道验证
你可以构建出:
- 更安全
- 更清晰
- 更易测试
- 更易维护
这才是实际生产环境中 .NET 系统的模样。