Middleware exception handling with ASP.NET Core
16 April 2023
First, let's create our middleware class, inherited from IMiddleware
. Idea is to write some exception details when error occures and little bit more details in special circumstances, in our example when users is logged in. For output details we'll use ProblemDetails class.
Middlewares/GlobalExceptionMiddleware.cs:
using LibraryServices;
using Microsoft.AspNetCore.Mvc;
using System.Net;
using System.Text.Json;
namespace Project.Middlewares
{
public class GlobalExceptionMiddleware : IMiddleware
{
private readonly ILogger<GlobalExceptionMiddleware> _logger;
private readonly UserManager _userManager;
public GlobalExceptionMiddleware(ILogger<GlobalExceptionMiddleware> logger, UserManager userManager)
{
_logger = logger;
_userManager = userManager; // this is something yours
}
public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
{
try
{
await next(ctx);
}
catch (Exception e)
{
_logger.LogError(e, e.Message);
ctx.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
ProblemDetails problem = new()
{
Status = (int)HttpStatusCode.InternalServerError,
Type = "Server error",
Title = "Server error",
Detail = "Error occured, you don't have permission for more details."
};
// So you are someone we can trust? Have some more information:
if (_userManager.GetCurrentUser() != null)
problem.Detail = e.Message;
string json = JsonSerializer.Serialize(problem);
ctx.Response.ContentType = "application/json";
await ctx.Response.WriteAsync(json);
}
}
}
}
We also need to add some extra lines of code inside Program.cs
. Add our custom middleware to services and finally use our middleware:
Program.cs:
//...
var builder = WebApplication.CreateBuilder(args);
//...
builder.Services.AddRazorPages().AddRazorRuntimeCompilation();
builder.Services.AddControllersWithViews();
builder.Services.AddTransient<GlobalExceptionMiddleware>(); // here we go #1
//...
var app = builder.Build();
//...
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseMiddleware<GlobalExceptionMiddleware>(); // here we go #2
And that's it, after that you can test it by forcing error on some of your route, you should get responnse something like this:
{
"type": "Server error",
"title": "Server error",
"status": 500,
"detail": "The method or operation is not implemented."
}
It would be wise to log this error in some database table and/or send mail to somebody responsible for your application (you?).