Global Exception Handling using Middleware in .NET Core
Exception handling for some of us is all about using try and catch blocks and throw statements in the application. Most of the time proper analysis or design approach never gets identified for the exception handling. Today we will see how to enable Global Exception Handling using Middleware in .NET Core-based applications like API or MVC apps.
Today in this article, we will cover below aspects,
In our previous article, we already talked about the below aspects in detail which are important while considering proper design around exception handling,
- Business exception Vs Technical Exception
- Exception handling
- Using Global Exception-handler in .NET Core
- Using Exception Filters in .NET Core
- Using simple Try -Catch-Throw best practices
In this article, we will see another preferred approach of using a global middleware component for handling exceptions in the ASP.NET Core application.
Let’s first understand the middleware’s role in the API pipeline. As shown in the below figure each middleware component in the request pipeline is responsible for invoking the next middleware component in the pipeline.
Now let’s use this concept and if we add first middleware as our own exception middleware component shouldn’t it suffice as our global exception handler? The answer is Yes. This component will handle all exceptions happening across API or Business or any other component including other middleware which are part of the application.
Getting started
Create an ASP.NET Core API
Please choose either .NET Core 3.1 or .NET 5 project template.
Overall creating a middleware component is just a 2-3 steps process.
Adding Middleware component
- The first step is to add a public class as ExceptionMiddleware.
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
public ExceptionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
await HandleGlobalExceptionAsync(httpContext, ex);
}
}
private static Task HandleGlobalExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(new GlobalErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = "Something went wrong !Internal Server Error"
}.ToString());
}
}
- The next step is to create a public static class GlobalExceptionMiddleware
public static class GlobalExceptionMiddleware
{
public static void UseGlobalExceptionMiddleware(this IApplicationBuilder app)
{
app.UseMiddleware<ExceptionMiddleware>();
}
}
- The last step is to enable this middleware in Startup.cs as shown below.
Generic Exception Code and Message
Now let’s test controller logic with some exceptions. Error response for most exceptions will be generic as below,
Tips:
As a good practice, the above-mentioned logic can be centralized in the form of a library/Nuget package. This library can be shared across any project so that there is uniformity across error handling.
- It’s very crucial to understand how to try -catch-throws work. Follow best practices on throw Vs throw-ex statements here Understanding throw vs throw-ex with examples.
- Also, try to throw an exception from other layers of your application like the BAL/Domain layer and make only the highest layer of your application swallow it. A throw will keep your stack trace details intact allowing you to log exception details.
Other references :
Using Global Exception Handler for ASP.NET Core exception handler
Logging exceptions in the Global Middleware component
Do you have any comments or ideas or any better suggestions to share?
Please sound off your comments below.
Happy Coding !!
Summary
Today we learned about global exception handling using the middleware approach in .NET Core. Centralize exception handling using the middleware approach brings uniformity across, how exceptions are handled in the applications. Proper exception handling /error handling helps in resolving the business/technical issues and is very critical for business success.
Please bookmark this page and share it with your friends. Please Subscribe to the blog to receive notifications on freshly published(2024) best practices and guidelines for software design and development.
Might be useful to note that this does not work if the application has an IExceptionFilter setup. The IExceptionFilter will consume the exception before it gets to the middleware and it will not be tracked.
Thanks, Stephen for that note. If any other layer absorbs the exception then the global exception will not have access to those exceptions.
Also not that IException filters work on Controller/API route layers, it doesn’t catch the exception thrown outside of that like other middleware/module,s etc…. in such cases Global exception will catch all those exceptions if implemented…
Thanks for the article. Just an FYI:
You have a typo in the static class method: UseGloablExceptionMiddleware(this IApplicationBuilder)
Global is misspelled.
Thanks Kurt for the comments.Appreciate it. I shall update the typo soon.
Thanks, how can we use serilog or other logger along with this.Can other logger work along with it as you overidding the and adding to console?
Hello Mos,
Thanks for your query. In the sample, however, I have created a new console logger object with an idea of the global logger taking care of logging concerns. But you can inject ILogger from Pipeline configured using Serilog provider and through DI use it in any layer of your application. If interested on how it will work, please check my articles on the same,
https://thecodebuzz.com/file-logging-in-net-core-2-2-and-net-core-3-0/
Thanks again.
Thanks for above details .I liked it. The only suggestion would be to explain more in details the logic of actual middleware component.
Thanks, Pat for your feedback.Appreciate that. Sure I shall do that as soon as I can.