Wednesday, February 12, 2014

MVC Filters Part 3 - Action Filter and Action Result Filter


Action Filter -By Narendra Shrestha

Action filters are executed before and after the execution of the actions on which these filters are applied. Due to the nature of their execution, these filters are useful for any purpose. This filter is implemented when we want to introduce some logic prior and posterior to the execution of our action methods. Like other filters, it is customizable as well as has its default implementation (refer table 1 Filter Part 1).

To implement customizable feature, we need to implement interface “IActionFilter” in our custom class. The skeletal code for this implementation is as listed below:-

using System;

namespace System.Web.Mvc
{
    // Summary:
    //     Defines the methods that are used in an action filter.
    public interface IActionFilter
    {
        // Summary:
        //     Called after the action method executes.
        //
        // Parameters:
        //   filterContext:
        //     The filter context.
        void OnActionExecuted(ActionExecutedContext filterContext);
        //
        // Summary:
        //     Called before an action method executes.
        //
        // Parameters:
        //   filterContext:
        //     The filter context.
        void OnActionExecuting(ActionExecutingContext filterContext);
    }
}

Listing 1:- “IActionFilter” interface skeletal code

As expected this interface has two methods. “OnActionExecuting” method is called before the action method is executed.  Inside this method we can investigate the request, modify the request, cancel the request or even start another request. “OnActionExecuted” method is called after the action method is executed. This method can be used to set the result type or cancel the result.

The parameter for “OnActionExecuting” method is of type “ActionExecutingContext”. The parameter for “OnActionExecuted” method is of type “ActionExecutedContext”. Both of these types are derived from “ControllerContext” giving us access over important properties of controllers.

Afar from the derived properties, both of these parameters have their own properties.
ActionExecutingContext” has following properties:-

·         ActionDescriptor” of type “ActionDescriptor” which provides the detail of the current action method
·         Result” of type “ActionResult” states the result of the action method. The result can be set to null to cancel the entire request

ActionExecutedContext” has following properties:-

·         ActionDescriptor” of type “ActionDescriptor” which provides the detail of the current action method
·         Result” of type “ActionResult” states the result of the action method. The result can be set to null to cancel the entire request
·         Canceled” of type “bool” which indicates whether “ActionExecutedContext” has been cancelled by other action filters
·         Exception” of type “Exception” provides the exception thrown by other action filter of action methods
·         ExceptionHandled” of type “bool” which indicates if the exception has been handled

For implementation of the custom action filter, let us consider a situation when user is authenticated. The “username” and “password” are posted along with the request. Before executing action method, we may need to encrypt the password before matching it. This is the ideal case in the real projects. For our purpose we are using “MD5” algorithm. The password encrypted by this algorithm cannot be decrypted. This one way nature of “MD5” algorithm makes it one of the safest encryption ciphers.

MyActionAttribute” class is created inside “infrastructure” folder. This class is derived from “FilterAttribute” and it implements interface “IActionFilter”. The code of this class is as listed below:-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Text;
using System.Security.Cryptography;
using System.Web.Security;

namespace Filters.Infrastructure
{
    public class MyActionAttribute:FilterAttribute, IActionFilter
    {
        #region IActionFilter Members
        private string username;
        private string password;
        private bool isAuthorized=false;
        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (isAuthorized)
            {
                filterContext.Result = new ViewResult { ViewName = "Welcome" };
            }
            else
            {
                ViewResult result = new ViewResult();
                result.ViewName = "Login";
                result.ViewBag.message = "Login attempt failed!!!";
                filterContext.Result = result;
            }
        }

        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            username = filterContext.HttpContext.Request.Form["username"];
            password = filterContext.HttpContext.Request.Form["password"];
            MD5 md5Hash = MD5.Create();
            string md5Password = GetMD5Hash(md5Hash, password);
            bool result = FormsAuthentication.Authenticate(username, md5Password);
            if (result)
            {
                FormsAuthentication.SetAuthCookie(username, false);
                isAuthorized = true;
            }
            else
            {
                isAuthorized = false;
            }
        }
        private static string GetMD5Hash(MD5 md5Hash, string input)
        {
            byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
            StringBuilder strBuilder = new StringBuilder();
            foreach (byte b in data)
            {
                strBuilder.Append(b.ToString("x2"));
            }
            return strBuilder.ToString();
        }
        #endregion
    }
}

Listing 2:-“ MyActionAttribute” class

In this class we added some of our own methods and variables. “username” and “password” are for the reference of the user login name and password. “isAuthorized” is to keep track of whether user has been authenticated or not. We will shortly get back to you why we chose this variable as a reference for authentication status. “GetMD5Hash” method’s role is to generate MD5 encryption of our password.

Starting from method “OnActionExecuting”, the username and the password values that are posted from the login view is retrieved through “filterContext.HttpContext.Request.Form”. The key specified in the form value is the “name” attributes value of the HTML controls in the view. The value of this key-value pair is the “value” attributes value of the HTML controls.  MD5 hash is created by “Create” method of “MD5” static class in namespace “System.Security.Cryptography”. This hash is used to encrypt the password by supplying it to “GetMD5Hash” method. We authenticate username and encoded password by “Authenticate” method. We saved the result of authentication because we are now not sure of the credentials supplied by user. This is not hard coded any more as it was in our previous cases (Refer filter Part 1 and 2). You might have inferred the changes in “Web.config” “authentication” tag mentioned below:-

<authentication mode="Forms">
  <forms loginUrl="~/Account/Login" timeout="2880">
    <credentials passwordFormat="Clear">
      <user name="name" password="5f4dcc3b5aa765d61d8327deb882cf99"/>
    </credentials>
  </forms>
</authentication>

Listing 3:- “Web.config” configuration

Value of “password” attribute within the “user” tag is pre-calculated by encoding it from “MD5” alogrithm. We kept note of MD5 hash value of password whose value is “password”. In real application, this value will be generated during sign up procedure and will be stored in database.

Based on the result of authentication, the value of “isAuthorized” is set accordingly. This turn was taken because, after arriving at “OnActionExecuted” method. We had no way of knowing the result of authentication. I even used “filterContext.HttpContext.User.Identity.IsAuthenticated”. But to my surprise even when authentication was successful this value remained false.  This value became true only when the whole thing was again repeated. It was not long enough to see that “IsAuthenticated” property is read-only and can’t be changed. I even realized that “filterContext” was updated only at the beginning of the action method. So my procedure is one of the effective ways to keep track of the authentication in scenarios like this one.

In “OnActionExecuted” method, the result of the action method is set to type “ViewResult”. If the authentication is successful, the view name to be rendered is “Welcome”. Else the view name “Login” is rendered. The failure method is stored in “Viewbag.message”.

The changes added in the “Home” controller are listed as below:-

[HttpGet]
public ViewResult Login()
{
    return View();
}
[HttpPost]
[MyAction]
public ActionResult Login(string username, string password)
{
    //do the database update such as last accessed etch here
    return null;
}
public ActionResult SignOut()
{
    FormsAuthentication.SignOut();
    return RedirectToAction("Login");
}

Listing 4:-“Home” controller

The view rendered by “Login” action method is listed as below:-

@{
    ViewBag.Title = "Login";
}
<h2>
    Login</h2>
 <p style=" color:Red">@ViewBag.message</p>
@using (Html.BeginForm())
{
    <div style="width: 25%; border: 1px solid black;">
        <table width="100%">
            <tr>
                <td>
                    Username:
                </td>
                <td>@Html.TextBox("username", null, new { style = "width:90%" })
                </td>
            </tr>
            <tr>
                <td>
                    Password:
                </td>
                <td>@Html.Password("password", null, new { style = "width:90%" })
                </td>
            </tr>
        </table>
        <table width="100%">
            <tr>
                <td align="center">
                    <input type="submit" name="login" value="Log in" />
                </td>
            </tr>
        </table>
    </div>
}

Listing 5:-“Login.cshtml”

Note the “ViewBag.message” to retrieve the failure message for failed login. The post request from this view is redirected to “Login” method with “[HttpPost]” attribute. It is in this action, where we apply our custom attribute “[MyAction]”. We simply returned null from this action. Everything else will be handled by our custom action filter. The comment in this action is suggestive in its own right.

The view “Welcome.cshtml” for successful login is as listed below.

@{
    ViewBag.Title = "Welcome";
}

<h2>Login Sucessfull. Welcome!!!</h2>
@Html.ActionLink("Sign out", "SignOut")

Listing 6:-“Welcome.cshtml”

In this view, there is an action link that redirects to “SignOut” action in “Home” controller. This particular action cancels the User’s current session. It then redirects to “Login” action with “[HttpGet]” attribute to prompt another session.

When we run our solution, we can see the following outputs:-

1)      When user enters URL “/Home/Login” which is our default URL


 Figure 1- “Login.cshtml”

2)      When user successfully sings in by entering username as “name” and password as “password”


Figure 2:-“Welcome.cshtml”

3)      When login fails

               
Figure 3:- Failed login implied with message


Action Result Filters

Action Result Filters are more or less general purpose filter that operate on the result produced by the action filters. Like action filters, these filters also function executes in two stages i.e. when the result from action filter becomes available and when the result has been executed. This filter is also customizable. To implement the custom behavior of this filter, we need to implement interface “IResultFilter”. The skeletal code for this interface is as listed as below:-

using System;

namespace System.Web.Mvc
{
    // Summary:
    //     Defines the methods that are required for a result filter.
    public interface IResultFilter
    {
        // Summary:
        //     Called after an action result executes.
        //
        // Parameters:
        //   filterContext:
        //     The filter context.
        void OnResultExecuted(ResultExecutedContext filterContext);
        //
        // Summary:
        //     Called before an action result executes.
        //
        // Parameters:
        //   filterContext:
        //     The filter context.
        void OnResultExecuting(ResultExecutingContext filterContext);
    }
}

Listing 7:- Skeletal code of interface “IResultFilter

As expected, the interface has two method implementations. “OnResultExecuting” is called when the action filter returns the result i.e. when the result is ready. Everything you put into this section executes before execution of the action result. The parameter to this action is of type “ResultExecutingContext” derived from “ControllerContext”.

OnResultExecuted” is called after the action result is executed. The tasks that needed to be conducted after the execution of the action result fit here. The parameter to this action is of type “ResultExecutedContext” which is also derived from “ControllerContext”.

Afar from having acquired derived properties, these parameter types also have their own general purpose properties:-

ResultExecutingContext” has following properties:-

·         Cancel” of type “bool” which indicates if the result has been canceled by other action filters
·         Result” of type “ActionResult” states the result of the action method. The result can be set to null to cancel the entire request

ResultExecutedContext” has following properties:-

·          Result” of type “ActionResult” states the result of the action method. The result can be set to null to cancel the entire request
·         Canceled” of type “bool” which indicates whether “ResultExecutedContext” has been cancelled by other action result filters
·         Exception” of type “Exception” provides the exception thrown by other action filter and action result filter of action methods
·         ExceptionHandled” of type “bool” which indicates if the exception has been handled

We are going to implement result filters to make slight modification in our previous example of action filters. Let us say that after the execution of the action filter, we need to convey small yet critical information to the users. In case of failed login attempt, we need to inform the user about his previous failed attempt’s date and time. After successful login, we need him to see complete long format date as Today is Saturday, 27 June, 1986 - 1:23 PM on the bottom of the page. We could easily use action filter without any second thoughts. Since we are trying to implement result filter here, we will select later case as a way to resolve our problem. However, there may be the situations like when action filters has been completely tested and approved. It may be compromising act to make changes to action filters which calls out for repetition of the entire testing procedure. Shifting this logic to the result filters decouples the dependency from the action filter eliminating overhead of retesting and speeding up the deliverance time.

