Posted by: Cirilo Meggiolaro | 04/25/2009

Tip of the day #193 – ASP.NET MVC – Custom Action Filters

Tip #192 described how ASP.NET MVC action filters work and showed two built-in action attributes: OutputCache and HandleError. Today we are going to check how to create a custom action filter and we are going to build an action filter to log requests information for audit purpose.

Interfaces

IActionFilter: Defines operations that will be called either before or after an action method executes. The following operations are available:

  • void OnActionExecuting(ActionExecutingContext filterContext):Called before the action method executes;
  • void OnActionExecuted(ActionExecutedContext filterContext):Called after the action method executes.

IAuthorizationFilter: Defines an operation that will be called when authorization is required. The following operation is available:

  • void OnAuthorization(AuthorizationContext filterContext): Called when authorization is required.

IExceptionFilter: Defines an operation that will be called when an exception occurs. The following operation is available:

  • void OnException(ExceptionContext filterContext): Called when an exception occurs.

IResultFilter: Defines operations that will be called either before or after an action result executes. The following operations are available:

  • void OnResultExecuting(ResultExecutingContext filterContext): Called before an action result executes;
  • void OnResultExecuted(ResultExecutedContext filterContext):Called after an action result executes.

Base classes

FilterAttribute: This is the base class for any action filter attribute. The following property is available:

  • int Order: Gets or sets the order.

ActionFilterAttribute: The ActionFilter attribute is an abstract class that inherits from the base FilterAttribute class and implements two interfaces: IActionFilter and IResultFilter.

Structure

Now that you know what are the interfaces and abstract classes available let’s check the basic structure. Basically, an action filter inherits from a base filter attribute type and may implement some filter interfaces. The code snippet below shows a class that inherits from the FilterAttribute base class:

public class MyCustomFilter : FilterAttribute { }

That is the minimum you need to create a custom filter. But the code above does not perform any task. Let’s move on to a real scenario to check how it works.

Audit attribute

Let’s create a custom action filter that will log to the database some piece of information about the requests done to all action methods to be used during audit processes.

Create a new class called AuditAttribute that inherits from the base FilterAttribute and implements the IActionFilter interface. The code will be similar to the following:

public class AuditAttribute : FilterAttribute, IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext filterContext) { }
    public void OnActionExecuting(ActionExecutingContext filterContext) { }
}

We need to gather some piece of information such as controller class name, action method name, parameters passed to this method (if any) and the user that is requesting the operation.

Controller name: The filterContext parameter contains a reference to the Controller object. Since you have the reference, you may check the Type name of it to get the controller name:

string controllerName = filterContext.Controller.GetType().Name;

Action method name: There are at least two options to retrieve the action method name. You may either access the route dictionary and get the value of the “action” key or use the ActionDescriptor property to access the action name:

string actionMethodName = filterContext.RouteData.Values[“action”].ToString();
string actionMethodName = filterContext.ActionDescriptor.ActionName;

Action method parameters: A generic Dictionary<string, object> of parameters passed to the action method is available in a property called ActionParameters. You can loop through this dictionary and get the parameter name and value as shown on the following code snippet:

if (filterContext.ActionParameters != null)
{
    foreach (KeyValuePair<string, object> parameter in filterContext.ActionParameters)
    {
        string parameterInfo = string.Format(“Parameter name: {0} – Parameter value:         {1}”, parameter.Key, parameter.Value == null ? “null” : parameter.Value);
    }
}

User name: For scenarios where users are authenticated via forms or Windows authentication you may use the IPrincipal User object under the HttpContext property:

string userName = filterContext.HttpContext.User.Identity.Name;

The way how you are going to save this piece of information is up to you. You may save it to a database table using any framework such as ADO.NET Entity Framework or use your own component. I am not going to describe the code to save data for brevity. Your custom audit attribute must be similar to the code below:

public class AuditAttribute : FilterAttribute, IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        /// Operations are going to be logged
        /// before the action method be executed.

    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        string controllerName = filterContext.Controller.GetType().Name;
        string actionName = filterContext.ActionDescriptor.ActionName;
        string userName = filterContext.HttpContext.User.Identity.Name;

        if (filterContext.ActionParameters != null)
        {
            foreach (KeyValuePair<string, object> parameter in             filterContext.ActionParameters)
            {
                string parameterInfo = string.Format(“Parameter name: {0} – Parameter                 value: {1}”, parameter.Key, parameter.Value == null ? “null” :                 parameter.Value);
            }
        }

        /// Save information to the database
    }
}

Optimizing

Since we are not using the OnActionExecuted operation we could inherit from the ActionFilterAttribute base class and just override the OnActionExecuting operation. The code will be similar to the following:

public class AuditAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        string controllerName = filterContext.Controller.GetType().Name;
        string actionName = filterContext.ActionDescriptor.ActionName;
        string userName = filterContext.HttpContext.User.Identity.Name;

        if (filterContext.ActionParameters != null)
        {
            foreach (KeyValuePair<string, object> parameter in             filterContext.ActionParameters)
            {
                string parameterInfo = string.Format(“Parameter name: {0} – Parameter                 value: {1}”, parameter.Key, parameter.Value == null ? “null” :                 parameter.Value);
            }
        }

        /// Save information to the database
    }
}


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: