Thursday, February 27, 2014

Controller Factory and Action Invoker Part 2


Action Invoker

Action Invoker’s role is to call suitable action depending upon the request directed by the controller. Controller consists of different actions with different return types. There may be two or more overloads of same actions inside the controller. The nature of those actions may be different. Some actions are targeted for the GET request and some for the POST request. Some actions may be disqualified as a valid action. It’s duty of the action invoker to look after all these aspects before diverting the action call. We gave little introduction about Action Invoker in “Controller Factory and Action Invoker Part 1”. Now we discuss about the extensibility and customizability of the action invoker.

Custom Action Invoker

Custom action invokers are rarely used unless we are writing our own rules. Sometimes afar from other native request, we may come up with our own action selector. Action selectors are attributes applied to any action. These selectors convey special kind of desired behavior such as GET, POST or PUT. We can customize action selector for our own purpose.

For example let us assume that there are two way to validate the price of the purchased item. A store keeper can directly input the price of the goods or he/she may use bar code reader. We can imagine that we have two actions based on the nature of input. A store keeper’s typed input can simply use POST request assisted action. However, for barcode reader integration, we need a separate custom action selector. The action for barcode reader contains the nifty codes to decode the price from an input image. Action invoker is the exact location, where we can find smooth fitting for such codes. Based on the nature of request, we can call correct override from the available action name. However, for pure custom action invoker their perspective of implementation is very hard to pin point.

To be useful as a custom action invoker, our custom class must implement the interface “IActionInvoker”. This interface is responsible for invoking the action rather than relying on the default configuration. The skeletal code for this interface is as listed below:-

using System;

namespace System.Web.Mvc
{
    // Summary:
    //     Defines the contract for an action invoker, which is used to invoke an action
    //     in response to an HTTP request.
    public interface IActionInvoker
    {
        // Summary:
        //     Invokes the specified action by using the specified controller context.
        //
        // Parameters:
        //   controllerContext:
        //     The controller context.
        //
        //   actionName:
        //     The name of the action.
        //
        // Returns:
        //     true if the action was found; otherwise, false.
        bool InvokeAction(ControllerContext controllerContext, string actionName);
    }
}

Listing 1:- Skeletal code for the interface “IActionInvoker

This interface defines a method named “InvokeAction” which is responsible for invoking the custom behavior. The first parameter to this method is “controllerContext” of type “ControllerContext” which gives us access over the current or requesting controller context. We can easily access controller specific property through this parameter. The second parameter is “actionName” of type “string” which represents the name of the requested action. This value can alternatively be obtained from the route value dictionary through route data with the help of requesting controller context. The return type of this method is boolean type. True value indicates that the request has been processed. False value indicates the inability to process the request.

We are going to use custom action invoker to imitate the POST and GET request. In GET assisted request, we are rendering a form which prompts for user input. In POST assisted request, we are addressing a hello message to the users along with their entered inputs. We could have done this just by using “[HttpPost]” and “[HttpGet]” attribute with the action methods, and rendering a suitable view for every user request.

To proceed with the example of custom action invoker, we create a class “MyActionInvoker” inside the “infrastructure” folder of the MVC project. We implement interface “IActionInvoker” for this class. Inside the “InvokeAction”, we check for the request type and the action name. Depending upon the request type and the action name, suitable action is carried out. The code below lists out the “MyActionInvoker” class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using ControllerExtensibility.Models;
using System.Text;

namespace ControllerExtensibility.Infrastructure
{
    public class MyActionInvoker : IActionInvoker
    {
        #region IActionInvoker Members
        public bool InvokeAction(ControllerContext controllerContext, string actionName)
        {
            if (controllerContext.HttpContext.Request.RequestType == "GET" && actionName == "item")
            {
                StringBuilder view = new StringBuilder();
                view.Append("<script type='text/javascript' src='/Scripts/jquery-1.5.1.min.js'></script>");
                view.Append("<form id='form' method='post' action='/test/item'>");
                view.Append("<script type='text/javascript'>");
                view.Append("$(document).ready(function () {");
                view.Append("$('#submit').click(function () {");
                view.Append("var name; var attribs;");
                view.Append("name=$('#name').val();");
                view.Append("attribs=$('#form').attr('action');");
                view.Append("attribs=attribs+'?name='+name;");
                view.Append("$('#form').attr('action',attribs);");
                view.Append(" })");
                view.Append(" })");
                view.Append("</script>");
                view.Append("<p>Your Name:</p>");
                view.Append("<input id='name' type='text' value='' name='name' id='name'>");
                view.Append("<input type='submit' value='submit' name='submit' id='submit'></form>");
                controllerContext.HttpContext.Response.Write(view.ToString());
                return true;
            }
            else if (controllerContext.HttpContext.Request.RequestType == "POST" && actionName == "item")
            {
                string name=string.Empty;
                if (controllerContext.HttpContext.Request.QueryString["name"] != null)
                {
                    name = controllerContext.HttpContext.Request.QueryString["name"];
                }
                controllerContext.HttpContext.Response.Write("<p>Hello:<b>" + name + "</b></p>");
                return true;
            }
            else
            {
                return false;
            }
        }

