【问题标题】:ASP.Net 5 MVC 6 - how to return invalid JSON message on POST using FromBody?ASP.Net 5 MVC 6 - 如何使用 FromBody 在 POST 上返回无效的 JSON 消息?
【发布时间】:2016-05-17 13:21:38
【问题描述】:

我正在使用 .Net 4.5.1 创建一个带有 MVC 6 的 ASP.Net 5 应用程序。我有一个使用 FromBody 参数自动获取对象的 POST 方法。

[HttpPost]
public IActionResult Insert([FromBody]Agent agent)
{    
    try
    {
        var id = service.Insert(agent);
        return Ok(id);
    }
    catch (Exception ex)
    {
        return HttpBadRequest(ex);
    }
}

这只是一个概念的证明,我不会只返回成功的 id 或错误的完整异常。

发送有效的 JSON 后,一切正常。但是,当发送无效的 JSON 时,我在调试期间遇到异常:

抛出异常:'Newtonsoft.Json.JsonReaderException' in Newtonsoft.Json.dll

附加信息:解析值后出现意外字符 遇到过:l。路径“名称”,第 2 行,位置 20。

问题是这个错误之后,方法调用正常,但是参数为空,异常没有传播。

我可以检查 null 并返回一个通用消息,但这不如原始消息有用,它包含重要信息,例如标签名称和无效字符的位置。

所以我想捕获这个异常并将它返回给 HTTP 调用者。我怎么做?像这样的:

{"error": "After parsing a value an unexpected character was encountered: l. Path 'Name', line 2, position 20"}

我知道我可以在 try/catch 块中手动捕获和反序列化 JSON,但这对我来说是不可接受的。我想这样做并继续使用 FromBody,我觉得这很有成效。

【问题讨论】:

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


    【解决方案1】:

    默认的 JsonInputFormatter 在遇到错误时实际上会返回一个空模型 - 但它会填充 ModelState 并排除所有异常。

    因此,您可以通过深入了解ModelState 来访问所有遇到的错误:

    [HttpPost]
    public IActionResult Insert([FromBody]Agent agent)
    {
        if (!ModelState.IsValid)
        {
            var errors = ModelState
                .SelectMany(x => x.Value.Errors, (y, z) => z.Exception.Message);
    
            return BadRequest(errors);
        }
    
        // Model is valid, do stuff.
    }
    

    上面的输出是所有异常消息的数组,例如:

    [
        "After parsing a value an unexpected character was encountered: l. Path 'Name', line 2, position 20",
        "Another exception message..."
    ]
    

    JsonInputFormatter - Source

    【讨论】:

    • 太棒了,正是我需要的。谢谢! =)
    • 天哪!这将节省我很多时间!泰。
    【解决方案2】:

    我发现自己遇到了完全相同的问题,但能够找到不同的解决方案。 我将在这里分享我的解决方案,作为@ypsilo0n 答案的替代方案。

    我们可以使用这个中间件过滤器,而不是检查每个控制器 if (!ModelState.IsValid)

    public class FooFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            var modelState = context.ModelState;
    
            if (modelState != null && modelState.IsValid == false)
            {
                // this class takes the model state and parses 
                // it into a dictionary with all the errors
                var errorModel = new SerializableError(modelState);
    
                context.Result = new BadRequestObjectResult(errorModel);
            }
        }
    }
    

    现在,控制器永远不会被调用,因为这个中间件在请求之前运行并结束。 (阅读docs了解更多信息)。

    当我们设置一个非空的context.Result 时,它的意思是“在此处结束 HTTP 请求”(文档)——如果你问我,这不是非常用户友好/直观,但可以(期望返回值)。


    使用 .net core 1.1

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-29
      • 2015-05-11
      • 2016-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多