c# - 是否可以在不启用子属性隐式验证的情况下使用 FluentValidation 验证可枚举模型?
问题描述
给定以下模型、验证器和控制器(在 ASP.NET Core 3.1 中):
public sealed class Model
{
public string Property { get; set; }
}
public sealed class ModelValidator : AbstractValidator<Model>
{
public ModelValidator()
{
RuleFor(m => m.Property).NotEmpty();
}
}
[ApiController]
[Route("[controller]")]
public sealed class TheController : ControllerBase
{
[HttpPost]
public IActionResult PostSomething(IEnumerable<Model> model)
{
// Do something
return Ok();
}
}
有没有一种方法可以在不启用我的启动类的情况下验证IEnumerable<Model>
模型,如下所示?TheController.PostSomething
ImplicitlyValidateChildProperties
services.AddControllers()
.AddFluentValidation(c => c.ImplicitlyValidateChildProperties = true);
到目前为止,我一直无法找到一种无需设置即可成功验证可枚举模型的方法ImplicitlyValidateChildProperties = true
。
更新:当更改 的签名PostSomething
,添加和注册一个额外的验证器(如下)时,确实会发生验证。
[HttpPost]
public IActionResult PostSomething(List<Model> model)
{
// Do something
return Ok();
}
public sealed class ListOfModelValidator : AbstractValidator<List<Model>>
{
public ListOfModelValidator()
{
RuleForEach(m => m).SetValidator(new ModelValidator());
}
}
然而,这感觉不对。它还会导致ValidationProblemDetails
返回的响应出现问题。具有验证错误的元素的索引将以 lambda 参数的名称为前缀,因此[0].Property
将m[0].Property
. 我一直无法找到删除 lambda 参数名称的方法,无论是使用WithName
还是自定义DisplayNameResolver
.
但是,这种方法感觉不对。我宁愿不必更改签名PostSomething
或添加一些感觉像 kludge 的东西来删除 lambda 参数名称。
虽然我可以启用ImplicitlyValidateChildProperties
它,但它可能会导致我需要进行的一些更改出现问题,所以我宁愿尽可能避免它。
解决方案
更新
从 FluentValidation 的 9.4.0 版本开始,该ImplicitlyValidateRootCollectionElements
选项可用,它将启用集合类型模型的验证,而不启用子属性的隐式验证。它可以像隐式子模型验证一样启用,例如:
services.AddMvc().AddFluentValidation(fv => {
fv.ImplicitlyValidateRootCollectionElements = true;
});
模型绑定和隐式子属性验证
模型绑定反序列化ICollection<TElement>
和IEnumerable<TElement>
as IList<TElement>
(List<TElement>
请参阅CollectionModelBinder.CreateEmptyCollection
)。
当ImplicitlyValidateChildProperties
is时false
,FluentValidation 将仅尝试查找与 action 方法参数的绑定类型匹配的验证器,List<TElement>
在我的示例中将是 或List<Model>
。因此,无需更改 的签名,TheController.PostSomething
但验证者必须从AbstractValidator<List<TElement>>
.
当ImplicitlyValidateChildProperties
is时true
,FluentValidation 将尝试为 type 查找验证器TElement
,因此将发生对根集合模型的元素的验证。但是,FluentValidation 也将验证TElement
匹配验证器在哪里注册并且ImplicitlyValidateChildProperties
是true
. 在我的情况下,我希望对集合元素进行验证,但我不希望对每个元素的子属性进行自动验证,因此不适合启用对子属性的隐式验证。
覆盖RuleForEach
属性名称
不幸的是,尝试通过使用WithName
orOverridePropertyName
和传递来删除 lambda 参数名称会string.Empty
失败,因为 null 或空属性名称总是被覆盖(在 v9.3.0 中,请参阅 参考资料CollectionPropertyRule.InvokePropertyValidator
)。RuleForEach
这对于似乎主要是为其设计的集合属性是有意义的,但不适用于根集合。
通过显式验证,似乎有两种方法可以从验证错误中删除 lambda 参数名称,这两种方法都有点麻烦。
- 覆盖
Validate
并重写属性名称。 - 实现
IValidatorInterceptor
并重写 中的属性名称AfterMvcValidation
。
概括
可用的选项有:
- 设置
ImplicitlyValidateChildProperties
为true
并确保设计与子属性的自动验证一起工作。 - 使用显式验证并覆盖
Validate
或使用验证器拦截器在验证后重写属性名称。 - 重新设计模型(尽管这是否是一个可行的选择显然取决于各种其他因素)。
推荐阅读
- html - 无法通过 NSAttributedString 适应 UITextView/HTML 中的屏幕宽度
- python - 将带有应用样式的 Pandas DataFrame 导出为 pdf 或 png
- node.js - 无法在类中使用类外的变量
- node.js - 如何在surge.sh 上部署Express 应用程序
- c# - C# 切换标签系统税
- python - Python SciPy ODE 求解器不收敛
- java - 将 sourceforge 依赖添加到 Gradle 项目
- java - 让 JPanel 重叠
- c# - 进行 SharpSvn 合并时如何忽略空格更改?
- javascript - 如何访问指令内部的控制器内部功能?一切都返回未定义