ASP.Net 5 MVC 6 - 如何使用FromBody在POST上返回无效的JSON消息?

Ric*_*nia 7 c# asp.net-mvc asp.net-core-mvc

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

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

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

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

抛出异常:Newtonsoft.Json.dll中的'Newtonsoft.Json.JsonReaderException'

附加信息:解析值后,遇到意外字符:l.路径'名称',第2行,第20位.

问题是在此错误之后,该方法被正常调用,但是使用null参数,并且异常不会传播.

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

所以我想捕获此异常并将其返回给HTTP调用者.我怎么做?像这样的东西:

{"error": "After parsing a value an unexpected character was encountered: l. Path 'Name', line 2, position 20"}
Run Code Online (Sandbox Code Playgroud)

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

小智 12

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.
}
Run Code Online (Sandbox Code Playgroud)

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

[
    "After parsing a value an unexpected character was encountered: l. Path 'Name', line 2, position 20",
    "Another exception message..."
]
Run Code Online (Sandbox Code Playgroud)

JsonInputFormatter - 来源


Vet*_*ras 6

我发现自己遇到了完全相同的问题,但能够找到不同的解决方案。我将在这里分享我的解决方案,以替代@ 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,控制器永远不会被调用,因为该中间件在请求之前和结束之前运行。(有关详细信息,请阅读文档)。

当我们设置非null时,context.Result它表示“在此处结束HTTP请求”(文档)-如果您问我,不是很友好/直观,但是可以(请期待返回值)。


使用.net core 1.1