This post will cover cross cutting concerns with filters in AspNet core
I will provide a general overview what filters are and how they can be applied. For this we will look at the four (or as we later see five) categories of filters that aspnetcore offers.

What this post will cover

  • The different filter types
  • Why to use filters
  • How to apply and control filter execution

What this post will not cover

  • Detailed examples of filter types
  • Hybrid filter (Result + action filter)
  • Custom Filter implementation

Why use Filters?

So why would you want to use filters for your application? First of all it reduces code duplication and second it separates cross cutting concerns like profiling, Security or Authorization topics from the controller code into attributes on the controller or action method. This makes the code much cleaner, readable and testable.

For starters there are four filter categories:
1.) ExceptionFilters
2.) AuthorizationFilters
3.) ActionFilters
4.) ResultFilters

These filters are applied in this order from MVC when each of everyone is applied.

Each filter category does provides a different interface that you can implement to create your own filters. But for the most part you want to derive from the given attributes.
Each category also has specific context object with properties that allows access to the context in which the request is invoked currently.

Filters are also available in two flavors: synchronous filters and asynchronous filters.

Apply filters in aspnetcore controllers

To apply a Filter you would derive from a MVC standard filter or implement your own and decorate the action method or controller with an attribute of this filter.

Consider the following ActionFilter:

public class MyFilterAttribute : ActionFilterAttribute
{

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (context.Controller.GetType().Name == "MyForbiddenController")
        {
            context.Result = new BadRequestResult();
        }
    }
}

 

  1. Apply on action method
    [MyFilter]
    public Task<IActionResult> SomePost([FromBody] MyModel model)
    {
    ...
    }
  2. Apply on controller to apply on all action methods.

    [MyFilter]
    public class MyController : ControllerBase
    {
    
    }
  3. Apply global to apply on all controllers and therefore all action methods
    Add the filter to the services in the Startup class

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped<ValidateModelAttribute>();
        services.AddMvc().AddMvcOptions(options => {
        options.Filters.AddService(typeof(ValidateModelAttribute));
         });
    }
    

By applying a filter on the controller itself you apply the filter automatically to every action method of that controller. If you want to go a step further and apply a certain filter to all actions in your application, then you can apply a global filter.
This is done by registering the filter in the startup class of your application with your services. Any filter can be used as a global filter.

The aforementioned fifth category is a hybrid, it consists of action and Result filter methods, because it is so common that those are applied together.
Filters, as well as controllers are created anew for each individual request. (scope of the request). This can be changed via means of dependency injection as we will later in this post see.

Filters and Dependency Injection

Most of the time you will apply an Attribute, but this does not allow for DI into the attribute, if it needs some dependencies. Instead you can apply a filter itself with dependency injection.
imagine following filter:

public class MyDIFilter : ActionFilterAttribute
{
    private readonly IDependency _dependency;
    public MyDIFilter(IDependency dependency)
    {
        _dependency = dependency;
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // something with the dependency
    }
}

This cannot be used as an attribute directly, because the constructor needs a dependency. Yet you can apply the filter in another way by using the [TypeFilter(typeof(MyDIFilter))] attribute instead of the actual filter attribute implementation of yours. This then in turn enables DI. Simply apply this attribute on the controller/action instead of the attribute itself.

It is also possible to manage life cycle of filters. By default there is always a new instance created for each request like for controllers. But to apply a singleton or scoped style filter you need to use the ConfigureServices method in the Startup class to register your filter:

...
public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<MyFilter>();
}
...

Order in which fitlers are executed

Now that we know how to apply filters the question comes up in which sequence those filters are executed. The Order of execution is by default the following:

  1. Exception
  2. Authorization
  3. Action
  4. Result

But what happens if there are multiple filters of one kind? Which one das then execute first? This is determined by the scope the filter is applied to:

  1. Global
  2. Controller scoped
  3. Action method scoped
  4. any other action filter (topmost is executed first)When the response is generated the order is exactly opposite.

Customize order

You can also determine the order in which the filters are executed for each incoming request. For this the Filters implement the following interface:

namespace Microsoft.AspNetCore.Mvc.Filters
{
     public interface IOrderedFilter : IFilterMetadata 
     {
          int Order { get; }
     } 
}

So you can simply utilize it like this:
[MyFilter(Order= 1)]
class Controller : ControllerBase
{

    [MyFilter(Order=-1)]
    public void Action()
    {
    }
}

 

The lower the number the earlier it is executed. On the return of the request the lower the number the later it is executed. So in this example the filter are executed in this order:

(request)
MyFilter on ActionScope
MyFilter on ControllerScope
(response)
MyFilter on ControllerScope
MyFilter on ActionScope

 

Interesting to mention is that Global filters default to one.

Summary

In this post we looked at what filters can do for you in your asp net core application, how you can arrange them in different scenarios and how to fine tune control over the execution order.

In further posts we will look at the different implementations of the aspnetcore attributes.

Categories: AspNetCore

0 Comments

Leave a Reply

Avatar placeholder