A complete look into Filters

Index

Filters

Filters are pieces of logic that can be attached over controllers or actions which affects the way in which a request get processed. Filters are normally used to perform the common functionalities in an application like authorization, caching and logging.

A glimpse

[Authorize]
public class AdminController: Controller
{
	[HandleError(View = "Admin")]
	public ActionResult EditSettings()
	{
		// TO DO
	}
}

Listing 1. Filters

In the above listing, the Authorize and HandleError attributes are filters. Filters can be applied to a controller or an action or globally to all controllers and actions.

Filters can be applied over actions, controllers or at global level. When the filters are applied at global level they affect all the actions of all controllers.

In this article we will learn about the basic things of filters, types of filters, creating custom filters and more.

Types of filters

The ASP.NET MVC framework provides four types of filters.

1. Authorization filters
2. Action filters
3. Result filters
4. Exception filters

Lets have a quick look at these types and followingly we will dive deep into each type.

1. Authorization filters

As you guessed these filters are used to authorize the user before the request is passed to the controller or action. If the authorization fails then the filter returns a 401 HTTP UnAuthorized result.

These filters run before any other filters. If you apply this filter to a controller then all the actions inside the controller will be authorized. We will see much about the Authorization filters soon.

2. Action filters

Action filters are the ones that are called by the framework before and after an action method is executed. If you want to run some logic before or after an action then this is the right filter to go with. These filters are called after the Authorization filters and before the Result filters.

3. Result filters

MVC framework separates the action execution from the result execution. Suppose you have a controller that returns a Razor view as result you can use the Result filters to execute some logic before or after the view result is generated. These filters helps to modify the way in which an action result is generated.

4. Exception filters

As the name, these filters are used to handle exceptions. These filters are called whenever an exception occurs in an action method or in any other filters that are applied over the action.

So far you learnt the basic definition of each type of filters. Let's go little deeper into each one by one.

Authorization filters

The Authorization filters implements the interface IAuthorizationFilter. The below listing shows the definition of this interface.

public interface IAuthorizationFilter
{
	void OnAuthorization(AuthorizationContext filterContext);
}

Listing 2. IAuthorizationFilter

The interface contains a single method called OnAuthorization which will be called by the framework to do the authorization. The AuthorizationContext parameter derives from the ControllerContext that provides access to the controller, route data and HttpContext.

Authorization filters are the ones that runs first compared to all the other types of filters.

The MVC framework provides a default implementation of this filter called AuthorizeAttribute. We can create custom authorization filters either by implementing the IAuthorization interface or by deriving from the AuthorizationAttribute. Unless it's really required, I don't suggest implementing the IAuthorizationFilter for creating custom authorization filters instead you can derive the AuthorizeAttribute and override the required methods. I'll tell you the reason why when we create a custom authorization filter.

Now, let's play a little with AuthorizeAttribute.

Let's assume we have an MVC application with Forms Authentication enabled and we have set-up two users in web.config.

<authentication mode="forms">
	<forms loginUrl="~/Agent/Login" timeout="2880">
		<credentials passwordFormat="clear">
			<user name="007" password="likegirls" />
			<user name="009" password="hate007" />
		</credentials>
	</forms>
</authentication>

Listing 3. Setting up Forms Authentication and users in web.config

In the application we have an AgentController with an action called Secret that returns a view which reveals the secret code to the secret service agents. We have marked the action with Authorize attribute.

public class AgentController: Controller
{
	[Authorize]
	public ActionResult Secret()
	{
		return View(new SecretCode("Indian Rupee"));
	}

	// other actions
}

Listing 4. An action with Authorize attribute applied

If any agent tries to invoke the action who is not authenticated they will be re-directed to the login page.

Specifying users and roles

Suppose we want only the "007" user to access the secret action (because he is going to handle the project!) then we have to pass the user-name to the Authorize attribute as below,

[Authorize(Users = "007")]
public ActionResult Secret()
{
	return View(new SecretCode("Indian Rupee"));
}

Listing 5. Passing user to Authorize attribute

If agent "009" or any other tries to access the action after successfully authenticated he will still be directed to the login page.

We can pass multiple users to the Authorize attribute as comma separated list.

[Authorize(Users = "007, 009")]

Listing 6. Passing multiple user to Authorize attribute

We can also pass roles,

[Authorize(Roles = "BritishAgents")]

Listing 7. Passing roles to Authorize attribute

When we pass the role name then all the users belongs to that role are authorized to access the action. Authorize attribute can be applied multiple times over an action or controller as shown in the below listing.

