Description
The default error handler for ExceptionHandlerMiddleware
always sets the response status code to 500. It is not uncommon for BadHttpRequestException
to be thrown by requests though which detail a different (usually 4xx) status code but this will get masked by the default error handler.
While it's possible to customize the error handler via setting ExceptionHandlerOptions.ExceptionHandler
, one has to reimplement the default feature logic for returning JSON Problem Details responses manually which is quite a bit of code, e.g.:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails(options =>
{
options.CustomizeProblemDetails = static ctx =>
ctx.ProblemDetails.Extensions["requestId"] = Activity.Current?.Id ?? ctx.HttpContext.TraceIdentifier;
});
var app = builder.Builder();
if (!app.Environment.IsDevelopment())
{
// Error handling
app.UseExceptionHandler(new ExceptionHandlerOptions
{
AllowStatusCode404Response = true,
ExceptionHandler = async (HttpContext context) =>
{
// Pass-through status codes from BadHttpRequestException
var exceptionHandlerFeature = context.Features.Get<IExceptionHandlerFeature>();
var error = exceptionHandlerFeature?.Error;
if (error is BadHttpRequestException badRequestEx)
{
context.Response.StatusCode = badRequestEx.StatusCode;
}
if (context.RequestServices.GetRequiredService<IProblemDetailsService>() is { } problemDetailsService)
{
await problemDetailsService.WriteAsync(new()
{
HttpContext = context,
AdditionalMetadata = exceptionHandlerFeature?.Endpoint?.Metadata,
ProblemDetails = { Status = context.Response.StatusCode }
});
}
else if (ReasonPhrases.GetReasonPhrase(context.Response.StatusCode) is { } reasonPhrase)
{
await context.Response.WriteAsync(reasonPhrase);
}
}
});
}
app.MapGet("/throw/{statusCode?}", (int? statusCode) =>
{
throw statusCode switch
{
>= 400 and < 500 => new BadHttpRequestException(
$"{statusCode} {ReasonPhrases.GetReasonPhrase(statusCode.Value)}",
statusCode.Value),
_ => new Exception("uh oh")
};
});
app.Run();
We should consider adding support for passing through the response status code from BadHttpRequestException
in the default error handler. We can consider adding a boolean property to ExceptionHandlerOptions
to control this behavior if necessary.