Introduction
Once in a project that was written in ASP.NET MVC 2, I needed to implement a means to register action filters for all controllers in the application, because it is not very convenient to specify the attributes for each class of controller if you want to perform the same operation globally, and as such, there is no flexibility. Later, I found mention of this only in ASP.NET MVC 3.
After that, I decided to write a similar functionality on my own, setting myself a series of requirements that had to be performed:
- Ease of registering a filter in the application
- Ability to add criteria to filter
- Ability to use dependency injection for filters
I have got an interesting approach and I decided to combine all this into a separate library to share it with others.
You can find the latest version of binary and source code on the project page on CodePlex. Working application examples are located in src\Samples folder of source code.
Using the Code
FluentFilters
helps you to implement a global filters functionality in your ASP.NET MVC application. A global filter is a filter that is going to run for every single action on every single controller. Creating a custom filter for usage globally is very simple. All you need to do is to implement your custom filter class with one or more interfaces which are the four types of ASP.NET MVC filters:
IAuthorizationFilter
IActionFilter
IResultFilter
IExceptionFilter
For example, a filter can look like:
public class CustomFilter: IResultFilter, IAuthorizationFilter
{
#region IResultFilter Members
public void OnResultExecuted(ResultExecutedContext filterContext)
{
}
public void OnResultExecuting(ResultExecutingContext filterContext)
{
}
#endregion
#region IAuthorizationFilter Members
public void OnAuthorization(AuthorizationContext filterContext)
{
}
#endregion
}
For working with filters, you should use class FluentFiltersBuilder
. This is the recommended usage if you build MVC application without Inversion of Control(IoC) container. Using FluentFilters
with IoC container is preferred rather than using the FluentFiltersBuilder
. In this case, you can manage creation and lifetime of filter object and use Dependency Injection. To achieve this, you need to do several steps.
- Implement custom class which inherits
FilterRegistry
class and overrides GetFilterInstance
method to resolve object by type instead of creating a new one. This class will be used instead FluentFiltersBuilder
for registering filters.
- Create a custom Controller Factory for supporting IoC container and registering it.
You can find the sample application with IoC container in src\Samples\FluentFilters.Samples.IoC folder of the source code.
For basic configuration, it is sufficient to edit Global.asax.cs file like below:
using System.Web.Mvc;
using System.Web.Routing;
using FluentFilters;
using FluentFilters.Criteria;
using Website.Core.Filters;
namespace Website
{
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index",
id = UrlParameter.Optional } );
}
private static void RegisterFluentFilters()
{
FluentFiltersBuilder.Current.Add();
}
private static void RegisterControllerFactory()
{
ControllerBuilder.Current.SetControllerFactory
(new FluentFiltersControllerFactory());
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
RegisterFluentFilters();
RegisterControllerFactory();
}
}
}
You can see in the method RegisterFluentFilters
registered filter BrowserDetectionFilter
.
Later, when you added global filters for usage, you can set chain criteria for each of those. By criteria, you can set whether to execute filter or not. Create criteria is very simple too. You need to inherit your custom criteria class from IFilterCriteria
interface and implement only method Match
. The library already provides three criteria for use:
ActionFilterCriteria
- Filter by specified action
AreaFilterCriteria
- Filter by specified area
ControllerFilterCriteria
- Filter by specified controller
For example, the code snippet below shows the source of the criteria ActionFilterCriteria
:
public class ActionFilterCriteria : IFilterCriteria
{
private readonly string _actionName;
public ActionFilterCriteria(string actionName)
{
_actionName = actionName;
}
#region Implementation of IActionFilterCriteria
public bool Match(FilterRegistryContext context)
{
return string.Equals(_actionName,
context.ControllerContext.RouteData.GetRequiredString("action"),
StringComparison.OrdinalIgnoreCase);
}
#endregion
}
For each filter, you can add two chains criteria. This is the criteria that is required and which should be excluded. If criteria are not specified, the filter will be executed for the entire application.
In the code below, we added filter BrowserDetectionFilter
for area "Blog
" and excluded for controller "Account
":
FluentFiltersBuilder.Current.Add
(c =>
{
c.Require(new AreaFilterCriteria("Blog")); c.Exclude(new ControllerFilterCriteria("Account"));
});
You can build the Chain of criteria by methods And(IFilterCriteria criteria)
and Or(IFilterCriteria criteria)
. Methods work as conditional logical operators &&
and ||
.
Contrived example:
registry.Add<displaytopbannerfilter>(c =>
{
c.Require(new IsFreeAccountFilterCriteria()).Or(new AreaFilterCriteria("Blog")).
Or(new AreaFilterCriteria("Forum")).And(new IsMemberFilterCriteria());
c.Exclude(new AreaFilterCriteria("Administrator")).
Or(new ControllerFilterCriteria("Account")).
And(new ActionFilterCriteria("LogOn"));
});
If speaking in C# language, then the code above can be understood as (like pseudocode):
if( IsFreeAccountFilterCriteria() || area == "Blog" ||
(area == "Forum" && IsMemberFilterCriteria()) )
{
if(area != "Administrator")
{
DisplayTopBannerFilter();
}
else if(controller != "Account" && action != "LogOn")
{
DisplayTopBannerFilter();
}
}
History
- 4th November, 2010: Initial version
- 5th November, 2010: Updated article