[Authorize(Users = "009", Order = 1)]
[Authorize(Users = "007", Order = 2)]
public ActionResult Secret()
{
	return View(new SecretCode("Indian Rupee"));
}

Listing 8. Applying Authorize multiple times

In the above case, both the agents will fail in any one the Authorize attribute and so both of them can't get the secret!

Creating a custom Authorize attribute

In some cases the built-in AuthorizeAttribute is not sufficient to achieve the authorization needed in an application. One of the limitation is the user names and roles are hardcoded when applying this attribute over actions. So whenever we want to change the users or roles list we have to recompile the code and that's bad! A better approach would be read the values from configuration. Let's create a custom authorization filter that reads the users who are authorized to access the action from the configuration.

As we saw initially the built-in AuthorizeAttribute filter implements the IAuthorizationFilter. For creating a custom authorization filter we have two approaches either we can go ahead and implement the IAuthorizationFilter or we can extend the built-in AuthorizeAttribute class.

There is one thing that we have to take serious while go for implementing the IAuthorizationFilter, the default implementation does some logic to overcome the cache problem. If you are going to implement the interface then we have to take care of that issue.

The cache issue

This issue occurs when both the OutputCache and Authorize filters are applied over an action. The OutputCache filter is used to cache an action result (we will see about this later). In MVC, the authorization code runs after the output caching module, because of that in worst cases this could allow an authorized user to cause the page to be cached, then an unauthorized user would later be served the cached page. The Authorize filter solves this problem by telling proxies not to cache the sensitive page, then hook the custom authorization code into the caching mechanism so that it has the final say on whether a page should be served from the cache or not.

Unless we are planning to create a complete new way of authorization the better idea would be extending the AuthorizeAttribute class instead of implementing the interface.

Here is our custom authorization filter implementation.

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
	public string UsersConfigKey { get; set; }

	public string RolesConfigKey { get; set; }

	protected override bool AuthorizeCore(HttpContextBase httpContext)
	{
		var authorizedUsers = ConfigurationManager.AppSettings[UsersConfigKey];
		var authorizedRoles = ConfigurationManager.AppSettings[RolesConfigKey];

		Users = String.IsNullOrEmpty(Users) ? String.Format("{0}, {1}", Users, authorizedUsers) : authorizedUsers;
		Roles = String.IsNullOrEmpty(Roles) ? String.Format("{0}, {1}", Roles, authorizedRoles) : authorizedRoles;

		return base.AuthorizeCore(httpContext);
	}
}

Listing 9. Custom authorization filter

Our CustomAuthorizeAttribute class provides two properties: UsersConfigKey and RolesConfigKey by which we can assign the users and roles who are authorized for an access through web.config. The custom class reads the users and roles from the configuration and append to the base class's Users and Roles properties.

<appSettings>
	<add key="britishagents" value="007, 009" />
</appSettings>

Listing 10. Specifying the users authorized for action in web.config

[CustomAuthorize(UsersConfigKey = "britishagents")]
public ActionResult Secret()
{
	return View(new SecretCode("Indian Rupee"));
}

Listing 11. Applying CustomAuthorize

Action filters

Action filters are called before or after executing an action. All the action filters should implement the interface IActionFilter. The IActionFilter interface contains two methods OnActionExecuted and OnActionExecuting.

public interface IActionFilter
{
	void OnActionExecuting(ActionExecutingContext filterContext);
	void OnActionExecuted(ActionExecutedContext filterContext);
}

Listing 12. IActionFilter

The OnActionExecuting method is called before executing the action and the OnActionExecuted method is called after the action is executed (this doesn't include the result generation). The ActionExecutingContext and ActionExecutedContext classes both derive from the ControllerContext which provides access to the controller and other information.

Let see how to create a custom ActionFilter that records the request information to database whenever an action receives the request.

We want to log the request information before the action is executed. We have to only implement the OnActionExecuting method and leave the other method with empty body.

public class AuditFilter : FilterAttribute, IActionFilter
{
	private readonly IAuditRepository _auditRepo;

	public AuditFilter(IAuditRepository auditRepo)
	{
		_auditRepo = auditRepo;
	}

	public void OnActionExecuted(ActionExecutedContext filterContext)
	{
	}

	public void OnActionExecuting(ActionExecutingContext filterContext)
	{
		var requestInfo = new RequestInfo
		{        
			User = filterContext.HttpContext.User.Identity.Name,
			IP = filterContext.HttpContext.Request.UserHostAddress,
			Time = DateTime.UtcNow,
			Url = filterContext.HttpContext.Request.RawUrl
		};

		_auditRepo.RecordRequest(requestInfo);
	}
}

Listing 13. Custom action filter

Since we want our AuditFilter to be applied over all the actions we have to add it to the global filters collection in the RegisterGlobalFilters method in Global.asax.cs.

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
	filters.Add(new AuditFilter());
}

