Fluent-API to add ActionFilters to Controller in ASP.NET MVC

Note: The name of the classes and the methods are just temporary and may change, I’m so bad when it comes to naming classes and methods. The source code is simple and haven’t done so much refactoring etc. Just wanted to see if I could get it to work, so please have that in mind.

EDIT: Working with new methods to get a better overview of Action Filters added and also reusing AcitonFitlers, you can read about it here. 


When we create controllers for our ASP.NET MVC application we can also add Action Filters to handle cross-cutting concerns, like Authorization, Error handling and Caching etc. If we want to have Error handling on every controller we need to add the HandleErrorAttribute to all controllers, like this:


[HandleError] 
public MyController : Controller 
{ 
}


By adding Action Filters by using attributes it can be hard to get a good overview of which controllers that has the HandleErrorAttribute. The same regarding Action methods, for example if we have a lot of Controllers and want to see see all the Action methods that uses for example the OutputCacheAttribute, we need to go through all Controllers and methods, there is no easy way to get a simple overview of them.

Adding Action Filters to Action methods and Controllers also add some sort of “dependency” to action filters (not a big deal, though). I decided to try a way to add Action Filters to Controllers and Action methods in one single file, so I could get a better overview of which Controllers and Action methods uses what ActionFilter etc. Because ActionFitlers contains cross-cutting concerns I also wanted to move it away as attributes from the Controllers and Action methods so developers don’t need to care about the cross-cutting concerns during the creation of Controllers. instead add them later.

I sort of used a Fluent-API for the configuration of ActionFilters, and the configuration is added to the Global.asax’s Application_Start event. Here is an example where I add the ErroHandler Action Filter to all Controllers Action Methods:


ConfigActionFilter.ConfigController<Controller>() 
                  .AddFilterToController(new HandleErrorAttribute());


If I want to add an Action Filters to a specific Controller I just use the type of the Controller:


ConfigActionFilter.ConfigController<HomeController>()
                              .AddFilterToController(new HandleErrorAttribute());

 

Note: The reason I don’t use XML and a XML meta data provider as a configuration is because I wanted to make the configuration type safe. If we use XML we can only get an exception if we spell something wrong at runtime.


If I want to add Action Filters to Action Methods I can write something like this:

 
ConfigActionFilter.ConfigController<HomeController>()
                              .AddFilterToController(new HandleErrorAttribute())
                              .AddFilterToAction(c => c.About(),
                                                 new MyActionFilterAttribute(),
                                                 new MyOtherActionFilter() { Metadata=10 })
                              .AddFilterToAction(c => c.Index(),
                                                 new MyActionFilterAttribute());

 

I didn’t wanted to use a string for the ActionMethod (AddFilterToAction(“About”)) because it would not be type safe. I want to get a warning or error while typing the code, so instead I created an Expression. The AddFilterToAction takes a params of FilterAttributes, so I can easy add several of Action Filters to an Action Method. The AddFilterToController method will add Action Filter to the Controller, just like adding an Action Filter attribute to the class definition, so all Action Methods within the Controller will use the Action Filter.

I created a Custom ControllerFactory so I could add my Custom ControllerActionInvoker to the Controller:


 public class ActionFilterConfigControllerFactory : DefaultControllerFactory
 {
        public override IController CreateController(RequestContext requestContext, string controllerName)
        {
             ...
                controllerInstance.ActionInvoker = new ActionFilerConfigControllerActionInvoker();

            return controller;
        }
 }

 

The custom ControllerActionInvoker will make sure the Action Filter added by using my solution is added to the FilterInfo class. This is done by overriding the GetFilters method:


public class ActionFilerConfigControllerActionInvoker : ControllerActionInvoker
{
        protected override FilterInfo GetFilters(ControllerContext controllerContext,
                                                 ActionDescriptor actionDescriptor)
        {
            var filters = base.GetFilters(controllerContext, actionDescriptor);

            ...

            if (ConfigActionFilter.Config.ContainsKey(controllerName))
                AddFiltersToFilerList(actionDescriptor, filters, controllerName);

            return filters;
        }

}

 

This project only a fun thing to do and I like the idea of having different options to add Action Filters, and this solution will make the Controller clean from Attributes and also have one place to add them. When I create my Controller I don’t need to worry or think about the cross-cutting concerns, I just add them later and into the Global.asax.

I want to thanks Mikael Söderström for taking time to discuss this solution with him, and get some feedback and also “host” the source code for me.

You can download the source code with an example here: http://vinkr.net/misc/ActionFilterConfig.zip

1 Comment

Comments have been disabled for this content.