0

我有一个简单的 Web API,它使用验证属性执行一些基本验证。如果请求不满足验证要求,我会从中提取错误消息ModelStateDictionary并将其作为字典返回给调用者,其中键是出现错误的属性的路径,消息指示问题所在。

问题是,给定某些输入,错误消息ModelStateDictionary通常包含我不想返回给客户端的信息(例如转换器尝试将 JSON 映射到的对象的全名(包括命名空间)) .

是否可以区分验证错误(由 Validation Attributes / IValidatableObject 实现生成)和尝试将无效 JSON 映射到某个对象而生成的错误?

例如:

我的模型

public class Order
{
    [Required]
    public Customer Customer { get; set; }
}

public class Customer
{
    [Required]
    public string Name { get; set; }
}

我的操作方法

public IActionResult Post(Order order)
{
    if (!ModelState.IsValid)
    {
        var errors = GetErrors(ModelState);
                
        return BadRequest(errors);
    }

    return Ok();
}

private Dictionary<string, string> GetErrors(ModelStateDictionary modelState)
{
    var errors = new Dictionary<string, string>();
            
    foreach (var error in modelState)
    {
        string message = null;
        if (error.Value.Errors.Any(e => e.Exception != null))
        {
            message = "Unable to interpret JSON value."; 
        }
        else
        {
            message = string.Join(". ", string.Join(". ", error.Value.Errors.Select(e => e.ErrorMessage)));
        }
                
        errors.Add(error.Key, message);
    }

    return errors;
}

我的示例输入:

  1. 缺少所需数据
{
    "Customer": {
        "Name": null // name is required
    }
}

因为Customer.Name用 装饰RequiredAttribute,这会生成以下错误消息:

{
    "Customer.Name": "The Name field is required."
}

这可以返回给调用者,所以这里没有问题。

  1. JSON 值不映射到 .Net 对象类型
{
    "Customer": 123 // deserializing will fail to map this value to a Customer object
}

由于将值反序列化为123对象Customer失败,因此会生成以下错误消息:

{
    "Customer": "Error converting value 123 to type 'MyProject.Models.Customer'. Path 'Customer', line 2, position 19."
}

不能返回给调用者,因为它包含映射到的对象的完整命名空间路径。在这种情况下,应该使用通用的东西(例如"Bad JSON value.")作为错误消息。

谁能帮我找到隐藏这些错误消息的解决方案,这些错误消息包含不应返回给调用者的信息?


从上面的代码中可以看出,我认为我可以检查ModelEntry.Exception属性并使用它来确定是否需要对调用者屏蔽错误消息,但在我的两个示例中都是 null。

解决方案可能是检查错误消息是否以 开头Error converting value,如果是,则屏蔽来自调用者的消息。这似乎不是很健壮,我不确定这在现实世界的例子中有多可靠。

4

2 回答 2

1

HTTP 400 响应的默认响应类型是ValidationProblemDetails类。因此,我们将创建一个继承 ValidationProblemDetails 类的自定义类并定义我们的自定义错误消息。

public class CustomBadRequest : ValidationProblemDetails
{
    public CustomBadRequest(ActionContext context)
    {
        ConstructErrorMessages(context);
        Type = context.HttpContext.TraceIdentifier;
    }

    private void ConstructErrorMessages(ActionContext context)
    {
       //this is the error message you get...
        var myerror = "Error converting value";
        foreach (var keyModelStatePair in context.ModelState)
        {
            var key = keyModelStatePair.Key;
            var errors = keyModelStatePair.Value.Errors;
            if (errors != null && errors.Count > 0)
            {
                if (errors.Count == 1)
                {
                    var errorMessage = GetErrorMessage(errors[0]);
                    if (errorMessage.StartsWith(myerror))
                    {
                        Errors.Add(key, new[] { "Cannot deserialize" });
                    }
                    else
                    {
                        Errors.Add(key, new[] { errorMessage });
                    }

                }
                else
                {
                    var errorMessages = new string[errors.Count];
                    for (var i = 0; i < errors.Count; i++)
                    {
                        errorMessages[i] = GetErrorMessage(errors[i]);
                        if (errorMessages[i] == myerror)
                        {
                            errorMessages[i] = "Cannot deserialize";
                        }
                    }

                    Errors.Add(key, errorMessages);
                }
            }
        }
    }

    string GetErrorMessage(ModelError error)
    {
        return string.IsNullOrEmpty(error.ErrorMessage) ?
            "The input was not valid." :
        error.ErrorMessage;
    }
}

在您的 Startup.cs 中配置:

services.AddControllers().ConfigureApiBehaviorOptions(options =>
{
    options.InvalidModelStateResponseFactory = context =>
    {
        var problems = new CustomBadRequest(context);

        return new BadRequestObjectResult(problems);
    };
});

确保您的控制器包含[ApiController]属性。

结果:

在此处输入图像描述

于 2021-02-09T09:20:35.047 回答
0

通常,当模型绑定因 JSON 有效负载格式不正确而失败时,传入的模型对象(在您的情况下为“订单”)将为null. 只需进行简单的空检查并返回带有通用错误消息的 BadRequest。

if (order == null)
    return BadRequest("Invalid JSON");
于 2021-02-08T16:53:09.810 回答