【问题标题】:Formatting error messages in custom model validator .NET Core在自定义模型验证器 .NET Core 中格式化错误消息
【发布时间】:2021-08-14 05:41:24
【问题描述】:

在 .NET Core 3.x 应用程序中,我有一个类似以下的模型,其中使用了自定义验证属性 [CustomValidator]。为 CustomValidator 返回的错误消息没有 $. 前缀,而内置 JSON 验证器返回前缀。我想让它们保持一致(无论是否始终使用 $. 前缀)。知道如何做到这一点吗?

示例模型:

public class Dto 
{
    public float Price {get; set;}
    [CustomValidator] public int? EntityID {get; set;}
}

[CustomValidator] 是一个自定义验证属性,它执行类似的操作

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");
    }

}

我在Controller.Action 中验证模型

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

【问题讨论】:

    标签: c# asp.net-core .net-core asp.net-core-mvc asp.net-core-3.1


    【解决方案1】:

    $. 前缀是 JsonException 中包含的 json 路径的一部分。您可以在此处SystemTextJsonInputFormatter的默认json输入格式化程序的源代码中看到它。

    因此,从技术上讲,在将该路径传递给方法ModelState.TryAddModelError 之前对其进行规范化,您当然需要以某种方式修改该路径。这种方式相当复杂。相反,您可以为最终的 ProblemDetails 结果配置自定义 InvalidModelStateResponseFactory。虽然这样你可能会权衡性能损失,特别是当验证错误频繁发生并且涉及很多错误时。但我认为这在现实中并不常见。这种方法的想法是修改ModelState,方法是尝试交换所有以$. 为前缀的条目,并将它们替换为去掉该键前缀的条目。

    您可以通过配置ApiBehaviorOptions 来配置该响应工厂,如下所示:

    //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);
                };
            });
    

    【讨论】:

      猜你喜欢
      • 2011-10-12
      • 2012-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-31
      相关资源
      最近更新 更多