Listing 14. Applying AuditFilter as a global filter

Result filters

Result filters are called before and after generating the result of an action. The result may be a view, JSON or any other thing that derives from the ActionResult class. These filters are called after the Action filters. Below listing shows the definition of the IResultFilter that these filters should implement.

public interface IResultFilter
{
	void OnResultExecuted(ResultExecutedContext filterContext);
	void OnResultExecuting(ResultExecutingContext filterContext);
}

Listing 15. IResultFilter

ActionFilterAttribute

MVC comes with a built-in abstract class named ActionFilterAttribute that combines both the Action and Result filters by implementing both the IActionFilter and IResultFilter.

The ActionFilterAttribute abstract class helps to create filters that depends upon both the action and result execution.

Let create a simple filter that measures the time taken by executing an action and generating the result. We need to override only two methods the OnActionExecuting and OnResultExecuted. Below listing shows the filter implementation.

public class DurationAttribute : ActionFilterAttribute
{
	private Stopwatch watch;

	public override void OnActionExecuting(ActionExecutingContext filterContext)
	{
		watch = Stopwatch.StartNew();
	}

	public override void OnResultExecuted(ResultExecutedContext filterContext)
	{
		watch.Stop();

		filterContext.HttpContext.Response.Write(string.Format("Elapsed time: {0}",
													watch.Elapsed.TotalSeconds));
	}
}

Listing 16. Implementing ActionFilterAttribute

In the OnActionExecuting method we are starting the timer and in the OnResultExecuted method we are writing the elapsed time into the Response.

Exception filters

Exception filters are called when exception occurs in actions or filters. The exception filters implements the interface IExceptionFilter.

public interface IExceptionFilter
{
	void OnException(ExceptionContext filterContext);
}

Listing 17. IExceptionFilter

Like authorization filter, the MVC framework comes with a default implementation of IExceptionFilter called HandleErrorAttribute. When the HandleErrorAttribute filter receives the exception it returns an Error view located in the Views/Shared folder. The HandleErrorAttribute can be configured to return different views for different exceptions. The HandleErrorAttribute works only if the <customErrors> section is turned on in web.config.

HandleErrorAttribute handle exceptions only if the <customErrors> is turned on in web.config.
[HandleError(ExceptionType = typeof(DatabaseException), View = "DatabaseError")]
[HandleError]
public ViewResult Contacts()
{

}

Listing 18. Applying HandleErrorAttribute filter multiple times

We have decorated the above action method with two HandleError attributes. The first one will handle only exceptions of type DatabaseException and returns a view named DatabaseError from the shared folder. The second one handles all the exceptions and returns the default Error view.

In most cases the built-in exception filter is not sufficient because it not logs the exceptions and all it does is return a custom error view. Let's create a custom exception filter that logs the handled exceptions to database using ELMAH.

ELMAH is an error logging module that handles exceptions and logs in an ASP.NET application. It also provides a custom page where the admin can see the latest errors, it also helps to send emails, generate RSS feeds for exceptions.
public class LogHandleErrorAttribute: HandleErrorAttribute
{
	public override void OnException(ExceptionContext filterContext)
	{
		var exceptionHandled = filterContext.ExceptionHandled;
	
		base.OnException(exceptionContext);

		if(!exceptionHandled && filterContext.ExceptionHandled)
			ErrorSignal.FromCurrentContext().Raise(filterContext.Exception);
	}
}

Listing 19. Custom exception filter

Instead of implementing the IExceptionFilter we have derived the HandleErrorAttribute to do the logging. The built-in filter sets the ExceptionHandled property to true once it handles the exception and this helps to avoid an exception being handled more than one time. So if the exception is handled then we signal ELMAH to log the exception which is available in the Exception property of the ExceptionContext.

Ordering filters

The order of a filter in which it run depends upon the type of the filter, the scope at which it applied and the value of the Order property. Regarding the types of filters, the Authorization filters runs at first and the Exception filters runs at last. Regarding the same type of filters the priority of execution depends upon the scope and order.

The order of a filter in which it run depends upon the type of the filter, the scope at which it applied and the value of the Order property.

