How to Avoid Duplicate 404 Code in .NET Core APIs - Part 1

How to Avoid Duplicate 404 Code in .NET Core APIs - Part 1

The Bad

Have you ever started creating an API, and by the second or third controller your code starts to look a lot like this?

library_books_controller.png

The library returning books is not exactly Restful, but for the sake of this article we really just are focused on the top level entities and checking for NotFound

That is a lot of code checking for not found entities! What's worse is it will only balloon over time, since we will have to check that the entities exist when doing a POST and a DELETE. We also may need to check that these entities do not exist in other controllers (think about our BooksController).

The Really Bad

The controller code is bad as we are duplicating the same code all over the various controllers; checking for null entities, but what about our unit tests? Duplicating code leads to duplicated tests, and as great as unit tests are this gets pretty messy pretty quickly. If we have to change any of our logic for checking entities not found we now have to update all the necessary controllers AND the unit tests! There just has to be a better way than this, and luckily there is!

The Solution

In .net core there is the concept of an ActionFilter (in our case we will be using the IAsyncActionFilter). With that in mind let's take a look at how we can solve this by creating a BookNotFound filter.

book_not_found.png

NOTE: we make an assumption that anytime we look for a book we pass in bookId for the name of the id. If we do not do this the code here will error.

Now we have to tie this together in our controller which we can do with the code below: book_controller_updated.png We can now use this code anywhere we are looking for a book in one of our controllers! We also only have to write one set of unit tests, which helps keep our controllers and unit tests looking extra clean.

What's Next?

There are still some issues and pieces we need to figure out here:

  • We still have to write a NotFound filter for each entity, but we can make this a bit easier since a lot of entities are retrieved the same way.
  • What if we want to just pass id instead of bookId in some places?
  • How do we actually write tests for the BookNotFoundFilter?
  • It's great that we are able to not write the NotFound code over and over, but we are now making two calls to our database every time we get a book in a controller.

Stay tuned for Part 2 where we will look at making a more generic NotFound filter! Hope this helps you keep that annoying 404 code to a minimum.