        #endregion
    }
}

Listing 2:-“ MyActionInvoker” class

We checked the request type by the help of HTTP context available through the Controller Context. For the “GET” request which targets the “item” action, we rendered an HTML page. The html was constructed by help of .net class “StringBuilder”. We have appended both JQuery/Javascript and HTML code within the “StringBuilder” instance. By JQuery/javascript, we extracted an attribute “action” of the “form” tag. To this attribute value, we appended the value entered inside the textbox as a query string. All this is carried out on the click event of the “submit” button. After successful completion of client event, form is submitted along with updated value of the “action” attribute. Frankly, this is not an appropriate way to code as you will observe compile time bug only during run time. At compile time our hectic code is nothing but a string value. During the “POST” request for the “item” action, we checked for query string “name”. The “name” value which was submitted in the previous request is rendered here. Similar to previous case, we rendered HTML by outputting the HTML content directly from HTTP Context response’s write method.

To take care of these actions, we add a “Test” controller (regular MVC controller). Below code lists the test controller.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using ControllerExtensibility.Infrastructure;
using ControllerExtensibility.Models;
using System.Web.Routing;

namespace ControllerExtensibility.Controllers
{
    public class TestController : Controller
    {
        //
        // GET: /Test/
        public TestController()
        {
            this.ActionInvoker = new MyActionInvoker();
        }
    }
}

Listing 3:-“Test” controller

During the instantiation of the “Test” controller i.e. inside the “Test” controller constructor, we set the “ActionInvoker” property of this controller to the newly created instance of “MyActionInvoker”.

When we run the project at this point, we can see the following outputs

When we redirect to URL /test/item

 
Figure 1:- redirecting to the URL “/test/item”

We are accessing the “GET” request portion of custom action invoker when we redirect to this particular URL “/test/item”.
  
When we fill up the form and click of submit/press enter,

 
Figure 2:- Upon submitting the form

During the form submission, we are accessing the “POST” request portion. Also notice the query string appended along with the URL.

Default Action Invoker

If we had directly used “GET” and “POST” attributes along with action methods and views in the test controller, then it would be one kind of default implementation. We are using this default action invoker knowingly or unknowingly. For insight knowledge about functionality of the default action invoker, we will explore default custom action invoker. To make our class as a default implementation of the action invoker, we must derive it from “ControllerActionInvoker”. “ControllerActionInvoker” is the action invoker used by every regular MVC controller. If we check the definition of this default class, we can see numerous virtual functions. We can override them with our own custom logics. “ControllerActionInvoker” implements “IActionInvoker” which is absolutely obvious.

With the help of default action invoker, we are going to replicate above example of custom action invoker. We are going to implement views instead of writing the HTML content directly. This makes our HTML code manageable, easily interpretable, and maintainable.

While deriving from a class and overriding any function, we must provide fall back support for other methods which are beyond our interest. Whereas for “POST” and “GET” request for “item” action, we implement our own logic. For this purpose we are overriding the method “InvokeAction”. We have already dealt with this method in custom action invoker of type “IActionInvoker”. The return type and parameters of this method has already been discussed in the custom action invoker segment.

We create class “MyDefaultActionInvoker” within the “infrastructure” folder of our application. “MyDefaultActionInvoker” class is derived from “ControllerActionInvoker”. The code listed below shows the same:-

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

