Action filters, service filters and type filters in ASP.NET 5 and MVC 6

Today, let's have a look at he area of filters in ASP.NET MVC 6 – because it actually contains quite a few interesting changes compared to classic MVC and Web API filter pipelines.

Let's leave the specialized filters (error filters, authorization filters) on a side for now, and focus instead on the functional, aspect oriented, filters. Aside from the good old action filters, known from both MVC and from Web API, there are two new types of filters (or rather filter factories, but we’ll get there) that you can use – ServiceFilters and TypeFilters.

Action filters

Action filters in MVC 6 play the same role as they did in both old MVC and Web API – allow you to run some code before or after the action is executed. They are defined by the two interfaces below (sync and async):

Additionally there is a concept of a result filter (which did not existed in Web API, but should be familiar to MVC users), which allows you to run some code just before or just after the execution of the result of your action.

The default ActionFilterAttribute that you will be probably using most commonly implements all 4 of these interfaces.

At this point, if you have been looking carefully, you might have noticed one thing – none of the interfaces allow you to order your filters. In Web API, indeed, there was no support for filter ordering (which was a big issue if you ask me), but it has always been there in MVC.

MVC 6 still supports filter ordering – it's done through yet another interface IOrderedFilter, whose sole purpose is to introduce an Order property into your filter. It is also implemented by the common ActionFilterAttribute.

So, overall, no big difference on that front, and the usage seems pretty straight forward too. Consider the following example:

In this case, the filter will validate the model state prior to action execution, and respond with a 400 response in case the model state is invalid (i.e. required parameters are missing or other validation conditions have not been satisified).

And here is the action filter in (pun intended) action. Notice that there is no longer the need for any model state checks inside the action, as this logic has been externalized into the action filter.

Of course this is nothing new for anyone – the behavior is virtually identical to that from both MVC 5 (and earlier) and Web API.

Service filters

Service filters are a bit more exciting. They allow you to resolve a filter instance directly from the IoC container. This means:

  • the filter must be registered in the container in the first place
  • the filter can have a lifetime that you control yourself (through the IoC container)
  • constructor injection is now supported into those filters

The last point is especially important, as for example in Web API this was not the case – the filters were cached, and having per request or transient dependencies injected into the filter was virtually impossible (at least not without manually rewriting the majority of the filter pipeline). As a result Web API had horrendous service locator patterns like these scattered all over the place:

In MVC 6 the same code can be nicely rewritten into a class using proper dependency injection.

At this point you may ask, how does this tie into the ServiceFilter, since our filter above is just a regular ActionFilterAttribute?

Well, you can think of ServiceFilter as a provider of filters (or technically, a factory). In fact, ServiceFilter is an implementation of a simple IFilterFactory interface:

The role of a filter factory is to provide an instance of an IFilter which can be used within the MVC 6 pipeline. It is a bit of a "filterception" since IFilterFactory is an IFilter itself :) This way the filters and filter factories can be used interchangeably in the pipeline. The default filter provider in MVC 6 will then attempt to cast each filter to IFilterFactory and if that succeeds, invoke the CreateInstance method – otherwise, it will simply treat the filter as a general IFilter.

So going back to ServiceFilter, its entire definition is shown below:

As you can see, all that's needed is a type of a real filter that you want to use to be provided through the constructor, and it will then be resolved from the ASP.NET 5 IoC container – through IServiceProvider.

So, with our earlier LogFilter example, in practice, it would look like this in a controller:

