Skip to content

Add an option to allow 404 responses from ExceptionHandlers #26567

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ private async Task HandleException(HttpContext context, ExceptionDispatchInfo ed

await _options.ExceptionHandler(context);

if (context.Response.StatusCode != StatusCodes.Status404NotFound)
if (context.Response.StatusCode != StatusCodes.Status404NotFound || _options.AllowStatusCode404Response)
{
if (_diagnosticListener.IsEnabled() && _diagnosticListener.IsEnabled("Microsoft.AspNetCore.Diagnostics.HandledException"))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,14 @@ public class ExceptionHandlerOptions
/// explicitly provided, the subsequent middleware pipeline will be used by default.
/// </summary>
public RequestDelegate ExceptionHandler { get; set; }

/// <summary>
/// This value controls whether the <see cref="ExceptionHandlerMiddleware" /> should
/// consider a response with a 404 status code to be a valid result of executing the
/// <see cref="ExceptionHandler"/>. The default value is false and the middleware will
/// consider 404 status codes to be an error on the server and will therefore rethrow
/// the original exception.
/// </summary>
public bool AllowStatusCode404Response { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -542,5 +542,59 @@ public async Task ExceptionHandlerNotFound_RethrowsOriginalError()
&& w.EventId == 4
&& w.Message == "No exception handler was found, rethrowing original exception.");
}

[Fact]
public async Task ExceptionHandler_CanReturn404Responses_WhenAllowed()
{
var sink = new TestSink(TestSink.EnableWithTypeName<ExceptionHandlerMiddleware>);
var loggerFactory = new TestLoggerFactory(sink, enabled: true);

using var host = new HostBuilder()
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder
.UseTestServer()
.ConfigureServices(services =>
{
services.AddSingleton<ILoggerFactory>(loggerFactory);
services.Configure<ExceptionHandlerOptions>(options =>
{
options.AllowStatusCode404Response = true;
options.ExceptionHandler = httpContext =>
{
httpContext.Response.StatusCode = StatusCodes.Status404NotFound;
return Task.CompletedTask;
};
});
})
.Configure(app =>
{
app.UseExceptionHandler();

app.Map("/throw", (innerAppBuilder) =>
{
innerAppBuilder.Run(httpContext =>
{
throw new InvalidOperationException("Something bad happened.");
});
});
});
}).Build();

await host.StartAsync();

using (var server = host.GetTestServer())
{
var client = server.CreateClient();
var response = await client.GetAsync("throw");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
Assert.Equal(string.Empty, await response.Content.ReadAsStringAsync());
}

Assert.DoesNotContain(sink.Writes, w =>
w.LogLevel == LogLevel.Warning
&& w.EventId == 4
&& w.Message == "No exception handler was found, rethrowing original exception.");
}
}
}