在自定义模型验证器 .NET Core 中格式化错误消息 [英] Formatting error messages in custom model validator .NET Core
问题描述
在 .NET Core 3.x 应用程序中,我有一个如下所示的模型,其中使用了自定义验证属性 [CustomValidator]
.为 CustomValidator
返回的错误消息没有 $.
前缀,而内置 JSON 验证器返回带有前缀.我想让它们保持一致(无论是否始终带有 $.
前缀).知道怎么做吗?
In a .NET Core 3.x application, I have a model like the following where a custom validation attribute [CustomValidator]
is used. The error messages returned for CustomValidator
doesn't have a $.
prefix whereas the built-in JSON validator returns with the prefix. I'd like to have them consistent (either with $.
prefix all the time or not). Any idea how could this be done?
示例模型:
public class Dto
{
public float Price {get; set;}
[CustomValidator] public int? EntityID {get; set;}
}
其中 [CustomValidator]
是一个自定义验证属性,它执行类似这样的操作
where [CustomValidator]
is a custom validation attribute which does something like this
public class CustomValidatorAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var isValid = CheckSomething(...);
return isValid
? ValidationResult.Success
: new ValidationResult($"Invalid value for Entity");
}
}
我使用
if (!ModelState.IsValid) return BadRequest(ModelState);
对于输入:
{
"Price" : "abc"
}
它回来了
{
...
"errors": {
"$.price": [
"The JSON value could not be converted to System.Single. Path: $.price | LineNumber: 7 | BytePositionInLine: 21."
]
}
}
而对于输入:
{
"Price" : 1.0,
"EntityID": -1,
}
它回来了
{
...
"errors": {
"EntityID": [
"Invalid value for Entity"
]
}
}
我希望 errors
始终具有一致的属性名称,例如Price
和 EntityID
而不是 $.price
和 EntityID
I'd like it to have the errors
always having consistent property names e.g. Price
and EntityID
instead of $.price
and EntityID
推荐答案
$.
前缀是 JsonException
中包含的 json 路径的一部分.您可以在此处的默认 json 输入格式化程序的源代码中看到它 SystemTextJsonInputFormatter.
The $.
prefix is part of the json path contained in JsonException
. You can see it in the source code of the default json input formatter here SystemTextJsonInputFormatter.
从技术上讲,要在将该路径传递给方法 ModelState.TryAddModelError
之前对该路径进行规范化,您当然需要以某种方式修改该路径.那样就相当复杂了.相反,您可以为最终的 InvalidModelStateResponseFactory
.尽管通过这种方式您可以权衡性能损失,尤其是当验证错误频繁发生并且涉及很多错误时.但我认为这在现实中并不常见.这种方法的想法是修改 ModelState
,尝试换出所有键前缀为 $.
的条目,并将它们替换为去掉该键前缀的条目.
So technically to normalize that path before it being passed to the method ModelState.TryAddModelError
, you need of course to modify that path somehow. It's quite complicated that way. Instead you can configure a custom InvalidModelStateResponseFactory
for your final ProblemDetails
result. Although this way you may take a tradeoff of performance hit especially when the validation error occurs frequently and there are a lot of errors involved. But I think it's not a common case in reality. The idea of this approach is to modify the ModelState
by trying to swap out all the entries having key prefixed with $.
and replace them with the ones having that key prefix trimmed off.
您可以通过配置 ApiBehaviorOptions
来配置该响应工厂,如下所示:
You can configure that response factory by configuring the ApiBehaviorOptions
, like this:
//inside Startup.ConfigureServices
services.Configure<ApiBehaviorOptions>(o => {
//we need to call this original factory inside our custom factory
var originalFactory = o.InvalidModelStateResponseFactory;
o.InvalidModelStateResponseFactory = context => {
//get all the keys prefixed with $. (which should be related to json errors)
var jsonPathKeys = context.ModelState.Keys.Where(e => e.StartsWith("$.")).ToList();
foreach(var key in jsonPathKeys)
{
var normalizedKey = key.Substring(2);
foreach (var error in context.ModelState[key].Errors)
{
if (error.Exception != null)
{
context.ModelState.TryAddModelException(normalizedKey, error.Exception);
}
context.ModelState.TryAddModelError(normalizedKey, error.ErrorMessage);
}
context.ModelState.Remove(key);
}
return originalFactory(context);
};
});
这篇关于在自定义模型验证器 .NET Core 中格式化错误消息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!