diff --git a/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs b/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs
index d954e00a44a2..7be8b56869c2 100644
--- a/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs
+++ b/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs
@@ -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"))
{
diff --git a/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerOptions.cs b/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerOptions.cs
index e5971ecd777d..370c6e419d60 100644
--- a/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerOptions.cs
+++ b/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerOptions.cs
@@ -22,5 +22,14 @@ public class ExceptionHandlerOptions
/// explicitly provided, the subsequent middleware pipeline will be used by default.
///
public RequestDelegate ExceptionHandler { get; set; }
+
+ ///
+ /// This value controls whether the should
+ /// consider a response with a 404 status code to be a valid result of executing the
+ /// . 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.
+ ///
+ public bool AllowStatusCode404Response { get; set; }
}
}
diff --git a/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerTest.cs b/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerTest.cs
index 1b8df0af5344..8c33d4a8a1e3 100644
--- a/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerTest.cs
+++ b/src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerTest.cs
@@ -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);
+ var loggerFactory = new TestLoggerFactory(sink, enabled: true);
+
+ using var host = new HostBuilder()
+ .ConfigureWebHost(webHostBuilder =>
+ {
+ webHostBuilder
+ .UseTestServer()
+ .ConfigureServices(services =>
+ {
+ services.AddSingleton(loggerFactory);
+ services.Configure(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.");
+ }
}
}