For trying the new feature IExceptionHandler of ASP.NET Core 8 based on the article: (Milan Jovanovic) Global Error Handling in ASP.NET Core 8.
I have the following code, and the context.GetRouteData() always return null.
Is it the bug of IExceptionHandler and app.UseExceptionHandler(); ?
I want to log the controller name and action name when exception occurs.
I’ve found the Program.cs call UseExceptionHandler() twice:
(1) app.UseExceptionHandler("/Home/Error");
(2) app.UseExceptionHandler();
But even comment the first app.UseExceptionHandler("/Home/Error"); , still cannot get the routeData.
I've tried the custom middleware (ExceptionHandlingMiddleware.cs) to catch exception, it can get the routeData.
the related code as follows:
GlobalExceptionHandler.cs
public class GlobalExceptionHandler : IExceptionHandler
{
private readonly ILogger<GlobalExceptionHandler> _logger;
public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
{
_logger = logger;
}
public async ValueTask<bool> TryHandleAsync(
HttpContext context,
Exception exception,
CancellationToken cancellationToken)
{
// !!! the routeData is always null !!!
var routeData = context.GetRouteData();
var controllerName = routeData?.Values["controller"]?.ToString();
var actionName = routeData?.Values["action"]?.ToString();
_logger.LogInformation("Controller={controller} Action={action}", controllerName, actionName);
var response = new ProblemDetails()
{
Status = 500,
Title = "Internal Server Error",
Detail = "occurs Internal Server Error, please contact MIS",
Instance = context.Request.Path,
};
//
await context.Response
.WriteAsJsonAsync(response, cancellationToken);
return true;
}
}
Program.cs
using ExceptionHandler01.Middlewares;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
#region (jasper) register GlobalExceptionHandler and ProblemDeatils to DI
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
builder.Services.AddProblemDetails();
#endregion
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
//// (jasper) use custom exception handler
//app.UseMiddleware<ExceptionHandlingMiddleware>();
// (jasper) use default exception handler
app.UseExceptionHandler();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
After debug the source code, I found we can't get route data in IExceptionHandler
. It's by design.
We can find line 143
in ExceptionHandlerMiddlewareImpl.cs
file, in github is line 164. In ClearHttpContext
method, it set the RouteValues
to null. This is the root cause for this issue.
If you are interesting in debug source code, you can check the official document.
How I debug it
Create a custom middleware and watch the context value.
You can try using the code below to get routing information from that location even if an exception occurs.
...
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
var routeData = context.Features.Get<IRoutingFeature>()?.RouteData;
if (routeData != null)
{
context.Items["controller"] = routeData.Values["controller"];
context.Items["action"] = routeData.Values["action"];
}
// handling the exception
});
});
app.UseHsts();
}
else
{
app.UseDeveloperExceptionPage();
}
...
app.UseRouting();
// capture route data
app.Use(async (context, next) =>
{
await next();
// You can save route data in HttpContext.Items here
});
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();