Description
Describe the bug
When returning a 404 within UseExceptionHandler when the api is not running in IIS Express, http protocol errors happen. There are no issues when running in IIS Express.
To Reproduce
Here is an example repo that reliably reproduces this error.
Use a handler like the following:
app.UseExceptionHandler(builder =>
builder.Run(async context =>
{
var errorFeature = context.Features.Get<IExceptionHandlerFeature>();
var ex = errorFeature.Error;
context.Response.StatusCode = ex switch
{
DomainException domainEx => StatusCodes.Status400BadRequest,
NotFoundException notFoundEx => StatusCodes.Status404NotFound, // if you change this status code to anything else, it will be fine
_ => StatusCodes.Status500InternalServerError,
};
await context.Response.WriteAsync("There was an error caught in the exception handler lambda");
}));
Then use a controller like the following
[ApiController]
public class NotFoundController : ControllerBase
{
/// <summary>
/// This endpoint works fine and returns a 400 as expected in all cases
/// </summary>
/// <returns></returns>
[HttpGet("throw-domain-ex")]
public IActionResult ThrowDomainEx() => throw new DomainException();
/// <summary>
/// This endpoint returns a 404 in IIS Express and causes http protocol errors otherwise.
/// Chrome logs this as "Failed to load resource: net::ERR_HTTP2_PROTOCOL_ERROR".
/// Trying to call the endpoint when it isn't running in IIS Express with something like HttpClient will cause: System.Net.Http.HttpRequestException: Error while copying content to a stream. ---> System.IO.IOException: ---> NotFoundBugExample.NotFoundBugExample
/// </summary>
/// <returns></returns>
[HttpGet("throw-not-found")]
public IActionResult ThrowNotFound() => throw new NotFoundException();
/// <summary>
/// This endpoint works fine and returns a 404 as expected in all cases
/// </summary>
/// <returns></returns>
[HttpGet("return-not-found")]
public IActionResult ReturnNotFound() => NotFound();
}
Lastly, run the app NOT in IIS Express and hit the endpoint that causes the middleware to set the status code to 404
. In this example, that is /throw-not-found
.
It should also be noted that setting the response code to anything else in the middleware circumvents the issue.
It should also be noted that using normal middleware works fine. So replacing the aforementioned middleware with the one below will fix the issue.
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
context.Response.StatusCode = ex switch
{
DomainException => StatusCodes.Status400BadRequest,
// if you change this status code to anything else, it will be fine
NotFoundException => StatusCodes.Status404NotFound,
_ => StatusCodes.Status500InternalServerError,
};
await context.Response.WriteAsync("There was an error caught in the exception handler lambda");
}
});
Exceptions (if any)
This is what the chrome dev tools logs when I hit this endpoint using Swagger UI
Hitting the endpoint with something like HttpClient
will cause the following error: System.Net.Http.HttpRequestException: Error while copying content to a stream. ---> System.IO.IOException: ---> NotFoundBugExample.NotFoundBugExample
Further technical details
- ASP.NET Core version:
net5.0
- Include the output of
dotnet --info
$ dotnet --info
.NET SDK (reflecting any global.json):
Version: 5.0.201
Commit: a09bd5c86c
Runtime Environment:
OS Name: Windows
OS Version: 10.0.19042
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\5.0.201\
Host (useful for support):
Version: 5.0.4
Commit: f27d337295
.NET SDKs installed:
2.1.202 [C:\Program Files\dotnet\sdk]
2.1.522 [C:\Program Files\dotnet\sdk]
3.1.407 [C:\Program Files\dotnet\sdk]
5.0.103 [C:\Program Files\dotnet\sdk]
5.0.104 [C:\Program Files\dotnet\sdk]
5.0.201 [C:\Program Files\dotnet\sdk]
.NET runtimes installed:
Microsoft.AspNetCore.All 2.1.26 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.26 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.1.26 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.1.13 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
To install additional .NET runtimes or SDKs:
https://aka.ms/dotnet-download
- IDE: Visual Studio 16.9.1