Jul 16, 2010

Building an ActionFilter - BeforeFilter for ASP .Net MVC, Part 2

I've broken this up into three parts:

I’ve decided to add more features to the before filter I’ve build in the previous post. I need a way to specify a few methods I’d like to execute before the controller actions. I can do that now by specifying a number of BeforeFilter attributes but that looks a little silly. What I want is the ability to do something like this:

    [BeforeFilter("InitializeCategories","InitializeProvinces")]

This should be fairly easy to accomplish. We should probably start with a test. I’ve added a logaction method to the test controller so we can assert it was called. Here is the test:

    [Test]
    public void should_call_all_specified_controller_methods_given_they_exist()
    {
        controller.Setup(x => x.initialize());
        controller.Setup(x => x.logaction());
        var attribute = new BeforeFilterAttribute("initialize", "logaction");

        attribute.OnActionExecuting(context);

        controller.VerifyAll();
    }

To make this pass we change our implementation slightly. The method names now become a collection we need to iterate over and invoke the method on the controller if found; if not, we throw and exception as before. I've changed the exception type to an InvalidOperationException.

    public BeforeFilterAttribute(string methodName) : this(new[] { methodName })
    {
    }
        
    public BeforeFilterAttribute(params string[] methods)
    {
        this.methods = new List<string>(methods);
    }
        
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var controller = filterContext.Controller;
        foreach (var method in methods)
        {
            ...
        }

        base.OnActionExecuting(filterContext);
    }

This makes the test pass and now we have a BeforeFilter that’s a little more useful and friendly.

I would like to add one more feature to my BeforeFilter and that is an exception list. In other words, I would like a way to specify controller actions for which to NOT execute the methods. That way I have the ability to apply the BeforeFilter to the controller itself instead if the individual action and still retained fine grained control over the before methods that execute for a given action. I will leave that for a separate post.