Action Filters

.Action Filters

Action Filters

Web page by Kevin Harris of Homer IL

Please contact Kevin Harris of Homer IL concerning this web site

"Action Filters are used to inject extra logic in the processing pipeline."


In MVC Action Methods are typically called as a result of a user action. A user clicks a link, or submits a form, which causes a request to be routed to a designated controller, and then within the controller, to a corresponding Action Method. Action Filters allow processing logic to be run before an action method is called or after the action method runs.

Action filters are implemented as attributes which are used to decorate an action method. These attributes can also be applied at the controller level which will affect every action method within the controller. The following example contains a controller which is decorated with the Authorize attribute which requires the user to be authenticated before he can run any of the action methods within the controller. However the Login action method is decorated with an AllowAnonymous attribute which overrides the controller-level attribute. This allows users who have not been authenticated to execute the Login action which will display the log in view (screen).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
namespace CathySite.Web.Controllers
{
    [Authorize]
    public class AccountController : Controller
    {
        [AllowAnonymous]       
        public ActionResult Login(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            return View();
        }

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();
            return RedirectToAction("Login", "Account");
        }
    }
}


Action Filter Attributes Applied at the Action Method and Controller Levels


Action filters can also be implemented at the global level, which will affect all action methods within all controllers. Action filters are registered globally in the Application_Start method inside the global.asax file. The following example shows how to create a custom action filter which writes the html comment <!-- Kevin Harris, Homer Illinois -->> to the response stream. The custom action filter is then registered globally, so any action method which returns a view will contain the comment.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
using System.Web.Mvc;

namespace MetriTech.IW.Interface.Web
{
    public class KevinActionFilterAttribute : ActionFilterAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            base.OnResultExecuting(filterContext);

            var response = filterContext.RequestContext.HttpContext.Response;

            response.Write("<!-- Kevin Harris, Homer Illinois -->"); 
        }
    }

}


Custom Action Filters

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
namespace MetriTech.IW.Interface.Web
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            GlobalFilters.Filters.Add(new KevinActionFilterAttribute());
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            AuthConfig.RegisterAuth();
        }


Registering Custom Action Filter at the Global Level

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<!-- Kevin Harris, Homer Illinois -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title> - Item Writing QTI Exporter</title>
    <link href="/favicon.ico" rel="shortcut icon" type="image/x-icon" />
    <meta name="viewport" content="width=device-width" />
    <link href="/Content/site.css" rel="stylesheet"/>

    <script src="/Scripts/modernizr-2.5.3.js"></script>

    <script src="/Scripts/jquery-1.7.1.js"></script>

</head>
<body>
    <header>
        <input id="Version_1_0_0_0" name="Version 1.0.0.0" type="hidden" value="" />


Results of Globally Registered Custom Action Filter


Built-in Action Filters

MVC provides several built-in action filters. Some of the built-in action filters are:

  1. Authorize - checks to see if user is authenticated. Also can check for authorization by comparing user's username or role against parameter values.

  2. OutputCache - directs ASP.NET to cache the output and serve cached output based on supplied parameters.

  3. HandleError - maps exceptions to specific View templates. By default maps to the error view located in the ~/Views/Shared folder.

  4. RequireHttps - forces a switch from http to https by redirecting GET request to the https version of the requested URL. Rejects non-https POST requests.

  5. ValidateAntiForgeryToken - checks to see if server request has been tampered with. Verifies request by comparing a hidden field value with a cookie value.

  6. ValidateInput - can be used to set Request Validation to false. To use, must also set requestValidationMode="2.0" in Web.config.

  7. ChildActionOnly - indicates action method can only be called as part of a parent request. For rendering inline markup instead of returning a full view.


Action Filter Types

MVC support the following four types of action filters. They are executed in the order listed:

  1. Authorization - implement authentication and authorization for controller actions. (IAuthorizationFilter)

  2. Action - contains logic that is executed before and after a controller action executes. (IActionFilter with methods:OnActionExecuting,OnActionExecuted)

  3. Result - contains logic that is executed before and after a view result is executed. (IResultFilter with methods:OnResultExecuting, OnResultExecuted)

  4. Exception - handles errors raised by controller actions or controller action results. (IExceptionFilter)

