首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ModelStateDictionary -标识不应返回给调用方的错误

ModelStateDictionary -标识不应返回给调用方的错误
EN

Stack Overflow用户
提问于 2021-02-08 15:22:37
回答 2查看 278关注 0票数 0

我有一个简单的web,它使用验证属性执行一些基本的验证。如果请求不符合验证要求,那么我将从ModelStateDictionary中提取错误消息,并将这些消息作为字典返回给调用方,其中键是指向有错误的属性的路径,该消息指示问题所在。

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

是否有可能区分验证错误(由验证属性/ IValidatableObject实现生成)和试图将无效的JSON映射到特定对象时生成的错误?

例如:

My models

代码语言:javascript
复制
public class Order
{
    [Required]
    public Customer Customer { get; set; }
}

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

我的行动方法

代码语言:javascript
复制
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;
}

我的示例输入:

缺少必需数据的

代码语言:javascript
复制
{
    "Customer": {
        "Name": null // name is required
    }
}

由于Customer.Name是用RequiredAttribute修饰的,因此将生成以下错误消息:

代码语言:javascript
复制
{
    "Customer.Name": "The Name field is required."
}

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

  1. JSON值不映射到.Net对象类型

代码语言:javascript
复制
{
    "Customer": 123 // deserializing will fail to map this value to a Customer object
}

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

代码语言:javascript
复制
{
    "Customer": "Error converting value 123 to type 'MyProject.Models.Customer'. Path 'Customer', line 2, position 19."
}

返回到调用方是不行的,因为它包含要映射到的对象的完整命名空间路径。在本例中,一些是通用的(例如"Bad JSON值“)。应该用作错误消息。

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

正如在上面的代码中所看到的,我想我可以检查ModelEntry.Exception属性并使用它来确定是否需要屏蔽错误消息,但是在我的两个示例中,这都是空的。

解决方案可能是检查错误消息是否以Error converting value开头,如果是的话,请屏蔽该消息不受调用方的影响。这看起来不太可靠,我也不确定这在现实世界的例子中会有多可靠。

EN

回答 2

Stack Overflow用户

发布于 2021-02-09 09:20:35

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

代码语言:javascript
复制
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中配置:

代码语言:javascript
复制
services.AddControllers().ConfigureApiBehaviorOptions(options =>
{
    options.InvalidModelStateResponseFactory = context =>
    {
        var problems = new CustomBadRequest(context);

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

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

结果:

票数 1
EN

Stack Overflow用户

发布于 2021-02-08 16:53:09

通常,当由于JSON有效负载没有正确格式化而导致模型绑定失败时,传入的模型对象(在您的示例中为“order”)将是null。只需输入一个简单的null检查,并返回带有泛型错误消息的BadRequest。

代码语言:javascript
复制
if (order == null)
    return BadRequest("Invalid JSON");
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66104418

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档