Filters in .NET offer a great way to hook into the MVC action invocation pipeline. Therefore, we can use filters to extract code which can be reused and make our actions cleaner and maintainable. There are some filters that are already provided by .NET like the authorization filter, and there are the custom ones that we can create ourselves.

There are different filter types:

  • Authorization filters – They run first to determine whether a user is authorized for the current request
  • Resource filters – They run right after the authorization filters and are very useful for caching and performance
  • Action filters – They run right before and after the action method execution
  • Exception filters – They are used to handle exceptions before the response body is populated
  • Result filters – They run before and after the execution of the action methods result.

In this article, we are going to talk about Action filters and how to use them to create a cleaner and reusable code in our Web API’s.

To download the source code for our starting project, you can check out start-project-branch.

For the finished project refer to end-project-branch

We have divided this article into following sections:

Action Filters Implementation

To create an Acton filter, we need to create a class that inherits either from the IActionFilter interface or IAsyncActionFilter interface or from the ActionFilterAttribute class which is the implementation of the IActionFilter, IAsyncActionFilter, and few different interfaces as well:

In our examples, we are going to inherit from the IActionFIlter interface because it has all the method definitions we require.

To implement the synchronous Action filter that runs before and after action method execution, we need to implement OnActionExecuting and OnActionExecuted methods:

We can do the same thing with an asynchronous filter by inheriting from IAsyncActionFilter, but we only have one method to implement the OnActionExecutionAsync:

The Scope of Action Filters

Like the other types of filters, the action filter can be added to different scope levels: Global, Action, Controller.

If we want to use our filter globally, we need to register it inside the AddMvc() method in the ConfigureServices method:

But if we want to use our filter as a service type on the Action or Controller level, we need to register it in the same ConfigureServices method but as a service in the IoC container:

Finally, to use a filter registered on the Action or Controller level, we need to place it on top of the Controller or Action as a ServiceType:

Order of Invocation

The order in which our filters are executed is as follows:

Order of execution of filters - Action Filters

Of course, we can change the order of invocation by adding an additional property Order to the invocation statement:

Or something like this on top of the same action:

Improving the Code with Action Filters

If we open the starting project from the AppStart folder from our repository, we can find the MoveController class in the Controllers folder. This controller has an implementation for all the CRUD operations. For the sake of simplicity, we haven’t used any additional layers for our API. This project also implements global error handling so if you are not familiar with that topic, we suggest you read Global Exception Handling in .NET Core Web API.

Our actions are quite clean and readable without try-catch blocks due to global exception handling, but we can improve them even further.

The important thing to notice is that our Movie model inherits from the IEntity interface:

So let’s start with the validation code from the POST and PUT actions.

Validation with Action Filters

If we look at our POST and PUT actions, we can notice the repeated code in which we validate our Movie model:

We can extract that code into a custom Action Filter class, thus making this code reusable and the action cleaner.

So let’s do that.

Let’s create a new folder in our solution explorer, and name it ActionFilters. Then inside that folder, we are going to create a new class ValidationFilterAttribute:

Now we are going to modify the OnActionExecuting method to validate our model:

Next, let’s register this action filter in the ConfigureServices method:

Finally, let’s remove that validation code from our actions and call this action filter as a service:

Excellent.

This code is much cleaner and more readable now without the validation part. And furthermore, the validation part is now reusable as long as our model classes inherit from the IEntity interface, which is a quite common behavior.

If we send a POST request for example with the invalid model we will get the BadRequest response:

Action Filters request

Dependency Injection in Action Filters

If we take a look at our GetById, POST and PUT actions, we are going to see the code where we fetch the move by id from the database and check if it exists:

That’s something we can extract to the Action Filter class as well, thus making it reusable in all the actions.

Of course, we need to inject our context in a new ActionFilter class by using dependency injection.

So, let’s create another Action Filter class ValidateEntityExistsAttribute in the ActionFilters folder and modify it:

We’ve created this Action Filter class to be generic so that we could reuse it for any model in our project. Furthermore, if we find the entity in the database, we store it in HttpContext because we need that entity in our action methods and we don’t want to query the database two times (we would lose more than we gain if we double that action).

Now let’s register it:

And let’s modify our actions:

Awesome.

Now our actions look great without code repetition, try-catch blocks or additional fetch request towards the database.

Conclusion

Thank you for reading this article. We hope you have learned new useful things.

As we already said, we always recommend using Action Filters because they give us reusability in our code and cleaner code in our actions as well.

If you have enjoyed reading this article and if you would like to receive the notifications about the freshly published Web Development content we encourage you to subscribe to our blog.