Welcome to ZaiZheLe Developer Zone-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

How can I make upstream exceptions return UnprocessableEntityResult with ASP.NET Core?

When a request comes in to my ASP.NET Core API controller that is properly formed, but can't be processed because of some sort of business rule that throws an exception when violated, I want to return a 422 HTTP response with error text in the body. I would like this to work across many actions on my controller without adding code such as a try/catch block to each of them.

How can I achieve this?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

It may be easiest to implement an IAsyncExceptionFilter in your application - this can pick up unhandled exceptions on controller actions and rewrite them into a form you'd like, such as the 422 HTTP response desired here. If implementing an IAsyncExceptionFilter you can use the below code which was tested with ASP.NET Core 3.1 to return the exception message to the client as plain text:

public override async Task OnExceptionAsync(ExceptionContext context) {
  if (!context.HttpContext.Response.HasStarted) {
    var errorMessage = "Could not do that because of X reason."; // or you could use `context.Exception.Message` if it is secure in your application to do so
    var responseBytes = Encoding.UTF8.GetBytes(errorMessage).AsMemory();
    context.Result = new UnprocessableEntityResult();
    context.HttpContext.Response.StatusCode = 422;
    context.HttpContext.Response.ContentType = "text/plain;charset=UTF-8";
    context.HttpContext.Response.ContentLength = responseBytes.Length;
    await context.HttpContext.Response.BodyWriter.WriteAsync(responseBytes);
    context.ExceptionHandled = true;
  } else {
     // if the response has already started, this can't be used.  You will have to figure
     // out a different solution.
  }
}

If not using an IAsyncExceptionFilter, you can still use most of the above code, it would be a matter of setting the same properties on the Response.HttpContext object available in your controller's handler, and then returning the UnprocessableEntityResult.

Note that setting both the ContentType and ContentLength headers correctly will prevent net::ERR_INCOMPLETE_CHUNKED_ENCODING errors with some browsers. In this case, the ContentLength will be accurate because we get the bytes of the error string from Encoding.UTF8 first and set the ContentLength to the length of those bytes, not just the string length. Also, the ContentType header includes the charset=UTF-8 qualifier to remove any ambiguity.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to ZaiZheLe Developer Zone-Open, Learning and Share
...