The code listed below is for the class “MyActionResultAttribute” in “infrastructure” folder which is the implementation of “IResultFilter” and derives from “FilterAttribute” as required.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Filters.Infrastructure
{
    public class MyActionResultAttribute:FilterAttribute,IResultFilter
    {
        #region IResultFilter Members
        private string longdate;
        private string lastTry;
        public void OnResultExecuted(ResultExecutedContext filterContext)
        {
            if (filterContext.Exception == null && !filterContext.Canceled)
            {
                ViewResult result = (ViewResult)filterContext.Result;
                if (result != null)
                {
                    if (result.ViewName == "Welcome")
                    {
                        filterContext.HttpContext.Response.Write("<p style='color:Green;'><br/>Today is "
+ longdate+"<br/></p>");
                    }
                    else if (result.ViewName == "Login")
                    {
                        filterContext.HttpContext.Response.Write("<p style='color:Red;'><br/>Last login
attempt at " + lastTry+"<br/></p>");
                    }
                    filterContext.Result = result;
                }
            }
        }

        public void OnResultExecuting(ResultExecutingContext filterContext)
        {
            longdate = DateTime.Now.ToLongDateString();
            lastTry = DateTime.Now.ToShortDateString()+"-"+ DateTime.Now.ToShortTimeString();
        }

        #endregion
    }
}

Listing 8:-“ MyActionResultAttribute” class

In this class there are two string variables “longdate” and “lastTry”. “longdate” variable is implemented to hold the long date format. “lastTry” variable is used to store a brief information about last unsuccessful authentication.

In method “OnResultExecuting” when the result from action filter becomes available, we just set these variables values through “DateTime” static class. In method “OnResultExecuted”, if there is no exception, we extract the result property from the filtercontext of type “ResultExecutedContext”. The result of the “filtercontext” is casted to “ViewResult” type. If the result is not null (i.e. result is of type “ViewResult”), the view name is checked. For view “Welcome.cshtml”, we simply write our message along with “longdate”. While for view “Login.cshtml”, we will write “lastTry” indicating last unsuccessful login attempt. We could have used “viewbag”, but I am lazy enough to show you the changes in these views. After all, we have dealt with these views while working with custom action filters. “HttpContext.Response.Write” works fine for this context. Am I right? Try to modify the View result in this method and see what happens. Only then it will make sense to you why I chose this process. You will realize that I am half lying. You know I also tend think the way you think.

We need to place “[MyActionResult]” attribute for action result filter in the action method “Login” that accepts POST request. The highlighted segment in code below shows this change:-

[HttpPost]
[MyAction]
[MyActionResult]
public ActionResult Login(string username, string password)
{
    //access the database and update the last accessed
    return null;
}

Listing 9:- “Login” action method incorporated with “[MyActionResult]” action result filter

When we run the solution and go to URL “/Home/Login”, enter any invalid credential, we can see the following output


Figure 4:- Failed login attempt showing last login attempt in view “Login.cshtml”

After successful login, we can see the following result


Figure 5:- Successful login showing today’s date in long date format in view “Welcome.cshtml”

Default Action filter and action result filter

The default filter for both action filter and result filter uses the same class “ActionFilterAttribute” (Refer table 1 in Filters Part 1). The benefit we get from using the default class over custom class is that we can skip some of the unnecessary method which we do not want to implement. In our previous example for action result, it may have not made sense to many of you about the places/methods where I kept the logic. It was only for the purpose of the demonstration of the result filters. Like in “AuthorizeAttribute” while implementing default, we need to have our class derived from “ActionFilterAttribute”. This is mandatory not because we want to preserve the native formation, but to write our own. The “ActionFilterAttribute” is the abstract class with different virtual methods which needs to be overridden in order to be useful. Hence, you must have understood why we must derive our class from “ActionFilterAttribute”. The following code shows the abstract class “ActionFilterAttribute”.
 public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter
 {
     protected ActionFilterAttribute();
     public virtual void OnActionExecuted(ActionExecutedContext filterContext);
     public virtual void OnActionExecuting(ActionExecutingContext filterContext);
     public virtual void OnResultExecuted(ResultExecutedContext filterContext);
     public virtual void OnResultExecuting(ResultExecutingContext filterContext);
 }

Listing 10:- abstract class “ActionFilterAttribute
This abstract class contains all the methods we used for action filters and result filter. We used them all in our previous encounters. It wasn’t necessary for us to use all these methods. We will modify our previous example by using this default implementation.

We add class “MyDefaultActionFiltersAttribute” to” infrastructure” folder and derive it from class “ActionFilterAttribute”. The below code lists this class:-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Security.Cryptography;
using System.Web.Security;
using System.Text;

namespace Filters.Infrastructure
{
    public class MyDefaultActionFiltersAttribute:ActionFilterAttribute
    {
        private string username;
        private string password;
        private bool isAuthorized = false;
        private string longdate;
        private string lastTry;
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            username = filterContext.HttpContext.Request.Form["username"];
            password = filterContext.HttpContext.Request.Form["password"];
            MD5 md5Hash = MD5.Create();
            string md5Password = GetMD5Hash(md5Hash, password);
            bool result = FormsAuthentication.Authenticate(username, md5Password);
            if (result)
            {
                FormsAuthentication.SetAuthCookie(username, false);
                isAuthorized = true;
                longdate = DateTime.Now.ToLongDateString();
            }
            else
            {
                isAuthorized = false;
                lastTry = DateTime.Now.ToShortDateString() + "-" + DateTime.Now.ToShortTimeString();
            }
        }
      
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (isAuthorized)
            {
                filterContext.Result = new ViewResult { ViewName = "Welcome" };
            }
            else
            {
                ViewResult result = new ViewResult();
                result.ViewName = "Login";
                result.ViewBag.message = "Login attempt failed!!!";
                filterContext.Result = result;
            }
        }

        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            if (filterContext.Exception == null && !filterContext.Canceled)
            {
                ViewResult result = (ViewResult)filterContext.Result;
                if (result != null)
                {
                    if (result.ViewName == "Welcome")
                    {
                        filterContext.HttpContext.Response.Write("<p style='color:Green;'><br/>Today is "
+ longdate + "<br/></p>");
                    }
                    else if (result.ViewName == "Login")
                    {
                        filterContext.HttpContext.Response.Write("<p style='color:Red;'><br/>Last login
attempt at " + lastTry + "<br/></p>");
                    }
                    filterContext.Result = result;
                }
            }
        }

        private static string GetMD5Hash(MD5 md5Hash, string input)
        {
            byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
            StringBuilder strBuilder = new StringBuilder();
            foreach (byte b in data)
            {
                strBuilder.Append(b.ToString("x2"));
            }
            return strBuilder.ToString();
        }

    }
}

