FluentValidation 正确运行需三要素:继承 AbstractValidator、RuleFor 写在构造函数中、验证时传入非 null 实例;否则易静默返回 true 或抛 NullReferenceException。

FluentValidation 不是“配置完就能用”的黑盒,它依赖三件事:正确继承 AbstractValidator<t></t>、规则写在构造函数里、验证时传入真实实例。漏掉任一环,Validate() 就会静默返回 IsValid = true 或抛出 NullReferenceException。
怎么写一个能跑起来的验证器类
核心就一条:必须继承 AbstractValidator<t></t>,且所有 RuleFor 调用必须放在构造函数中。写在其他地方(比如单独方法、静态初始化块)不会生效。
-
RuleFor的 lambda 参数必须是属性访问表达式,不能是计算属性或方法调用(如x => x.FullName.ToUpper()会编译失败) - 如果验证目标类是
record或有只读属性,确保该属性有 getter,否则RuleFor无法绑定 - 不要在构造函数里 new 其他验证器并直接调用其
Validate()—— 这会绕过 FluentValidation 的上下文和级联逻辑
为什么 Validate() 总是返回 IsValid = true
最常见原因是验证对象本身为 null。FluentValidation 默认不校验输入对象是否为空,而是直接遍历其属性 —— 如果传入 null,RuleFor(x => x.Name) 中的 x 是空引用,整个表达式求值失败,但框架捕获异常后默认跳过该规则,不报错也不记录。
- 显式检查 null:在调用
Validate()前加if (user == null) return new ValidationResult(...) - 用
NotNull()规则只能校验属性值,不能校验根对象;要校验根对象,得靠业务层守门 - 调试时打印
result.Errors长度,别只看IsValid—— 有时错误被吞了但Errors里有内容
RuleSet 用错会导致验证完全不触发
RuleSet 不是开关,而是命名规则分组。不指定 IncludeRuleSets 选项时,Validate() 默认只执行无名规则(即不在任何 RuleSet 块里的规则)。
- 定义了
RuleSet("Create", () => { ... }),却调用validator.Validate(obj)→ 什么都不会验 - 正确做法:
validator.Validate(obj, opt => opt.IncludeRuleSets("Create")) - 多个 RuleSet 可同时启用:
opt.IncludeRuleSets("Create", "EmailCheck") - 注意大小写敏感:
"create"和"Create"是两个不同规则集
ASP.NET Core 自动验证失效的几个硬坑
装了 FluentValidation.AspNetCore 并注册服务后,仍可能不触发自动验证,问题通常出在模型绑定阶段之前或类型匹配上。
- 控制器参数类型必须和验证器泛型参数严格一致(比如验证器是
UserDtoValidator : AbstractValidator<userdto></userdto>,Action 参数就得是UserDto,不能是object或基类) - 没调用
services.AddFluentValidationClientsideAdapters()?那前端 JS 校验不会生成,但后端仍会验证 - 使用
[FromBody]时,若 JSON 字段名和 C# 属性名不匹配(比如用了[JsonPropertyName("user_name")]),而验证器里写的是RuleFor(x => x.UserName),验证仍走通 —— 因为验证的是反序列化后的对象,不是原始 JSON - 自定义
IValidatorFactory实现时,若GetValidator返回null,框架会静默跳过验证,不会报错
RuleSet 和级联模式(CascadeMode.Stop)这类机制,表面是功能开关,实际是控制验证流程的“断点”和“路径”。改一处,整条验证链的行为都可能变,务必在单元测试里覆盖带 RuleSet 的分支路径。


