In other words, we are using ServiceFilter as a gateway to get the actual filter we want to use (in this case above it's LogFilter) to be resolved from the IoC container.

One final missing piece is that LogFilter needs to be registered in the IoC container for this setup to work. This has to happen in your Startup class, inside ConfigureServices.

Type filters

Finally, you can choose to use type filters, which are quite similar to service filters. Type filters are also implementations of IFilterFactory and can allow you to have dependencies injected into your filter.

The definition is shown below:

The difference between service filters and type filters is that Types that are resolved through type filters do not get resolved directly from an IoC container, but rather use ObjectFactory for instantiation. This allows you to use TypeFilterAttribute with filters that – contrary to our earlier service filter example – have not been registered in the IoC container.

So the example with LogFilter would look like this when used in conjunction with TypeFilterAttribute:

Which is identical as when using service filters, accept it will just work straight away – without us having to explicitly register the filter in IoC container.

Additionally, TypeFilter also allows you to pass in extra arguments to the filter, directly from the controller. Consider we modify our filter definition to be as follows – adding an extra constructor parameter that would not be resolved from IoC container:

We could now change our type filter usage in the controller to explicitly pass the string value into the constructor.

Ultimately the entire structure gives you quite a lot of flexibility in terms of what you can do with the filter pipeline, and how do you wish your filters to be “brought to life”.

Be Sociable, Share!

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1877()

  • JordanM

    This looks ugly to me. Hasn’t anyone at Microsoft heard of AOP or looked at the solutions that have been available in various containers for more than 10 years?

    The way arguments are passed in is awful – and the lack being able to use a proper constructor for passing in mandatory arguments is very poor.

    The problem with MVC/WebAPI action filters is that the advice is tightly coupled with the pointcut. The attributes should be markers, and the code to be executed should live in another class which would then have its dependencies injected into it. This is the way it is done in Spring.NET, Unity etc, and is much cleaner than the solution here.

    ps – many thanks for introducing this, I’m having a go at Microsoft not you :)

    • http://www.dox.com.au/ Ian Yates
      • JordanM

        Thanks Ian!

        • yellowfive

          I agree with JordanM that this is a bit ugly, but the article linked by Ian Yates is ugly in a different way.

          I like the TypeFilter concept, but as others say, I don’t like the syntax. I like the syntax in the article linked by Ian Yates, but I don’t like the approach. Couldn’t a simple wrapper on a TypeFilter give you the best of both? Here’s a quick example:

          http://pastebin.com/0fddQEZY

          You can then decorate a controller or action with [MyFilter] and get all the TypeFilter handiness without the mess.

          And then you don’t have all the shenanigans of the “passive attribute” approach suggested in that blog. Things like… running every filter on every action even if you only want the action to apply to a specific controller. Sure they don’t do much work if the attribute is missing… but it feels like a pretty hacky approach. Also, you have to remember to register each filter type for DI… which isn’t a huge deal but is a pain, and can lead to annoying code maintenance. Like what if you remove all controllers/actions that use the attribute? Now you have a registration and a global filter in Startup that will never do anything… junk code that will never break or cause an error, but clutter your config for the next guy reading it.

        • yellowfive

          I really like the concept of the TypeFilter (get constructor injection without any “maintenance” required like registering types in Startup and stuff), but I get why people don’t like the syntax. I think that’s pretty easy to get around by just wrapping it though, e.g.

          public class MyFilterAttribute : TypeFilterAttribute {
          public MyFilterAttribute : base(typeof(MyFilterInternal)) { }
          }

          internal class MyFilterInternal : ExceptionFilterAttribute { /* you can use constructor injection in this filter! */ }

          Decorate your stuff with [MyFilter] and you’re good to go. Put this in its own class library to hide the internal filters from intellisense in your controllers so that you always use the one derived from TypeFilterAttribute.

          • Andrew

            Maybe make it a nested attribute inside a generic and pass the generic arg typeof to the type/service attribute.

  • http://www.philliphaydon.com Phillip Haydon

    WTF is this… It’s so messy… Can’t decide if MVC is taking steps forward or backward.

  • Pingback: Dew Drop – June 9, 2015 (#2031) | Morning Dew()

  • TimotyW

    Why not implement this kind of cross cutting concerns with dynamic interceptors (i.e. Castle Windsor)? Will be definitely a more DRY and SOLID approach, and also the injections could be handled in a cleaner way.

  • Eleftheria Kiourtzoglou

    Hello Mr Woj,

    Nice blog! I am editor at .NET Code Geeks (www.dotnetcodegeeks.com). We have the NCG program (see http://www.dotnetcodegeeks.com/join-us/ncg/), that I think you’d be perfect for.

    If you’re interested, send me an email to eleftheria[dot]kiourtzoglou[at]dotnetcodegeeks[dot]com and we can discuss further.

    Best regards,
    Eleftheria

  • Pingback: Links of the month (June Edition) | Jan @ Development()

  • Guest

    If my action filter need a value in AppSetting how to do that?