Exception handling middleware in .NET Core
No one can write bug-free code and in my mind, you should have a centralized place in which you catch globally your exceptions and return a proper custom response to your clients. For example, you can think about your domain exceptions, you maybe want to log them with their stack trace but the clients shouldn’t know what happened internally in your system; the same if something broke inside your system, a service registration which wasn’t made or a null object; both cases you should log exactly what happened (for finding the issue easily), but your clients should get more than “An error has occurred” and a 500 status code.
To solve this, I propose to use an exception handler middleware. It is pretty easy to implement as you can see below:
Step1: Create a return view model (if you use swagger you’ll have the structure of your return model)
public class ErrorViewModel{ public string ErrorReason { get; set; }}
Step2: Create the middleware
public class ExceptionHandlingMiddleware{private readonly RequestDelegate _next;public ExceptionHandlingMiddleware(RequestDelegate next){_next = next;}public async Task InvokeAsync(HttpContext httpContext){try{await _next(httpContext);}catch (Exception ex){Log.Error($"My Test Api exception: {ex}");await HandleExceptionAsync(httpContext);}}
private static async Task HandleExceptionAsync(HttpContext context){context.Response.ContentType = "application/json";context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;await context.Response.WriteAsync(JsonConvert.SerializeObject(new ErrorViewModel{ErrorReason = "Internal Server Error from My Test Api.",}));}}
As you can see, for any uncaught exceptions, we will first log the exception and after that, we will return a response with an ErrorViewModel with ErrorReason inside populated with “Internal Server Error from My Test Api.” and a 500 (InternalServerError) status code, on the response and it will have the content type “application/json”. Of course, if you throw other custom exceptions you can catch them here also and return different things (the sky is the limit).
Step 3: Registration.
For this, I prefer to create an extension:
public static class ExceptionHandlingMiddlewareExtensions{public static IApplicationBuilder UseExceptionHandlingMiddleware(this IApplicationBuilder builder){return builder.UseMiddleware<ExceptionHandlingMiddleware>();}}
And the registration is pretty straightforward:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env){[...]
app.UseExceptionHandlingMiddleware();
[...]
}