There are different types of filters in MVC. Depending on the type of the Filter, and the scope of the Filter, the order of the execution also changes.

You may already aware that there are different types of filters within MVC framework. They are listed below.

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

The above list is ordered as exactly the order they executes within MVC framework. The Authorization filters always run first, and the Exception filters run at the end.

Specifying the Order property for Filters

Within each filter, you may specify the Order property. (All filters are derived from the abstract class FilterAttribute, and this class has an Order property). This property will ensure the filter runs in a specific Order. For an example, let’s say we have 2 Authorization filters, AuthorizationFilterA and AuthorizationFilterB and they are implemented as below.


public class AuthorizationFilterA : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        Debug.WriteLine("OnAuthorization : AuthorizationFilterA");
    }
}

public class AuthorizationFilterB : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        Debug.WriteLine("OnAuthorization : AuthorizationFilterB");
    }
}

Now, if I want these two filters to run in a specific order - AuthorizationFilterB to run first, and AuthorizationFilterA to run second, I can simply specify the execution order as below.


[AuthorizationFilterA(Order=2)]
[AuthorizationFilterB(Order=1)]
public ActionResult Index()
{
    return View();
}

When you run the code, the Output window should display that AuthorizationFilterB runs first, AuthorizationFilterA runs second.

OnAuthorization : AuthorizationFilterB

OnAuthorization : AuthorizationFilterA

Filter Scope

Within each filter type, you can define a scope. For an example, I could scope all my Authorization Filters to run within the Controller Scope, and all Action Filters to run in Global scope (Every Action within the MVC application).

FilterScope.Global is a new scope which was introduced in MVC 3. The existing scopes are First, Controller, Action, and Last. The complete enumeration is defined as below.

namespace System.Web.Mvc {
    public enum FilterScope
        First = 0,
        Global = 10,
        Controller = 20,
        Action = 30,
        Last = 100,
    }
}

By default, the filters with the lowest scope runs first. For an example, scope Global (10) executes first and scope Controller (20) executes second.


Filter Scope with explicit Ordering

Below example shows, how explicit filter ordering (using Order property) and scope of the filters determine their correct run order.

Let’s add the scope to our existing Authorization Filters – AuthorisationFilterA and AuthorisationFilterB. Please note that we haven’t changed the usage of Order property.

AuthorizationFilterA(Order 2) has the scope Global.

AuthorizationFilterB(Order 1) has the scope Action.

public class AuthorizationFilterA : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        Debug.WriteLine("OnAuthorization : AuthorizationFilterA (Order: 2) in scope : Global");
    }
}

public class AuthorizationFilterB : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        Debug.WriteLine("OnAuthorization : AuthorizationFilterB (Order: 1) in scope : Action");
    }
}


[AuthorizationFilterB(Order = 1)]
public ActionResult Index()
{
    return View();
}

GobalFilters.Filters.Add(new AuthorizationFilterA() { Order = 2});

According to the above Filter Scope enumeration, if we were to specify only the scope but not the Order property, then we would expect AuthorizationFilterA (in Global scope) to run first, and AuthorizationFilterB (in Action scope) to run second. Since we are explicitly specifying the Order property for both filters, AuthorizationFilterB (in Action scope – Order 1) runs first and AuthorizationFilterA (in Global scope – Order 2) runs second.


Output:

OnAuthorization : AuthorizationFilterB (Order: 1) in scope : Action

OnAuthorization : AuthorizationFilterA (Order: 2) in scope : Global

Now let’s add another Authorization filter (AuthorizationFilterC) to Global scope. Also note that we have not specified the Order property.

public class AuthorizationFilterC : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            Debug.WriteLine("OnAuthorization : AuthorizationFilterC (no Order defined) in scope : Global");
        }
    }

GlobalFilters.Filters.Add(new AuthorizationFilterC());

After running all three filters, you can see the output as below. 

Output

OnAuthorization : AuthorizationFilterC (no Order defined) in scope : Global

OnAuthorization : AuthorizationFilterB (Order: 1) in scope : Action

OnAuthorization : AuthorizationFilterA (Order: 2) in scope : Global

For same Filter type, the filters without the explicit ordering (i.e AuthorizationFilterC) take precedence over the filters with explicit ordering (AuthorizationFilterB, AuthorizationFilterA).

Now let’s remove the Order property from both AuthorizationFilterB and AuthorizationFilterA

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

GlobalFilters.Filters.Add(new AuthorizationFilterA());

Output:

OnAuthorization : AuthorizationFilterA (no order defined) in scope : Global

OnAuthorization : AuthorizationFilterC (no order defined) in scope : Global

OnAuthorization : AuthorizationFilterB (no order defined) in scope : Action

As we would expect, the filters with scope Global (AuthorizationFilterA and AuthorizationFilterC) run first and second in order, the scope Action filter (AuthorizationFilterC) runs at the end. 2 Global filters (AuthorizationFilterA and AuthorizationFilterC) run order relies on the order which they were added to the Global Filter collection. In this example AuthorizationFilterA which was added prior to AuthorizationFilterC , runs first.

For the same filters, when determine the run order, first it sorts them by their order (lowest numbers first), and then by their scope (also lowest numbers first).

Controller as a Filter

In MVC the Controller itself is also a Filter. Controller implements all filters, so Controller can subscribe to all filter events.

public abstract class Controller : ControllerBase,
IActionFilter, IAuthorizationFilter, IDisposable,
IExceptionFilter, IResultFilter

MVC framework ensures, the Controller always runs first, before any other filters get executed. It has been accomplished by specifying the scope as the First and the order property as Int32.MinValue. By default, an MVC filter has the value for the Order -1. Therefore Int32.MinValue along with the Scope First together ensures the Controller always runs first.