Listing 11:- “MyDefaultActionFiltersAttribute” class

You can notice that the method in this class resembles the methods in our custom action filters and custom result filters. Only difference is we saved our self from using “OnResultExecuting”. We didn’t do much useful in this action in our previous implementation rather than assigning date formats to our variables. We have transferred this logic to “OnActionExecuting” method. In “OnResultExecuting” method of custom result filter, both values of “longdate” and “lastTry” were assigned value irrespective of authentication result. Now after transferring this logic to “OnActionExecuting”, we don’t need to assign values for unused variables. A good programming practice!

The change we make in “Login” action for POST request in the home controller is as below:-

[HttpPost]
[MyDefaultActionFilters]
public ActionResult Login(string username, string password)
{
     //access the database and update the last accessed
     return null;
}

Listing 12:- “Login” action method incorporated with “[MyDefaultActionFilters]” filter attribute

When we run the project and enter URL “/Home/Login”, we see the same result as in above case of result filter


Figure 6:- Failed login attempt showing last login attempt in view “Login.cshtml”


Figure 7:- Successful login showing today’s date in long date format in view “Welcome.cshtml”

Global Filter

Global filters are applied to all the controllers and their actions in your application. We change a regular filter into global filter by registering it as a global filter. This is done in the “RegisterGlobalFilters” method of the “Global.asax.cs” class. We register our filters by adding it to collection “GlobalFilterCollection” as follows

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

Listing 13:-Registering Global filters

Ordering the filter execution

The filters are executed on order based of their type. Authorization filter are executed at the very beginning, followed by action filters and then by result filters. However, the exception filters are invoked only when there is an exception in the application. For same filter types applied multiple times to an action, there is no strict distinction for the order in the execution, unless explicitly specified. The MVC framework can run any filter instance prior to another. In order to be sure about the ordering, we add order parameter to the filter attribute of type “int” indicating the order of the execution.

This is shown as below

[HttpPost]
[MyDefaultActionFilters1(Order=2)]
[MyDefaultActionFilters2(Order=1)]
public ActionResult Login(string username, string password)
{
    //access the database and update the last accessed
    return null;
}

Listing 14:- Ordering of filter execution

In above action, “MyDefaultActionFilters2” executes first and “MyDefaultActionFilters1” executes later. If we do not specify the order of a filter it takes the value “-1” causing it to have lowest value and become the first one to be executed.

 If all filters have same order specified, MVC framework determines the order based on where filter has been applied. Global filter are executed first, then follows the filter applied to the controller and then comes the turn of the filters applied to the actions. Whereas, in case of exception filters with same orders the execution order is reversed. The filters applied to actions are executed first, and then the filters applied to the controllers and finally the global filters.

For sake of curiosity, try an example to find out the order of execution of “OnActionExecuting” and “OnActionExecuted” when you have two action filters applied to same action. Don’t forget to order them. All I can say is that the MVC framework maintains the stack of filters along while executing them and pops them up accordingly. The behavior may be exactly what you were searching for. So go for it!!!

2 comments: