As I mentioned here the filter attribute design in MVC is terrible. That other post explains why so I'm not going to rehash. This post is about how we can do it better. I don't really have anything against using the attributes to annotate an action or controller it's mainly that MVC makes the attribute itself the filter. What if, instead, the attribute was simply a marker that said that a particular filter should be applied and allowed you to customize it:

public interface IFilterAttribute
{
    Type FilterType { get; }
    void InitializeFilter(object filter);
}

This interface specifies the filter type then a method to initialize the filter. We could then implement this interface on an attribute like so:

public class SecureAttribute : Attribute, IFilterAttribute
{
    private readonly bool _enabled;

    public SecureAttribute(bool enabled)
    {
        _enabled = enabled;
    }

    public Type FilterType { get { return typeof(SecureFilter); } }

    public void InitializeFilter(object filter)
    {
        ((SecureFilter) filter).Enabled = _enabled;
    }
}

This attribute allows us to turn security on and off on an action or controller. You can see that it specifies the type and initialization of the filter. The filter is nothing special; in this case it implements IMvcFilter so it can set the multiplicity (In this case we only want one filter to be applied) and IAuthorizationFilter for authentication. It is also setup to be constructor injected:

public class SecureFilter : IAuthorizationFilter, IMvcFilter
{
    private readonly IAuthenticationService _authenticationService;

    public bool Enabled { get; set; }

    public SecureFilter(IAuthenticationService authenticationService)
    {
        _authenticationService = authenticationService;
        Enabled = true;
    }

    public bool AllowMultiple { get { return false; } }
    public int Order { get; set; }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if (Enabled && !_authenticationService.Authenticate())
        {
            // Access denied
        }
    }
}

Now our filter provider can look for this new interface, IFilterAttribute and create the filters from the information provided from the attribute:

public class FilterAttributeProvider : IFilterProvider
{
    private static readonly Dictionary<string, IEnumerable<FilterAttributeMetadata>> AttributeCache =
        new Dictionary<string, IEnumerable<FilterAttributeMetadata>>();

    private readonly Func<Type, object> _filterFactory;

    public FilterAttributeProvider(Func<Type, object> filterFactory)
    {
        _filterFactory = filterFactory;
    }

    public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
        return GetFilterAttributes(actionDescriptor).Select(x => CreateFilter(x, _filterFactory));
    }

    private static IEnumerable<FilterAttributeMetadata> GetFilterAttributes(ActionDescriptor actionDescriptor)
    {
        IEnumerable<FilterAttributeMetadata> attributes;
        if (!AttributeCache.TryGetValue(actionDescriptor.UniqueId, out attributes))
        {
            attributes = LoadFilterAttributes(actionDescriptor);
            lock (AttributeCache) AttributeCache.Add(actionDescriptor.UniqueId, attributes);
        }
        return attributes;
    }

    private static IEnumerable<FilterAttributeMetadata> LoadFilterAttributes(ActionDescriptor actionDescriptor)
    {
        return actionDescriptor.GetCustomAttributes(typeof(IFilterAttribute), true).Cast<IFilterAttribute>().
                                Select(x => new FilterAttributeMetadata(x, FilterScope.Action)).
                   Union(actionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(IFilterAttribute), true).
                                Cast<IFilterAttribute>().
                                Select(x => new FilterAttributeMetadata(x, FilterScope.Controller)));
    }

    private static Filter CreateFilter(FilterAttributeMetadata filterMetadata, 
                                       Func<Type, object> filterFactory)
    {
        var filter = filterFactory(filterMetadata.Attribute.FilterType);
        filterMetadata.Attribute.InitializeFilter(filter);
        return new Filter(filter, filterMetadata.Scope, GetFilterOrder(filter));
    }

    private static int? GetFilterOrder(object filter)
    {
        var mvcFilter = filter as IMvcFilter;
        return mvcFilter != null ? mvcFilter.Order : (int?)null;
    }

    private class FilterAttributeMetadata
    {
        public FilterAttributeMetadata(IFilterAttribute attribute, FilterScope scope)
        { Attribute = attribute; Scope = scope; }

        public IFilterAttribute Attribute { get; private set; }
        public FilterScope Scope { get; private set; }
    }

    public interface IFilterAttribute
    {
        Type FilterType { get; }
        void InitializeFilter(object filter);
    }
}

Using the type on the attribute, the provider can create the filter with a factory passed into the constructor (Which would be our IoC container). Once the filter is created it is initialized by the InitializeFilter() method on the attribute. This completely decouples attributes from filters and allows us to constructor inject filters even when they are applied via attributes. So as an example lets say that we want to globally apply security but in some actions or controllers we want to turn it off so they are public. We can register the secure filter we defined above and our authentication service in our IoC container (In this example StructureMap):

public class MvcApplication : HttpApplication
{
    protected void Application_Start()
    {
        ObjectFactory.Configure(x =>
                                    {
                                        x.For<IAuthenticationService>().Use<AuthenticationService>();
                                        x.For<IAuthorizationFilter>().Use<SecureFilter>();
                                    });

        DependencyResolver.SetResolver(x => (x.IsAbstract || x.IsInterface) ?
                                                ObjectFactory.TryGetInstance(x) :
                                                ObjectFactory.GetInstance(x), 
                                       x => ObjectFactory.GetAllInstances(x).Cast<object>());

        FilterProviders.Providers.Add(new FilterAttributeProvider(ObjectFactory.GetInstance));
    }
}

Now our entire site requires authentication. For an action that we want public we can turn authentication off:

public class HomeController : Controller
{
    [Secure(false)]
    public ActionResult Index()
    {
        return View();
    }
}

We only allow one SecureFilter to be applied, so since the filter associated with the attribute has an "Action" scope it will take priority over the global filter which has a "Global" scope.