Wednesday, 30 January 2013

ASP.NET MVC Filters

Filters are .NET attributes that add extra steps to the request processing pipeline. MVC Filters supports to perform logic, either before an action method is called or after an action method runs. Attributes are special .NET classes derived from System.Attribute.

Types of Filters:

The MVC Framework supports four different types of filters.

Filter Type
Interface
Default Implementation
Description
Authorization IAuthorizationFilter AuthorizeAttribute Runs first, before any other filters or the action method
Action IActionFilter ActionFilterAttribute Runs before and after the action method
Result IResultFilter ActionFilterAttribute Runs before and after the action result is executed
Exception

IExceptionFilter

HandleErrorAttribute Runs only if another filter, the action method, or the action result throws an exception

The framework includes default attribute classes that implement the filter interfaces. The Controller class implements each of the filter interfaces. We can implement filters for a specific controller by overriding the controller's On<Filter> method. We can implement the following On<Filter> methods in a controller:
  • OnAuthorization
  • OnException
  • OnActionExecuting
  • OnActionExecuted
  • OnResultExecuting
  • OnResultExecuted 
Filters run in the following sequence:
  • Authorization filters
  • Action filters
  • Result filters
  • Exception filters
How To Create a Filter:
Filter can create in the following ways:
  • Override one or more of the controller's On<Filter> methods.
  • Create an attribute class that derives from FilterAttribute and apply the attribute to a controller or an action method.
  • Register a filter with the filter provider (the FilterProviders class).
  • Register a global filter using the GlobalFilterCollection class.
Applying Filters to Controllers and Action Methods: Filter can be applied to an entire controller or action methods. We can also apply multiple filters and can manage order of execution of those custom filters.

Example:
[Authorize] // controller level attribute/filter - applies to all actions
public partial class AccountController : Controller
{
      
       [OutputCache(Duration = 60)] // applies to this action
[HeaderType] // applies to this action
[HttpGet]
        public ActionResult GetAccountHeader()
        {
           
        }
}

Authorization Filters: Authorization filters run first before the other kinds of filters and action method is invoked. This filter use to authenticate user before executing action methods. Authorization filters implement the IAuthorizationFilter interface

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

A Custom Authentication Filter:
Example:
public class DemoAuthAttribute : AuthorizeAttribute
{
    private string[] _roles;
    public DemoAuthAttribute(params string[] roles)
    {
        _roles = roles;
    }
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        return httpContext.Request.IsAuthenticated && httpContext.User.IsRole(_roles);
    }
    public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            if (AuthorizeCore(filterContext.HttpContext))
            {
                // your custom logic here
               string text = string.Format("<u><h5>Auth successfull.....</h5></u></br>");
                filterContext.HttpContext.Response.Write(text);
            }
            else
            {
                // RedirectResult, etc.
                string text = string.Format("<u><h5>Auth unsuccessfull.....</h5></u></br>");
                filterContext.HttpContext.Response.Write(text);
            }
        }
}

The simplest way to create an authorization filter is to subclass the AuthorizeAttribute class and overrides the AuthorizeCore method. This ensures that we benefit from the features built in to AuthorizeAttribute.


By default AuthorizeCore gets called by both OnAuthorize and OnCacheAuthorization. This allows the authorization to be cached but still allow certain actions and to make the actual decisions about the authorization. If we need something from the AuthorizationContext then we can create a property to hold the information and then access that in the AuthorizeCore method.


 AuthorizationContext Properties

Name Type Description
ActionDescriptor ActionDescriptor Provides details of the action method
Result ActionResult The result for the action method; a filter can cancel the request by setting this property to a non-null value

Exception Filters: Exception filters are run only if an unhandled exception has been thrown when invoking an action method. The exception can come from another kind of, the action method itself and when the action result is executed.

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

