Automatically handle exceptions in .NET Core API

Image for post
Image for post

If you develop APIs in .NET Core, I’m sure this is quite familiar to you:

[HttpGet("{id}")]
public IActionResult Get(int id)
{
var book = _books.GetById(id);

if (book == null) return NotFound();

return Ok(book);
}

Is there anything particularly wrong with this code? Not really, just a normal Action which fetches a book and returns it if it has been found.

However, you see the if-statement which checks if the book was found in order to return the appropriate status code? Wouldn’t it be great to take that out of the controller? It is cluttering up the Action and causes us to repeat ourselves constantly, especially in a larger application. If we want to log this as well, even more space will be taken.

We can’t, however, move this code to another layer such as Business unless the business layer actually returns an IActionResult, which is ridiculous. What if we could simply throw an Exception in one of our application’s layers and have MVC handle this for us instead?

So how do we extract this from our controller and at the same time doesn’t require us to repeat ourselves? The answer is Action Filters. More specifically, a custom implementation of ExceptionFilterAttribute found in Microsoft.AspNetCore.Mvc.Filters.

Filters are a way to run code before or after certain stages of the request pipeline. An example of this is the [Authorize] attribute which checks if a user is authorized.

As mentioned above, in order to run code every time an Exception is thrown, we’ll have to create a custom ExceptionFilterAttribute.

Let’s quickly set up a basic example.
We have a single API project called ActionFilters.Api

There is an entity Book:

public class Book
{
public int Id { get; set; }
public string Name { get; set; }
public decimal RetailPrice { get; set; }
}

And a BookService inheriting from IBookService :

This is all we need so far. Now, the GetById(int) method in BookService seems like a good place to check if a book was found and throwing an exception if it wasn’t. Let’s create an exception specifically for when an Entity was not found:

And we can do the check inside GetById :

That’s all we need to do here, we’ll handle this exception elsewhere.

Derived types of this attribute have to override either OnException(ExceptionContext) or OnExceptionAsync(ExceptionContext) but not both.

In case you’re new to writing attributes, [AttributeUsage] indicates where we can use a certain attribute, in this case, we only want to use it on Controllers (classes) and Actions (methods).

So our goal is to return an appropriate status code based on what went wrong. We know that if the thrown exception is of type EntityNotFoundException, we need to return a status code 404 Not Found, otherwise, it will default to 500 Internal Server Error.

This is rather simple:

Now, whenever an exception is thrown and it is of type EntityNotFoundException, we change the Response’s Status Code to 404.

We can extend this by returning the exception message and/or the stack trace.

Great! Now all that remains is telling MVC about the filter.

Inside Startup.cs in the ConfigureServices method, add the filter like this:

Done! That’s all we need to do to get this to work!
Now, starting the API and making a GET request to /api/books/1 returns this:

{
"id": 1,
"name": "Book 1",
"retailPrice": 19.99
}

And making a request to /api/books/3 returns this:

You can play around with how exactly it handles the exceptions, of course.

Thank you very much for reading this post.
There’s a repository available on Gitlab here containing the source code.
Clone: git clone https://gitlab.com/jelleverheyen/actionfilters-example.git

Feel free to check out my other stories!

Written by

.NET enthusiast

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store