Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AspNet.Core returns 200 OK and invalid Json if there is an exception while iterating an IEnumerable (returned from controller)

It seems that AspNet.Core starts sending response that is IEnumerable right away without iterating over the whole collection. E.g.:

[HttpGet("")]
public async Task<IActionResult> GetData()
{
    IEnumerable<MyData> result = await _service.GetData();
    return Ok(result.Select(_mapper.MapMyDataToMyDataWeb));
}

Now there is an exception that happens during mapping of one of the elements, so I would assume a 500 response, but in reality what happens is that I get a 200 with only partial (and incorrect) Json.

I assume it's a feature and not a bug in Asp.Net Core that provides this behavior and it is additionally relatively easy to fix by calling e.g. ToList(), but I am wondering if there is some kind of flag that can prevent this situation from happening since it does not really make sense for e.g. API project and standard JSON response.

I was not able to find anything in documentation that describes this behavior and how to prevent it.

P.S. I have verified that calling ToList() fixes the issue and the response is 500 with correct exception (with UseDeveloperExceptionPage)

like image 464
Ilya Chernomordik Avatar asked Mar 21 '19 11:03

Ilya Chernomordik


People also ask

What could possibly be the result of returning IEnumerable from a controller action?

If a controller action returns any IEnumerable that has not been realized yet, and an exception happens during evaluation of it, a 200 response is returned with invalid JSON. It is arguable that controller methods should not return an unrealized IEnumerable in the first place, but there's nothing stopping you.

How do you handle exceptions globally in .NET core?

Use the UseExceptionHandler middleware in ASP.NET Core So, to implement the global exception handler, we can use the benefits of the ASP.NET Core build-in Middleware. A middleware is indicated as a software component inserted into the request processing pipeline which handles the requests and responses.


1 Answers

It seems that this is actually "by design", this issue was raised few times on Asp.Net Core github repository.

What happens is that header with 200 is already sent, while the body is not. While I would think that enumeration must proceed before sending headers, asp.net team says it will use more resources on the server and that's why it is like that.

Here is a quote:

It is very likely the case that your exception is thrown while writing to the body, after headers have already been sent to the client, so there's no take-backs on the 200 that was already sent as part of the response. The client will see an error because the body will come back as incomplete.

If you want to deterministically report a 500 when this happens you'll need to either:

  • Buffer your IEnumerable as part of the action (.ToList())
  • Buffer the response body -https://github.com/aspnet/BasicMiddleware/tree/dev/src/Microsoft.AspNetCore.Buffering Obviously both of these things require more server-side resources, which is why we don't have this kind of behavior by default.

I can confirm that this solution worked:

  1. Reference Microsoft.AspNetCore.Buffering package
  2. Write app.UseResponseBuffering() before app.UseMvc()
like image 113
Ilya Chernomordik Avatar answered Sep 28 '22 04:09

Ilya Chernomordik