Action Filters in ASP.NET MVC




Continuing with our MVC 101 series today we look at an important MVC feature, Filters. Filters in ASP.NET MVC are a way to apply cross-cutting logic at the controller level. Some examples of cross-cutting logic is Security and Logging.

Security is a cross cutting concern because, once enabled, we need to apply it for all incoming requests in the web Application. Imagine a world where you don’t have filters, in such case for every request that comes in, you Action method in controller will have to check if the user was Authorized to perform the action and view its result.

This not only violates single responsibility principle of the Action Method (it’s doing two things, evaluating security and computing the Action’s output) but is also an extremely verbose and repetitive work that we’ve to do irrespective of what the Action is supposed to do. Moreover, writing Authentication Code in action method cannot be guaranteed. There is no certainty that a developer may or may not miss out on implementing the code!

In comes Security Filters! The Authorize Attribute that we set at a class, method or property level in a Controller is nothing but a Security Filter implementation. All your security related code is now abstracted out of the Action method into the filter, that’s executed before the action method is executed. Voila!

Types of Filters and their Sequence of Execution

There are four types of Filters in ASP.NET MVC as of today

1. Authorization Filters: Responsible for checking User Access, these implement the IAuthorizationFilter interface in the framework. The AuthorizeAttribute and RequireHttpsAttribute are examples of Authorization Filters.

2. Action Filters: These implement the IActionFilter interface that have two methods OnActionExecuting and OnActionExecuted. OnActionExecuting runs before the Action and gives an opportunity to cancel the Action call.

3. Result Filters: These implement the IResultFilter interface which like the IActionFilter has OnResultExecuting and OnResultExecuted. The OutputCacheAttribute class is example of a Result Filter.

4. ExceptionFilters: These implement the IExceptionFilter interface and they execute if there are any unhandled exceptions thrown during the execution pipeline. The HandleErrorAttribute class is an example of such a filters.

Apart from using the Filter Attribute, each controller class implements all four interfaces, so if applying an attribute is not something you prefer, you can override the methods in your controller class if you want.

Action Filters – Whys and Hows

As we saw above, Action Filters are second in line to be executed in the MVC Pipeline. They are kind of the ‘catch all’ filters. You do all ‘other’ filtering activity there. What could be an example of such ‘other’ activity that is a cross-cutting concern? Well, Logging comes to mind as comes Session Management.

One of the neatest use of IActionFilter interfaces I’ve seen is by Ayende Rahien to save Data in RavenDB. Ayende uses the OnActionExecuting method to open a RavenDb Session and the OnActionExecuted method to save the Session data if there are no errors. This leaves the Controller method with the responsibility of checking/validating the incoming data only. In the above example, Ayende overrides the interface method of the controller itself thus making a base controller for you to derive your controller classes from.

Another good example can been seen when you create a new MVC 4 project using the Internet template, Visual Studio actually code-gens the filter that creates default database for Forms Authentication in the InitializeSimpleMembershipAttribute attribute.

Today we will take a simple example where we will profile how much time the Action method took to execute.

Building your own speed-profiling Action Filter

1. Create a new ASP.NET MVC 4 Project. You can use the Internet or Intranet template

2. If a ‘Filters’ folder does not exist, create one in the project

3. Add a class to the Filters folder and call it ActionSpeedProfilerAttribute. Add the default interface implementations for IActionFilter interface from the System.Web.Mvc namespace. You also need to derive the class from FilterAttribute base class for ActionFilters and ResultsFilters

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ActionSpeedProfilerAttribute : FilterAttribute, IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        throw new NotImplementedException();
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        throw new NotImplementedException();
    }
}


We have added an ‘AttributeUsage’ attribute indicating where our filter can be used.

4. To time how fast our Action method executes, we will use a StopWatch from the System.Diagnostics namespace. We’ll start the timer in the OnActionExecuting method and stop the timer OnActionExecuted method. Then print the elapsed time inside a div that will be at the absolute top left corner of our page. The implementation is as follows

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ActionSpeedProfilerAttribute : FilterAttribute, IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        throw new NotImplementedException();
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        throw new NotImplementedException();
    }
}

We are initializing the time in OnActionExecuting. In the OnActionExecuted method, we are simply checking if the filterContext has an exception or not. If not, we are inserting a div with the time elapsed string in it to be shown on the top left corner of the page.

5. Add the attribute at class level on the HomeController.cs

[ActionSpeedProfiler]
public class HomeController : Controller
{
public ActionResult Index()
{
  …
}

}


6.    Now run the application. Once the page loads on the top left hand corner, you will see the time required to execute the ‘Index’ Action Method in the Home Controller

action-filter-first-run

7.    To verify the result is actually true, you can add a Thread.Sleep(1000) in the Index() method of HomeController and run the application again. This time the Action method will take 1+ seconds to execute because of the mandatory 1 second Thread.Sleep we introduced

action-filter-with-delay

8. You can navigate to the About and Contact pages to see how fast they load as well. Essentially any action method you add to the HomeController now will show the result div.

9. If you don’t want the result to be shown for all Action methods, you can remove it from the Class and add it to one Action method only. For example the following will only show the action method for the Index method and not the About or Contact methods

public class HomeController : Controller
{
[ActionSpeedProfiler]
public ActionResult Index()
{
  ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
   Thread.Sleep(1000);
   return View();
  }

}


10.    If we put a breakpoint in the OnActionExecuted method and watch the filterContext, we’ll see we have a host of information about the request and we can utilize all this information to return an appropriate result

action-filter-fiter-context

Conclusion

With that we conclude this introduction to Action Filters. As you can see they are a ‘catch-all’ type of Filters in MVC where you can write implementation to do pre or post processing of Action Method calls. One sample use we saw was creation of a simple speed profiler.

Download the entire source code over here

Will you give this article a +1 ? Thanks in advance




About The Author

Suprotim Agarwal, ASP.NET Architecture MVP works as an Architect Consultant and provides consultancy on how to design and develop Web applications.

Suprotim is also the founder and primary contributor to DevCurry, DotNetCurry and SQLServerCurry. He has also written an EBook 51 Recipes using jQuery with ASP.NET Controls.

Follow him on twitter @suprotimagarwal

comments

1 Response to "Action Filters in ASP.NET MVC"
  1. Anonymous said...
    October 1, 2013 at 10:03 AM

    Your code in #4 is the same in #3

 

Copyright © 2009-2014 All Rights Reserved for DevCurry.com by Suprotim Agarwal | Terms and Conditions