Reputation: 4640
I have the following code in my ASP.NET Core application to handle errors:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(builder =>
{
builder.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var loggingService = context.RequestServices.GetService<ILoggingService>();
var exceptionFeature = context.Features.Get<IExceptionHandlerFeature>();
if (exceptionFeature != null)
{
Exception ex = exceptionFeature.Error;
Console.Error.WriteLine(ex.Message + " " + ex.StackTrace);
loggingService.Error(ex);
}
});
});
}
However, when being in a Development environment, I would like to use both the UseDeveloperExceptionPage()
and the custom error handling for DB logging. But it seems that this does not work (the UseExceptionHandler()
disables the functionality of UseDeveloperExceptionPage()
).
Is there any way to achieve this, without writing the UseDeveloperExceptionPage()
code form scratch?
Upvotes: 9
Views: 5546
Reputation: 29996
The built-in UseDeveloperExceptionPage
does not expose any extension method to customize the error process. You could try implement your own middleware to log the error like:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
};
app.Use(async (context, next) =>
{
try
{
await next.Invoke();
}
catch (Exception ex)
{
//var loggingService = context.RequestServices.GetService<ILoggingService>();
//loggingService.Error(ex);
Console.Error.WriteLine(ex.Message + " My " + ex.StackTrace);
throw;
}
});
Upvotes: 4
Reputation: 27609
I was struggling with this and couldn't find any documentation on the subject however from looking at the source code I did find there is a way to add some logging into the Developer ExceptionPage. The source code is here and I will make some references to parts of it: https://github.com/dotnet/aspnetcore/blob/master/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs
The part we care about is this:
_exceptionHandler = DisplayException;
foreach (var filter in filters.Reverse())
{
var nextFilter = _exceptionHandler;
_exceptionHandler = errorContext => filter.HandleExceptionAsync(errorContext, nextFilter);
}
Essentially it will take a set of filters and put them into a pipeline before the default DisplayExecution is run.
The filters are passed in as IEnumerable<IDeveloperPageExceptionFilter>
by DI which means all we need to do is add our own class that implements IDeveloperPageExceptionFilter
and it will be executed. Here is an example filter from my testing of this:
public class LoggingDeveloperPageExceptionFilter : IDeveloperPageExceptionFilter
{
private readonly ILogService _logService;
public LoggingDeveloperPageExceptionFilter(ILogService logService)
{
_logService = logService;
}
public Task HandleExceptionAsync(ErrorContext errorContext, Func<ErrorContext, Task> next)
{
_logService.Error(errorContext.Exception);
return next(errorContext);
}
}
And this is registered in my startup.cs as such:
services.AddSingleton<IDeveloperPageExceptionFilter, LoggingDeveloperPageExceptionFilter>();
So in this case it will call my error logging in log service passing the exception in and then call the next item in the processing chain. Since I only have one next
for me will be the original DisplayException
which does all our nice pretty error output.
Upvotes: 4