跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

C#怎么使用FluentValidation_C#优雅的实体属性校验规则【核心】

正确写法是:对可能为null的导航属性用NotNull()守卫顶层+When()显式判空,值类型用Must()判断;异步校验需用MustAsync()或SetValidator()指向含异步规则的验证器;注册时须AddControllers().AddFluentValidation()并确保特性与泛型匹配。 FluentValidation 的
AbstractValidator
怎么写才不踩空引用? 直接继承
AbstractValidator
时,最常见的崩溃是校验器内部访问了
null
属性却没提前判空——比如
RuleFor(x => x.Address.Street)
,但
Address
是
null
,就会抛
NullReferenceException
。 正确做法是:所有可能为
null
的导航属性,必须用
When()
或链式
NotNull()
+
SetValidator()
分层守卫:
RuleFor(x => x.Address).NotNull().WithMessage("地址不能为空"); RuleFor(x => x.Address.Street) .NotEmpty().When(x => x.Address != null); // 显式守卫
别依赖
NotNull()
自动跳过子属性校验——它只拦自己,不拦后续链式调用 如果
Address
是值类型(如
struct
),
NotNull()
无效,得用
Must()
手动判断 泛型校验器注册到 DI 容器时,确保生命周期是
Scoped
或
Transient
,避免跨请求状态污染 怎么让
ValidateAsync()
真正异步,而不是假异步?
ValidateAsync()
默认是同步执行的,除非你显式用了异步规则(比如调用外部 API、数据库查重)。很多人误以为只要方法签名带
Async
就自动异步,结果压测时线程池被卡死。 真正触发异步的只有两类规则:
MustAsync()
:传入
Func>
SetValidator()
指向一个继承自
AbstractValidator
且内部含异步规则的验证器 示例(邮箱唯一性检查):
RuleFor(x => x.Email) .MustAsync(async (model, email, ct) => { return !await _userRepository.ExistsByEmailAsync(email, ct); }) .WithMessage("该邮箱已被注册");
注意:
MustAsync
内部不能混用同步 I/O(如直接调用
HttpClient.Send()
),否则会阻塞线程;也别在异步规则里 throw 异常,要用
return false
+
WithMessage
控制错误输出。 C知道 CSDN推出的一款AI技术问答工具 下载 为什么
AddFluentValidation()
后模型绑定仍不触发校验? ASP.NET Core 中,仅注册 FluentValidation 不等于开启自动校验。常见遗漏点: 没在
Program.cs
调用
services.AddControllers().AddFluentValidation(...)
(MVC)或
AddFluentValidationClientsideAdapters()
(Razor Pages) 控制器参数没加
[ApiController]
特性 —— 它启用了默认的
ModelState
自动验证拦截 参数是
object
或基类类型(如
PersonBase
),但注册时只配了子类
PersonValidator
,需手动指定
validatorType
或用
RegisterValidatorsFromAssemblyContaining()
验证失败时,默认返回
400 BadRequest
并附带
ModelState
错误。若想统一响应格式,要替换
InvalidModelStateResponseFactory
,而不是重写 ActionFilter。 自定义错误消息里的占位符为什么不替换?
{PropertyName}
、
{PropertyValue}
这些占位符失效,通常是因为: 手误写成大括号全角字符(中文输入法下容易中招) 在
WithMessage()
中用了字符串插值(
$"xxx {x.Name}"
),导致占位符被提前求值,变成字面量 使用了
WithName()
但没配合
OverridePropertyName
,导致
{PropertyName}
仍显示原始属性名 正确写法:
RuleFor(x => x.Age) .GreaterThan(0) .WithMessage("{PropertyName} 必须大于 {ComparisonValue}") // ✅ 占位符原样传入 .WithName("年龄"); // ✅ 配合生效
复杂消息建议用资源文件 +
WithLocalizedMessage()
,避免硬编码和多语言维护困难。 FluentValidation 的核心不是“写更多规则”,而是让每条规则的执行边界清晰、失败路径可控。最容易被忽略的是:异步规则的异常吞吐、
null
导航属性的隐式链式访问、以及 ASP.NET Core 中验证触发链的完整闭环——缺一环,就只是个漂亮的装饰器。

相关文章