namespace ControllerExtensibility.Infrastructure
{
    public class MyDefaultActionInvoker : ControllerActionInvoker
    {
        public override bool InvokeAction(System.Web.Mvc.ControllerContext controllerContext, string actionName)
        {
            if (controllerContext.HttpContext.Request.RequestType == "GET" && actionName == "item")
            {
                ViewResult viewResult = new ViewResult();
                viewResult.View = viewResult.ViewEngineCollection.FindView(controllerContext, "ItemGet", null).View;
                InvokeActionResult(controllerContext, viewResult);
                return true;
            }
            else if (controllerContext.HttpContext.Request.RequestType == "POST" && actionName == "item")
            {
                ViewResult viewResult = new ViewResult();
                viewResult.View = viewResult.ViewEngineCollection.FindView(controllerContext, "ItemPost", null).View;
                InvokeActionResult(controllerContext, viewResult);
                return true;
            }
            else
            {
                return base.InvokeAction(controllerContext, actionName);
            }
        }
    }
}

Listing 4:-“ MyDefaultActionInvoker” class

Within the “InvokeAction”, as in previous case, we’ve split the logic into two parts for “GET” and “POST” request type for the action “item”. An instance of type “ViewResult” is created in both portions. The view property of this instant is set to the view property of the result returned by “FindView” method. This find view method searches over the collection of the view from “ViewEngineCollection”. This collection comprises of all the views of our application. The parameters to “FindView” are the current controller context, the name of the view and the name of the master page. For the “GET” request we search for the view named “ItemGet.cshtml” and for the “POST” request we search for the view named “ItemPost.cshtml”.  These views are set as “ActionResult” (“ViewResult” are derived from “ActionResult”) by calling method “InvokeActionResult”. The controller context and the view result instance are passed as parameters to this method. If the request is for any action other than “item”, we will call the base class method “InvokeAction”. This way we will be supporting fall back architecture.

We make minor change in the “test” controller i.e. by updating reference to action invoker to “MyDefaultActionInvoker” class instance. The code for the “test” controller is as listed below:-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using ControllerExtensibility.Infrastructure;
using ControllerExtensibility.Models;
using System.Web.Routing;

namespace ControllerExtensibility.Controllers
{
    public class TestController : Controller
    {
        //
        // GET: /Test/
        public TestController()
        {
            this.ActionInvoker = new MyDefaultActionInvoker();
        }
    }
}

Listing 5:- “Test” controller

The newly added views “ItemGet.cshtml” and “ItemPost.cshtml” in “/views/test” path is as listed below:-

@{
    ViewBag.Title = "ItemGet";
}
<script type="text/javascript">
    $(document).ready(function () {
        $("#submit").click(function () {
            var name;
            var attribs;
            name = $('#name').val();
            attribs = $('#form').attr('action');
            attribs = attribs + '?name=' + name;
            $('#form').attr('action', attribs);
        })
    })
</script>
@using (Html.BeginForm("item", "test", FormMethod.Post, new { id = "form" }))
{
    <p>
        Your Name:</p>
    <input id='name' type='text' value='' name='name' id='name' />
    <input type='submit' value='submit' name='submit' id='submit' />
}

Listing 6:-“ItemGet.cshtml”

@{
    ViewBag.Title = "ItemPost";
}
@{string name = string.Empty;}
@if (HttpContext.Current.Request.QueryString["name"] != null)
{
    name = HttpContext.Current.Request.QueryString["name"].ToString();
}
<p>Hello:<b>@name</b>

Listing 7:-“ItemPost.cshtml”

The HTML code in “ItemGet.cshtml” is proper than the preceding one in the custom action invoker class. JQuery/Javascript is properly placed along with use of razor “Html” helper methods.

When we run the project, we can see the following outputs

when redirecting to URL “/test/item”, i.e. a “GET” request


 Figure 3:- Redirecting to URL “/test/item”

when posting the request by clicking the submit button or pressing enter


 Figure 4:- After submitting or posting the request

The both outputs in the custom and the default implementation looks similar. What we need to put inside our head is that for custom implementation, everything is hand written. While for default implementation, a view is used instead. Nobody can tell the difference by only observing the screenshots. Even the URL is same. Everything remains unchanged except the internal structure. This is the flexibility of the MVC framework. We can get the job done without letting others know about the changes we made. No can tell that we switched from the custom action invoker to the default action invoker. We have maintained the integrity of our application throughout the project. Only we know how much vital is our change, but to the outside world they don’t feel the difference. Discipline of the software engineering/development is to settle on the fact what other assumes us to be and still be able to keep our achievement to ourselves. We are rather happy and better off with clients who does feasibility research on our behalf.