If there are multiple action filters within the same type, the order in which they execute are determined by the Order property on the action filter. Action filters with lower order values execute before those with greater values. An action filter which does not have an Order value specified is assigned a value of -1, which will executed in an undetermined order, but before action filters which have a specified order


Custom Action Filters

As an aid to creating custom action filters, MVC provides the following attribute classes which can be used to derive custom action filters:

  1. AuthorizeAttribute - implementation of IAuthorizationFilter which makes it easier to implement the custom authentication and authorization.

  2. ActionFilterAttribute - implementation of both IActionFilter and IResultFilter, with these methods: OnActionExecuting, OnActionExecuted, OnResultExecuting, OnResultExecuted.

  3. HandleErrorAttribute - implementation of IExceptionFilter which makes it easier to implement custom exception handling.

A class derived from ActionFilterAttribute is both an action filter and a result filter which has the following method which can be overridden:

  1. OnActionExecuting - method is called before a controller action is executed

  2. OnActionExecuted - method is called after a controller action is executed.

  3. OnResultExecuting - method is called before a controller action result is executed.

  4. OnResultExecuted - method is called after a controller action result is executed.

It is convenient to derive from ActionFilterAttribute when you wish to bundle both the action and result methods. However some people prefer to keep these methods separate by instead deriving from FilterAttribute and implementing either the IActionFilter or IResultFilter interface. An example of this is shown below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
using System.Web.Mvc;

namespace CathySite.Web
{
    public class ExmpleFilter : FilterAttribute, IActionFilter
    {

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            filterContext.HttpContext.Trace.Write("Action Executing: " +
                filterContext.ActionDescriptor.ActionName);

            base.OnActionExecuting(filterContext);
        }

    }

}


Deriving from FilterAttribute and Implementing IActionFilter


Customer Action Filter Code

Below is an example of a custom action filter derived from ActionFilterAttribute which writes to the trace log before an action is executed or if and exception is thrown.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System.Web.Mvc;

namespace MetriTech.IW.Interface.Web
{
    public class KevinActionFilterAttribute : ActionFilterAttribute
    {

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            filterContext.HttpContext.Trace.Write("Action Executing: " +
                filterContext.ActionDescriptor.ActionName);

            base.OnActionExecuting(filterContext);
        }

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (filterContext.Exception != null)
                filterContext.HttpContext.Trace.Write("Exception thrown");

            base.OnActionExecuted(filterContext);
        }

        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            base.OnResultExecuting(filterContext);

            var response = filterContext.RequestContext.HttpContext.Response;

            response.Write("<!-- Kevin Harris, Homer Illinois --></br>"); 
        }
    }

}


Custom Action Filter Derived from ActionFilterAttribute

.Trace Request Details


Log Message from Action Filter Shown in Trace Details



Error | ASP.NET Developer

Error

Error message

  • Warning: Cannot modify header information - headers already sent by (output started at /srv/disk9/1218369/www/kcshadow.net/aspnet/includes/common.inc:2748) in drupal_send_headers() (line 1232 of /srv/disk9/1218369/www/kcshadow.net/aspnet/includes/bootstrap.inc).
  • PDOException: SQLSTATE[42000]: Syntax error or access violation: 1142 INSERT command denied to user '1218369_b2cf'@'185.176.40.58' for table 'watchdog': INSERT INTO {watchdog} (uid, type, message, variables, severity, link, location, referer, hostname, timestamp) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9); Array ( [:db_insert_placeholder_0] => 0 [:db_insert_placeholder_1] => cron [:db_insert_placeholder_2] => Attempting to re-run cron while it is already running. [:db_insert_placeholder_3] => a:0:{} [:db_insert_placeholder_4] => 4 [:db_insert_placeholder_5] => [:db_insert_placeholder_6] => http://www.kcshadow.net/aspnet/?q=filters [:db_insert_placeholder_7] => [:db_insert_placeholder_8] => 54.80.87.62 [:db_insert_placeholder_9] => 1534851937 ) in dblog_watchdog() (line 160 of /srv/disk9/1218369/www/kcshadow.net/aspnet/modules/dblog/dblog.module).
The website encountered an unexpected error. Please try again later.