ASP.Net MVC Framework 2 - Interception and creating a Role Action Filter

With the ASP.Net MVC Framework Preview 2, we can use Action Filters. An Action Filter can be used to intercept actions before and after an Action method of a Controller is executed. By using Action Filters you can for example add code that will check if a user has the right permission to access a action method, or you can use it to log messaged before and action is executed, and also after. An Action Filter is easy to apply to a Controller or its Action method. Just add the ActionFilter as an attribute to the class or specific method of a Controller.

[RoleFilter(AllowRoles="Admin,Everyone"]
public class MyController
{
    [MyFilter]
    public void Index()
    {
        …
        RenderView("Index");
    }
}

If you apply an Aciton Filter to the Controller class, it will be executed for every action method in the Controller, if you add Action Filter to a specific method, it will only be called when that specific method is executed. An Action Filter has two methods that you can override and use, OnActionExecuting and OnActionExecuted. As you probably can get out from the names, the Executing method will be called before the action method is executed, and the Executed, after the action method is executed. I will write more about the both method later in this post. You can apply several Action Filters to a Controller. One thing you need to have in mind is the order they will be executed. Reflection is used to get the Action Filters out from a Controller, Reflection can’t promise to give items in the order which they are applied. To solve this we can use the Order property of an Action Filter. By using the Order property we can specify in which order the Action Filter should be executed.

[MyFilter(Order=1)]
[MyFilter2(Order=2)]
[MyFilter3(Order=3)]
public void Index()

The OnActionExecuting method, which is the method that will be executed before an Action method of a Controller is executed, has one argument, filterContext of type FilterExecutingContext. The FilterExecutingContext has the following properties:

ActionMethod
Cancel
Controller
HttpContext
RouteData

The ActionMethod is of type MemberInfo and can give you all the information needed of the Action method that are going to be executed. The Cancel property is a Boolean where you can set it to true to cancel all execution of Action Filters and avoid calling the Action method. The Controller property is of type IController and will give you access to the current Controller. The HttpContext is of type HttpContextBase and will give you access to the HttpContext. The RouteData will give you access to the current route information. The OnActionExecuted method takes an argument of type FilterExecutedContext, which have the same properties as the FilterExecutingContex except Cancel and have two other properties, Exception which can be used to get an Exception thrown inside of an Action method, ExceptionHandled can be used to specify if you have handled the exception or not. The reason why the FilterExecutedContext don’t have the Cancel property is because you can’t cancel an action method that has already been executed. If you want a common way to handle exceptions thrown from an Action method, you can use the OnActionExecuted method to log or handle the exception in your own way, instead of showing the ASP.Net yellow error page.

The Controller base class has two methods which can also be used to handle pre and post action for every action methods of the Controller.  They have the same name and interface as the Action Filter’s OnActionExecuting and OnActionExecuted methods.

public class MyController : Controller
{
    public override void OnActionExecuting(FilterExecutingContext filterContext)
    {
    }

    public override void OnActionExecuted(FilterExecutedContext filterContext)
    {
    }
}

If you need to use some unique logic for a specific Controller on every action that is called, you can override the OnActionExecuting and Executed methods of the Controller, if not, it’s better to use an Action Filter, so you can reuse code among the Controllers.
The execution of an Action method is the following when an Action filter is used:

Controller.OnActionExecuting

ActionFilter.OnActionExecuting

Controller’s Action method Executed

ActionFilter.OnActionExecuted

Controller.OnActionExecuted

To implement an Action Filter you need to create a class which inherits from the ActionFilterAttribute, where you override the ActionFilterAttribute’s OnActionExectuing or Executed methods or both if you want to use both pre and post actions:

public class MyFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(FilterExecutingContext filterContext)
    {
    }

    public override void OnActionExecuted(FilterExecutedContext filterContext)
    {
    }
}

When the Action Filter is created you can simply apply the attribute to a Controller’s Class definition (if you want the pre and post action to be executed for every action method) or to methods (if you only want to make sure the pre and post action should be used on a particular method).
The following Action Filter can be used to make sure that only a user to a specified role can execute the action method. In this code the Roles feature of ASP.Net is used:

using System;
using System.Web.Mvc;
using System.Web.Security;
using System.Security;

public class RoleFilterAttribute : ActionFilterAttribute
{
    public string Roles { get; set; }


    public override void OnActionExecuting(FilterExecutingContext filterContext)
    {
        string[] userRoles = System.Web.Security.Roles.GetRolesForUser();

        foreach (string definedRole in this.Roles.Split(','))
        {
            foreach (string role in userRoles)
            {
                if (definedRole.Equals(role))
                    return;
            }
        }

        throw new SecurityException("Access not granted!");
    }

}

When the attribute is created, you can simply add it to the Controller.

[RoleFilter(Roles="Admin")]
public void ListUser()
{
    //...
    RenderView("Users");
}

The only thing I want now for make it easier to add or remove interceptors, is to make it possible to set them up in a configuration file. In that way we can add or remove interceptors from a Controller without recompile our application. We can use Spring.Net’s Aspect Oriented Programming (AOP) support to add interceptors, but it would be so nice to have AOP support added to C# ;)

1 Comment

Comments have been disabled for this content.