Exception handling is one of most important but irritating jobs for developers. There are tons of articles about the importance of exception handling. Fortunately, ASP.NET Core application has got a lot of improvement for exception handling through the request/response pipeline. In this article, we’re going to have a brief look how we can handle exceptions.
Sample application can be found here: https://github.com/devkimchi/ASP.NET-Core-Tips-and-Tricks-Sample
Global Exception Filter – OWIN Pipeline
Unlike ASP.NET MVC applications, ASP.NET Core applications only use OWIN pipelines to handle requests and responses. In order to handle exceptions, UseExceptionHandler()
middleware extension is provided out of the box. Therefore, it can be easily implemented like this:
However, if you want to do more control, creating a custom exception filter class would be recommended. The following code is a simple GlobalExceptionFilter
class:
Microsoft.AspNet.Mvc
package provides an interface called IExceptionFilter
. It declares the OnException()
method so the GlobalExceptionHandler
implements it. As this class accepts the ILoggerFactory
instance as its constructor parameter, we can write logs when an exception is thrown. It is implemented with log4Net
, NLog
and ApplicationInsights
libraries. You can choose anything in your taste.
So, the GlobalExceptionFilter
class can be resolved within the ConfigureServices()
method on Startup.cs
like:
Then, raise an exception within a controller and you will be able to see the error log like this:
Exception Handling outside OWIN Pipeline
As stated above, the GlobalExceptionFilter
only works within the OWIN pipeline. How about handling outside OWIN pipeline? There are only three places to handle exceptions outside OWIN pipeline – Startup()
constructor, ConfigureServices()
method and Configure()
method. Let’s have a look at the code below:
This is how the Configure()
method handles exceptions. Note that this is the only place where we can handle exceptions. In other words, the other two places, Startup()
and ConfigureServices()
can throw exceptions but can’t handle them. It is because of the IApplicationBuilder.Run()
method. This method takes HttpContext
instance to write responses. Hence, using try...catch
block catches any exception and writes response messages back to browsers.
One question still remains. How can the other two places handle exceptions? Here’s a trick for them:
All codes should be surrounded by try...catch
block. If any exception is thrown, the _exceptions
field having type of Dictionary<string, List<Exception>> stores the exception. If any exception is thrown from Startup()
or ConfigureServices()
, this is handled within the Configure()
method like above.
Now, we can handle all exceptions inside/outside OWIN pipeline. As this is just an example, if you apply this approach into your production code, you need to carefully review and do a bit of refactoring.
Fantastic tip! Thanks for your article.
Great article! Normally I use IExceptionFilter and a middleware mix. The filter handles API errors to return JSON data depending on exception type, and the middleware for more “low level” exceptions.
As a hint, if someone need to make exception handling based on exception type, feel free to use a small library I made to make it more “legible”: https://medium.com/@nogravity00/asp-net-core-mvc-and-exception-handling-f0da1c820d4a
@joão I like your SimpleExceptionHandling package! Thanks for your comment!
This is crashing Kestrel on Dotnet Core 1.1
@boyner Thanks for the comment.
The codebase used in this post is .NET Core RC1. As you know, RC1, and RC2 and beyond has far different from each other. I’ll have a check and update this post accordingly.
The only point that these solutions doesn’t resolve are the exceptions raised in threads outside the Owin pipeline and outside all of that methods (configure,startup,…)
There was an issue https://github.com/dotnet/corefx/issues/6398 where I was hoping to find the solution (“….Should be raised for all threads…..”) but I couldn’t see any new feature added.
@Xisco Thanks for your comment!
You’re right. The code written in this post covers the OWIN pipeline and methods right outside the OWIN pipeline such as Startup, Configure, ConfigureService, etc.
By the way, how many chances are you anticipating to handle errors outside those pipeline and methods?
We’ve got threads that run async , initialized in the startup, the only chance I see is to pass to those threads the ILogger and log the exception in a try-catch. I’m investigating some kind of middleware exception handler that should have a reference to some shared action between those threads…
For the record net core 1.1 is crashing on FreeBSD when it comes to filters and exception logging.
Thanks for the head up!