Name Type Description
ActionDescriptor ActionDescriptor Provides details of the action method
Result ActionResult The result for the action method; a filter can cancel the request by setting this property to a non-null value
Exception Exception The unhandled exception
ExceptionHandled bool Returns true if another filter has marked the exception as handled

Example:
public class MyExceptionAttribute : FilterAttribute, IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        if (!filterContext.ExceptionHandled &&
        filterContext.Exception is NullReferenceException)
        {
            filterContext.Result = new RedirectResult("/ErrorPage.html");
            filterContext.ExceptionHandled = true;
        }
    }
}

[MyException]
public ActionResult Index() {
}

Action and Result FiltersAction and result filters follows similar pattern and runs before and after the action method/result. ActionFilterAttribute class implements both IActionFilter and IResultFilter.

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

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

OnActionExecuting: The OnActionExecuting method run before the action method is invoked. We can modify/cancel the request or do some activity.

OnActionExecutedThis filter use to perform some activity after execution of the action method.

ActionExecutingContext   is a Subclass of ControllerContext which is passing as parameter in the method.  ActionExecutingContext   defines following properties which are described below.

Name Type Description
ActionDescriptor ActionDescriptor Provides details of the action method
Result ActionResult The result for the action method; a filter can cancel the request by setting this property to a non-null value
Canceled Bool Returns true if the action has been canceled by another filter
Exception Exception The unhandled exception
ExceptionHandled bool Returns true if another filter has marked the exception as handled

Example: 
    public class ExecutionTimeAttribute : FilterAttribute, IActionFilter
    {
        private Stopwatch timer;

        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            timer = Stopwatch.StartNew();
        }
        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            timer.Stop();
            if (filterContext.Exception == null)
            {
                filterContext.HttpContext.Response.Write(
                string.Format("Action method execution time: {0}", timer.Elapsed.TotalSeconds));
            }
        }
    }

OnResultExecuting: The OnResultExecuting method is invoked once the action method has returned an action result, but before the action result is executed.

OnResultExecuted: The OnResultExecuted method is invoked after the action result is executed.

The parameters to these methods are ResultExecutingContext and ResultExecutedContext objects, respectively, and they are very similar to their action filter counterparts.

Example:
    public class ExecutionTimeResultAttribute : FilterAttribute, IResultFilter
    {
        private Stopwatch timer;
        public void OnResultExecuting(ResultExecutingContext filterContext)
        {
            timer = Stopwatch.StartNew();
        }
        public void OnResultExecuted(ResultExecutedContext filterContext)
        {
            timer.Stop();
            filterContext.HttpContext.Response.Write(
            string.Format("Result execution - elapsed time: {0}",
            timer.Elapsed.TotalSeconds));
        }
    }

[ExecutionTime]
[ExecutionTimeResult]
public ActionResult Index()
{
     return View();
}

Override Filters in a Controller: Create a controller class that implements filter interfaces and can use as base controller so that other controller can use functionality across application. Through this we can put code that is required across the application in one reusable location.

Example:
public class BaseController : Controller
{
   
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        //write your logic here
    }
    protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        //write your logic here
    }
    protected override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        //write your logic here
    }
    protected override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        //write your logic here
    }
}

Global Filter: Global filters are a way of applying filters in all action methods in application. The advantage of this is we no longer need to apply attribute s on every controller or action methods.
We can make a regular filter into a global filter through the RegisterGlobalFilters method in Global.asax. The RegisterGlobalFilters method is called from the Application_Start method, which ensures that the filters are registered when your MVC application starts.

Example:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new ExecutionTimeResultAttribute());
    filters.Add(new MyExceptionAttribute());
}

Filter Scope and Order:  

The Scope enumeration value specifies the order for filters. This enumeration defines the following filter scope:

Name
Description
First
Specifies first.
Global
Specifies an order before Controller and after First.
Controller
Specifies an order before Action and after Global.
Action
Specifies an order before Last and after Controller.
Last
Specifies last.

We can also take control execution order of filter to specify order if scope is same.

For example, an OnActionExecuting(ActionExecutingContext) filter that has the Order property set to zero and filter scope set to First runs before an action filter that has the Order property set to zero and filter scope set to Action. Because exception filters run in reverse order, an exception filter that has the Order property set to zero and filter scope set to First runs after an action filter that has the Order property set to zero and filter scope set to Action.

The execution order of filters that have the same type, order, and scope is undefined.
The OnActionExecuting(ActionExecutingContext), OnResultExecuting(ResultExecutingContext), and OnAuthorization(AuthorizationContext) filters run in forward order. The OnActionExecuted(ActionExecutedContext), OnResultExecuting(ResultExecutingContext), and OnException(ExceptionContext) filters run in reverse order.

If we don’t specify a value for the Order property, it is assigned a default value of -1. This means that if you mix filters so that some have Order values and others don’t, the ones without these values will be executed first, since they have the lowest Order value.

Example:
// AttributeTargets  : Attribute can be applied to a method and class.
// AllowMultiple : True if more than one instance is allowed to be specified;
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public class SampleAttribute : FilterAttribute, IActionFilter
{
    public string ExecutionOrder { get; set; }
    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.HttpContext.Response.Write(ExecutionOrder);
    }
    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        filterContext.HttpContext.Response.Write(ExecutionOrder);
    }
}

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new SampleAttribute() { Message = "Global", Order = 1 });
}

[Sample(Message = "Controller", Order = 1)]
public class DemoController : Controller
{
    [Sample(Message = "Action", Order = 1)]
    [Sample(Message = "Action2", Order = 2)]
    public ActionResult Index()
    {
        Response.Write("Action method");
        return View();
    }
}

Order of execution for OnActionExecuting would be:
Global => Contoller => Action => Action2 =>  Action method
Order of execution for OnActionExecuted would be:
Action method => Action2  => Action => Contoller => Global


It's important to note here that "Controller" scope means "filters applied at the controller level"; the controller itself is also a filter which always runs first (that is, its order is Int32.MinValue and Scope is First). The execution order of filters with the same order and scope is undefined.

Canceling Filter Execution: We can cancel filter execution in the OnActionExecuting and OnResultExecuting methods by setting the Result property to a non-null value. Any pending OnActionExecuted and OnActionExecuting filters will not be invoked and the invoker will not call the OnActionExecuted method for the canceled filter or for pending filters. The OnActionExecuted filter for previously run filters will run. All of the OnResultExecutingand OnResultExecuted filters will run.

Example:
if (filterContext.RouteData.Values.ContainsValue("Cancel")) {
    filterContext.Result = new RedirectResult("~/Home/Index");
}

Other important built-in filters:
OutputCache: The OutputCache filter tells the MVC Framework to cache the output from an action method so that the same content can be reused to service subsequent requests for the same URL. There is number of parameters in OutputCache filter to manage caching.

Example:
[OutputCache(Duration = 30)]
public ActionResult About()
{
   return View();
}

RequireHttps: The RequireHttps filter 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.

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

}

ChildActionOnly: A child action method renders inline HTML markup for part of a view instead of rendering a whole view. Child actions cannot be called directly called from the browser. Any method that is marked with ChildActionOnlyAttribute can be called only with the Action or RenderAction HTML extension methods.

Example:
[ChildActionOnly]
public ViewResult Header()
{
    string ="This is header";
    return PartialView(header);
}

@Html.Action("Header")
@Html.RenderAction("Header")

Other Posts: MVC3 Overview | Controller and Actions

4 comments:

  1. I am lucky that I found this web blog, precisely the right information that I was searching for about "The MVC Framework supports four different types of filters". Carry on the great work to inspire people. I'm sure it would be a great read for ASP.NET lovers.

    See Optimized360's 8 impressive and economical Medical Marketing designed mainly for physicians of all specialties.

    ReplyDelete