We all know the traditional try-catch blocks and -used correctly- there is of course nothing wrong with them! But even though all is good with that, ASP.NET Core has an even better way of doing things, two ways actually, that can make our code cleaner and easier to read! By following the “Middleware” approach, we extract all our custom exception handling code from within the actions and centralizing it in one place thus, cleaner code!
In this post we will explore these three cases, and starting from the simple try-catch block we will refactor towards a custom error handling middleware.
Let’s assume the following action block:
One thing we can notice here, is that the only reason we have the try-catch block is the logging logic: we need to log the exception. That means that if we had a centralized logging system that captures all exceptions, the whole action would be just two lines of perfectly readable code!
A second thing we can easily spot, is that even during development the exception is not thrown for the developer to debug. And we developers love exceptions! We see one, we know where to search for the problem!
In a more general quote, hiding exceptions has a huge impact on how maintainable a project is. If needed, catch only the specific exception type, all other cases fail-fast not fail-safe!
A solution of course, would be to just add a condition and re-throw if we are in a development environment, but imagine the amount of repeated code we would have in all our actions…!
There is a build in middleware that we can add in the pipeline and configure it to do all the exception handling work for us. This middleware will catch exceptions, log them, and re-execute the request in an alternate pipeline (note that the request will not be re-executed if the response has already started.)
Let’s check it:
This solution works great actually! We could even tidy up things a little bit by creating an extension method for
IApplicationBuilder and migrate all the code to a different file. For full control though, we need to create a custom exception middleware, that can help us build a much more sophisticated logging system.
And here we are, last but not least, the “custom middleware” approach! Check for example the code below of the simplest possible middleware:
A detailed guide on how to handle exceptions with a middleware can be found here: https://blog.georgekosmidis.net/2018/07/24/handling-serializing-and-returning-exceptions-with-a-middleware-in-asp-net-core-api/
All we need to do now, is add it in the pipeline and it will capture all unexpected exceptions in our code. And although there are many things we can add (things that you could read in details here), the most important is to make sure we will not expose sensitive information to the end user by returning exception messages. The safest way to achieve this, is by not returning anything at all, just log the details but I guess the best of both worlds would be to check if we are in a development environment and return the actual message instead of “
Internal Server Error“. Check for example the
There are many more things to do with our custom middleware! For example we could loop through inner exceptions and collect all messages, serialize stack trace or even capture only specific exceptions. In any case if you are interested in a more detailed approach on a custom error handling middleware, just read my blog post on how to Handle, serialize and return exceptions with a middleware in an ASP.NET Core API