Let's first see how the scope affects the priority of filters by creating a simple action filter TrackFilter that writes a simple text message to the response.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class TrackFilter: FilterAttribute, IActionFilter
{
	private readonly string _level;

	public TrackFilter(string level)
	{
		_level = level;
	}

	public void OnActionExecuted(ActionExecutedContext filterContext)
	{
	}

	public void OnActionExecuting(ActionExecutingContext filterContext)
	{
		filterContext.HttpContext.Response.Write
			(string.Format("Filter is executing in the \"{0}\" level
", _level)); } }

Listing 20. TrackFilter

Apply the TrackFilter at three scopes: controller, action and global.

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
	filters.Add(new TrackFilterAttribute("Global"));
}

[TrackFilter("Controller")]
public class HomeController : Controller
{
	[TrackFilter("Action")]
	public ActionResult Index()
	{
		return View();
	}
}

Listing 21. Applying TrackFilter at all three scopes

On calling the Home controller's Index action we will see the following result,

Filter is executing in the "Global" level 
Filter is executing in the "Controller" level
Filter is executing in the "Action" level

The result clearly reveals that the action filter that is applied at global scope is executed first followed by the ones applied at controller and action scopes.

All the scopes are defined in FilterScope enumeration.

public enum FilterScope 
{
	First = 0,
	Global = 10,
	Controller = 20,
	Action = 30,
	Last = 100
}

Listing 21. FilterScope enumeration

A default the scope that has the lowest value executes first and that is not in all types of filters and at-least in the case of Authorization and Action filters. Since the value of Global is lesser than the Controller and Action and so it runs first.

But what would we do if we want to reverse the order? there comes the Order property.

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
	filters.Add(new TrackFilterAttribute("Global"){ Order = 3 });
}

[TrackFilter("Controller", Order = 2)]
public class HomeController : Controller
{
	[TrackFilter("Action", Order = 1)]
	public ActionResult Index()
	{
		return View();
	}
}

Listing 22. Applying TrackFilter with Order

The output is,

Filter is executing in the "Action" level 
Filter is executing in the "Controller" level
Filter is executing in the "Global" level

The action filter that has the lowest order is executed first.

Unlike the other filter types, things works in opposite way in Exception filters. In the case of Exception filters the filter with lowest scope/order runs at last in a reverse order compared to the other types. There is a good article that explains much about the ordering of filters here.

Like Exception filters the OnActionExecuted method of Action filters and OnResultExecuted of Result filters runs in a reverse order.

Cancelling filters

An filter can be cancelled while running at the OnActionExecuting or OnResultExecuting method by setting an action result to the Result property or by throwing an exception. When we cancel an action filter at the OnActionExecuting method the OnActionExecuted method won't be called, also the pending OnActionExecuted and OnActionExecuting of the other filters will not be invoked as well. All the OnResultExceuting and OnResultExecuted methods of the Result filters will be called as well.

Other important built-in filters

OutputCache filter

Caching greatly improves the performance of a website. In MVC, the OutputCache filter is used to cache the result of an action.

[OutputCache(VaryByParam="none", Duration=60)]
public ViewResult Index()
{  
    return View();
}

Listing 23. OutputCache filter

The OuputCache filter implements both ActionFilterAttribute and IExceptionFilter. The OutputCache filter has some limitations when applied to child actions especially they don't obeys the cache settings applied in the web.config. ASP.NET MVC 3 doesn't supports donut caching (caching a complete page except a portion) and there is an open-source avialable here for the rescue.

ASP.NET MVC supports only donut-hole caching (caching only portion of a page) not donut caching (caching a complete page except a portion).

RequireHttps

This attribute helps to force the client to access a particular action to be accessed over HTTPS channel. If the action is not accessed over HTTPS then the filter redirect the user to access the same over HTTPS. The important thing is this filter works only for the GET requests and for the other types it throws an InvalidOperationException.

[RequireHttps]
public ActionResult Login(string name, string password, string redirect)
{

}

Listing 24. RequireHttps filter

RequireHttps filter only works on GET requests.

ChildActionOnly

Child actions are used to create reusable UI controls or widgets that need to contain business logic. When we use a child action, it invokes an action method, renders a partial view, and injects the result into the response. Child actions cannot be called directly called from the browser.

public ViewResult Blog()
{
   return View();
}

Listing 25. Main action that returns view

[ChildActionOnly]
public ViewResult Comments()
{
   ...
   return PartialView(comments);
}

Listing 26. Child action that returns a partial view

Child action can be called from the view using the Html.Action or Html.RenderAction method.

@model ...

// call the child action and render the partial view
@Html.Action("Comments")

Listing 27. Calling the child action from view

Summary

In this article we explored about the filters and different types of filters in MVC. We also discussed how to create custom filters and how to order the filters and cancel their execution. Please share your comments.

blog comments powered by Disqus