public class ControllerInstanceFilterProvider : IFilterProvider {
        public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
            if (controllerContext.Controller != null) {
               
// Use FilterScope.First and Order of Int32.MinValue to ensure controller instance methods always run first
                yield return new Filter(controllerContext.Controller, FilterScope.First, Int32.MinValue);
            }
        }
    }

Forward Filter Order and Reverse Filter Order

So far we have discussed the order of filters which get executed based on the explicit ordering (using Order property) and the scope which they can be belong to. However, MVC filters run order, do not only rely only by these two factors. Sometimes filters run in forward order and sometimes filters run in reverse order. Let’s see the below example.

Let’s say I have three Action filters (ActionFilter1, ActionFilter2, ActionFilter3), one Authorization filter (AuthorizationFilter) and two Exception filters (HandleErrorA, HandleErrorB). They are implemented as below.

public class ActionFilter1 : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        Debug.WriteLine("Forward Order - OnActionExecuting : ActionFilter1 (Scope Global)");
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        Debug.WriteLine("Reverse Order - OnActionExecuted : ActionFilter1 (Scope Global)");
    }

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        Debug.WriteLine("Forward Order - OnResultExecuting : ActionFilter1 (Scope Global)");
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        Debug.WriteLine("Reverse Order - OnResultExecuted : ActionFilter1 (Scope Global)");
    }
}


public class ActionFilter2 : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        Debug.WriteLine("Forward Order - OnActionExecuting : ActionFilter2 (Scope Controller)");
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        Debug.WriteLine("Reverse Order - OnActionExecuted : ActionFilter2 (Scope Controller)");
    }

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        Debug.WriteLine("Forward Order - OnResultExecuting : ActionFilter2 (Scope Controller)");
    } 

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        Debug.WriteLine("Reverse Order - OnResultExecuted : ActionFilter2 (Scope Controller)");
    }
}

public class ActionFilter3 : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        Debug.WriteLine("Forward Order - OnActionExecuting : ActionFilter3 (Scope Action)");
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        Debug.WriteLine("Reverse Order - OnActionExecuted : ActionFilter3 (Scope Action)");
    } 

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        Debug.WriteLine("Forward Order - OnResultExecuting : ActionFilter3 (Scope Action)");
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        Debug.WriteLine("Reverse Order - OnResultExecuted : ActionFilter3 (Scope Action)");
    }
}    


public class AuthorizationFilter : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        Debug.WriteLine("Forward Order - OnAuthorization : AuthorizationFilter (Scope Controller)");
    }
}   


public class HandleErrorA : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        Debug.WriteLine("Reverse Order - OnException : HandleErrorA (Scope Action)");
    }
}

public class HandleErrorB : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        Debug.WriteLine("Reverse Order - OnException : HandleErrorB (Scope Action)");
    }
}

We will register the above filters as below…


ActionFilter1 has the scope Global, ActionFIlter2 has the scope Controller, ActionFIlter3 has the Scope Action, AuthorizationFilter has the scope Controller, HandleErrorA has the scope Global, and HandleErrorB has the scope Action.

At the beginning of this article I mentioned that the run order of different types of filters. First Authorization filters, then Action Filters, followed by Result filters and at the end the Exception filters. This is true, however within each type of filters, some executes in reverse order and some executes in forward order.

The best way to explain is to examine the generated output.

Forward Order - OnAuthorization : AuthorizationFilter (Scope Controller)

Forward Order - OnActionExecuting : ActionFilter1 (Scope Global)

Forward Order - OnActionExecuting : ActionFilter2 (Scope Controller)

Forward Order - OnActionExecuting : ActionFilter3 (Scope Action)

Home Controller, Index Action

Reverse Order - OnActionExecuted : ActionFilter3 (Scope Action)

Reverse Order - OnActionExecuted : ActionFilter2 (Scope Controller)

Reverse Order - OnActionExecuted : ActionFilter1 (Scope Global)

Forward Order - OnResultExecuting : ActionFilter1 (Scope Global)

Forward Order - OnResultExecuting : ActionFilter2 (Scope Controller)

Forward Order - OnResultExecuting : ActionFilter3 (Scope Action)

Reverse Order - OnResultExecuted : ActionFilter3 (Scope Action)

Reverse Order - OnResultExecuted : ActionFilter2 (Scope Controller)

Reverse Order - OnResultExecuted : ActionFilter1 (Scope Global)

Reverse Order - OnException : HandleErrorB (Scope Action)

Reverse Order - OnException : HandleErrorA (Scope Global)

-
There is only one Authorization Filter in Controller scope and it runs first as expected.

-
The Action Filters run at second. Notice that OnActionExecuting event runs in an order where the order of filter execution is determined by their scope (lowest number first in the enumeration). As you see, Action Filters OnActionExecuting event runs in a forward order.

-
Home Controller’s Index Action executes.

-
Action Filters do not always run in forward order. As you see during OnActionExecuted event, they run in a reverse order.

-
Very similar to OnActionExecuted event, OnResultExecuted event runs in reverse order.

-
Error Handler filters run at last. For each Error Handler filter, OnException event runs in  reverse order. Prior to MVC 3, ErrorHandler’s OnException event ran in forward order. This approach makes MVC exception filters more aligned with .NET exception handlers.

Summary

There are multiple factors involved when deciding the run order of MVC filters. This includes the explicit ordering, the scope they belong to, and the type of the filter. Controller itself is a filter and always run first. By design, MVC executes certain filters in specific order. Some are run in forward order and some are run in reverse order.