asp.net core middleware can't catch newtonsoft exception

i use middleware catch request exception and write response like this

public async Task Invoke(HttpContext context /* other dependencies */)
{
    try
    {

        await next(context);
    }
    catch (Exception ex)
    {
        logger.LogError(ex.Message);
        await HandleExceptionAsync(context, ex); //write response
    }
}
    private static Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        FanjiaApiResultMessage resultMessage = new FanjiaApiResultMessage()
        {
            ResultCode = -1,
            Data = null,
            Msg = exception.Message
        };
        string result = JsonConvert.SerializeObject(resultMessage);
        context.Response.ContentType = "application/json;charset=utf-8";
        if (exception is QunarException)
        {
            context.Response.StatusCode = (int)(exception as QunarException).httpStatusCode;
        }
        else
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        return context.Response.WriteAsync(result);
    }

a request model param like this

public class FlightModel {
   [JsonProperty("depCity", Required = Required.Always)]
   public string DepCity { get; set; }
}

public IActionResult Test(FlightModel model){
  return Content("test");
}

when i post the FlightModel without DepCity , i will get the exception

{
    "errors": {
        "": [
            "Required property 'depCity' not found in JSON. Path '', line 6, position 1."
        ]
    },
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "8000000a-0003-ff00-b63f-84710c7967bb"
}

Obviously the exception are not catched by middleware.

why middleware is not catch?

1 answer

  • answered 2019-01-11 05:55 Simon C

    An Aspnet Core Model Validation failure does not throw an exception. It provides it's own response with a status code of 400 (Bad request) in a default format.

    There are a few ways to override this including a custom attribute: https://www.jerriepelser.com/blog/validation-response-aspnet-core-webapi/

    It looks like this:

    public class ValidateModelAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (!context.ModelState.IsValid)
            {
                context.Result = new ValidationFailedResult(context.ModelState);
            }
        }
    }
    

    And then is added like so:

    [Route("api/values")]
    [ValidateModel]
    public class ValuesController : Controller
    {
    ...
    }
    

    Or you can control the response generation by overriding the InvalidModelStateResponseFactory, like in this SO question: How do I customize ASP.Net Core model binding errors?

    Here is an example:

    services.Configure<ApiBehaviorOptions>(o =>
    {
        o.InvalidModelStateResponseFactory = actionContext =>
            new MyCustomBadRequestObjectResult(actionContext.ModelState);
    });