OdeToCode IC Logo

Configurable Global Action Filters for ASP.NET MVC

Tuesday, January 18, 2011

ASP.NET MVC 3.0 introduces global action filters - an easy way to apply an action filter to every action in an MVC application. All you need to do is register the filters during application startup:

protected void Application_Start()
{
    ...
    GlobalFilters.Filters.Add(new HandleErrorAttribute());
    GlobalFilters.Filters.Add(new FooFilter());
    GlobalFilters.Filters.Add(new BarFilter());
    ...
}

But what if you wanted to add (or remove) filters through configuration?

<configSections>
  <section name="filters" 
           type="ConfigurableFilters.FiltersSettings, AssemblyName "/> 
</configSections>
...
<filters>
  <add type="System.Web.Mvc.HandleErrorAttribute, System.Web.Mvc..." />
  <add type="ConfigurableFilters.BarFilter, AssemblyName" />
  <add type="ConfigurableFilters.FooFilter, AssemblyName" />
</filters>

In that case you'll need a ConfigurationElement.

public class FilterAction : ConfigurationElement
{
    [ConfigurationProperty("type", IsRequired = true, IsKey = true)]
    public string Type 
    {
        get { return base["type"] as string; }
        set { base["type"] = value; }
    }
}

And a ConfigurationElementCollection.

public class FilterActionCollection : ConfigurationElementCollection
{       
    protected override ConfigurationElement CreateNewElement()
    {
        return new FilterAction();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {            
        return ((FilterAction) element).Type;
    }
}

And a ConfigurationSection.

public class FiltersSettings : ConfigurationSection
{
    public static FiltersSettings Settings
    {
        get
        {
            var section = ConfigurationManager.GetSection("filters")
                          as FiltersSettings;
            return section ?? new FiltersSettings();                
        }
    }

    [ConfigurationProperty("", IsDefaultCollection = true)]
    public FilterActionCollection Filters
    {
        get
        {
            return base[_filtersProperty] as FilterActionCollection;
        }
        set
        {
            base[_filtersProperty] = value;
        }
    }

    private readonly ConfigurationProperty _filtersProperty = 
        new ConfigurationProperty(
            null, typeof (FilterActionCollection), null, 
            ConfigurationPropertyOptions.IsDefaultCollection);
}

One way to apply the configured filters is to use the following code during application startup:

var filters = FiltersSettings.Settings.Filters;
foreach (var filter in filters.Cast<FilterAction>())
{
    var filterType = Type.GetType(filter.Type);
    GlobalFilters.Filters.Add(Activator.CreateInstance(filterType));
}

Another approach is to implement a filter provider. We'll take a look at the provider approach tomorrow.