Filters

This section documents ASP.NET MVC 5.3.x, ASP.NET Web API 2.2, ASP.NET Web Pages 3, OWIN/Katana, SignalR 2, and ASP.NET Identity 2 — all running on .NET Framework 4.8.1 — the System.Web-hosted MVC framework, its routing, Razor views, HTML helpers, model binding, filters, and the OWIN-based authentication/Identity stack — as described by the official documentation at Microsoft Learn (plus Web API, Web Pages, SignalR, and Identity), which are the reference these pages are written and verified against.

This is not ASP.NET Core MVC. ASP.NET MVC 5 is the pre-Core, System.Web-hosted MVC framework; it is functionally frozen and receives only security fixes. For the current, cross-platform MVC framework see MVC Controllers and Views under ASP.NET Core (Blazor).

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page documents ASP.NET MVC 5.3.x filters on .NET Framework 4.8.1 — not ASP.NET Core’s filter pipeline (same four kinds plus endpoint filters, different interfaces — see Filters and the MVC Pipeline under ASP.NET Core).

The four filter kinds

Kind Interface Runs

Authorization

IAuthorizationFilter

First, before model binding — decides whether the request may proceed at all.

Action

IActionFilter

OnActionExecuting before the action runs; OnActionExecuted after it returns, before the result executes.

Result

IResultFilter

OnResultExecuting before ActionResult.ExecuteResult; OnResultExecuted after.

Exception

IExceptionFilter

Only if an unhandled exception propagates out of model binding, action execution, or result execution.

[Authorize]                                    // authorization filter
[RequireHttps]                                  // authorization filter
public class AccountController : Controller
{
    [AllowAnonymous]                            // opts this action out of [Authorize] above
    public ActionResult Login() => View();

    [HttpPost]
    [ValidateAntiForgeryToken]                  // authorization filter -- validates the CSRF token
    public ActionResult Login(LoginViewModel model) { ... }

    [OutputCache(Duration = 60, VaryByParam = "none")]   // result filter
    public ActionResult Terms() => View();

    [HandleError(ExceptionType = typeof(SqlException), View = "DatabaseError")]  // exception filter
    public ActionResult Report() { ... }
}

Execution order and pipeline

flowchart TD A[Authorization filters] -->|all authorize| B[Model binding] A -->|any denies| Z1[401/403 result, pipeline short-circuits] B --> C[Action filters: OnActionExecuting] C --> D[Action method executes] D --> E[Action filters: OnActionExecuted] E --> F[Result filters: OnResultExecuting] F --> G[ActionResult.ExecuteResult] G --> H[Result filters: OnResultExecuted] D -.exception.-> X[Exception filters] G -.exception.-> X X -->|handled| H X -->|unhandled| Y[ASP.NET default error handling]

Within a scope, filters of the same kind run in Order (ascending; default -1 means "unspecified, runs before explicitly ordered ones") and then by FilterScope (First, Global, Controller, Action, Last) when Order ties — global filters generally run outermost, action-level filters innermost, for the "before" half, and in reverse for the "after" half.

Global filters

FilterConfig.RegisterGlobalFilters (called from Application_Start, see Getting Started) registers filters that apply to every action in the application, without decorating each controller:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        filters.Add(new RequireHttpsAttribute());
    }
}

[ChildActionOnly]

Marks an action so it can only be invoked via Html.Action/Html.RenderAction (see Views, Layouts, and Partials) — a direct browser request to its URL returns an error. Typical for a widget-like action that only makes sense embedded in a page:

[ChildActionOnly]
public ActionResult CartSummary() => PartialView(_cart.GetSummary());

Filter overrides

[OverrideAuthorization] (and the analogous [OverrideActionFilters] etc. from MVC 5) lets an action or controller opt out of filters that would otherwise apply from a higher scope (global or controller-level), without removing the global registration itself:

[Authorize(Roles = "Admin")]
public class AdminController : Controller
{
    [OverrideAuthorization]
    [AllowAnonymous]
    public ActionResult Status() => Content("ok");   // bypasses the controller-level [Authorize] entirely
}

IFilterProvider and DI in filters

By default, attribute-based filters are constructed with a parameterless constructor, so they cannot easily take a DI-resolved dependency. A custom IFilterProvider (or a "filter attribute as a marker + separately registered filter implementation" pattern, using FilterAttributeFilterProvider alongside a DI-aware provider) lets filters be resolved from a container instead:

public class AuditFilterAttribute : FilterAttribute, IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext ctx)
    {
        var audit = DependencyResolver.Current.GetService<IAuditLogger>();  // classic MVC 5 service-locator seam
        audit.Log(ctx.ActionDescriptor.ActionName);
    }
    public void OnActionExecuted(ActionExecutedContext ctx) { }
}

DependencyResolver.Current is MVC 5’s built-in service-locator abstraction that most third-party containers (Ninject, Autofac, Unity) plug into via IDependencyResolver.

Writing a custom filter

public class LogExecutionTimeAttribute : ActionFilterAttribute       // ActionFilterAttribute implements both
{                                                                     // IActionFilter and IResultFilter
    public override void OnActionExecuting(ActionExecutingContext ctx)
        => ctx.HttpContext.Items["__stopwatch"] = Stopwatch.StartNew();

    public override void OnResultExecuted(ResultExecutedContext ctx)
    {
        var sw = (Stopwatch)ctx.HttpContext.Items["__stopwatch"];
        sw.Stop();
        Trace.TraceInformation($"{ctx.RouteData.Values["action"]} took {sw.ElapsedMilliseconds}ms");
    }
}

See Understanding Action Filters for the full ActionFilterAttribute base class and Testing and Diagnostics for unit-testing filters in isolation.

Next: Bundling and Client-Side Integration moves from the server pipeline to the client-side assets MVC 5 ships.