Skip to content

Commit 8555002

Browse files
author
John Luo
authored
Add an option to allow 404 responses from ExceptionHandlers (#26567)
1 parent d5b5f08 commit 8555002

File tree

3 files changed

+64
-1
lines changed

3 files changed

+64
-1
lines changed

src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ private async Task HandleException(HttpContext context, ExceptionDispatchInfo ed
132132

133133
await _options.ExceptionHandler(context);
134134

135-
if (context.Response.StatusCode != StatusCodes.Status404NotFound)
135+
if (context.Response.StatusCode != StatusCodes.Status404NotFound || _options.AllowStatusCode404Response)
136136
{
137137
if (_diagnosticListener.IsEnabled() && _diagnosticListener.IsEnabled("Microsoft.AspNetCore.Diagnostics.HandledException"))
138138
{

src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerOptions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,14 @@ public class ExceptionHandlerOptions
2222
/// explicitly provided, the subsequent middleware pipeline will be used by default.
2323
/// </summary>
2424
public RequestDelegate ExceptionHandler { get; set; }
25+
26+
/// <summary>
27+
/// This value controls whether the <see cref="ExceptionHandlerMiddleware" /> should
28+
/// consider a response with a 404 status code to be a valid result of executing the
29+
/// <see cref="ExceptionHandler"/>. The default value is false and the middleware will
30+
/// consider 404 status codes to be an error on the server and will therefore rethrow
31+
/// the original exception.
32+
/// </summary>
33+
public bool AllowStatusCode404Response { get; set; }
2534
}
2635
}

src/Middleware/Diagnostics/test/UnitTests/ExceptionHandlerTest.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,5 +542,59 @@ public async Task ExceptionHandlerNotFound_RethrowsOriginalError()
542542
&& w.EventId == 4
543543
&& w.Message == "No exception handler was found, rethrowing original exception.");
544544
}
545+
546+
[Fact]
547+
public async Task ExceptionHandler_CanReturn404Responses_WhenAllowed()
548+
{
549+
var sink = new TestSink(TestSink.EnableWithTypeName<ExceptionHandlerMiddleware>);
550+
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
551+
552+
using var host = new HostBuilder()
553+
.ConfigureWebHost(webHostBuilder =>
554+
{
555+
webHostBuilder
556+
.UseTestServer()
557+
.ConfigureServices(services =>
558+
{
559+
services.AddSingleton<ILoggerFactory>(loggerFactory);
560+
services.Configure<ExceptionHandlerOptions>(options =>
561+
{
562+
options.AllowStatusCode404Response = true;
563+
options.ExceptionHandler = httpContext =>
564+
{
565+
httpContext.Response.StatusCode = StatusCodes.Status404NotFound;
566+
return Task.CompletedTask;
567+
};
568+
});
569+
})
570+
.Configure(app =>
571+
{
572+
app.UseExceptionHandler();
573+
574+
app.Map("/throw", (innerAppBuilder) =>
575+
{
576+
innerAppBuilder.Run(httpContext =>
577+
{
578+
throw new InvalidOperationException("Something bad happened.");
579+
});
580+
});
581+
});
582+
}).Build();
583+
584+
await host.StartAsync();
585+
586+
using (var server = host.GetTestServer())
587+
{
588+
var client = server.CreateClient();
589+
var response = await client.GetAsync("throw");
590+
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
591+
Assert.Equal(string.Empty, await response.Content.ReadAsStringAsync());
592+
}
593+
594+
Assert.DoesNotContain(sink.Writes, w =>
595+
w.LogLevel == LogLevel.Warning
596+
&& w.EventId == 4
597+
&& w.Message == "No exception handler was found, rethrowing original exception.");
598+
}
545599
}
546600
}

0 commit comments

Comments
 (0)