在自定义模型验证器 .NET Core 中格式化错误消息 [英] Formatting error messages in custom model validator .NET Core

查看:16
本文介绍了在自定义模型验证器 .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 始终具有一致的属性名称,例如PriceEntityID 而不是 $.priceEntityID

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 之前对该路径进行规范化,您当然需要以某种方式修改该路径.那样就相当复杂了.相反,您可以为最终的 ProblemDetails 结果配置自定义 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