Click here to Skip to main content
Click here to Skip to main content

Implementing an extensible factory method pattern using ASP.NET and AJAX

, 3 Nov 2005
Rate this:
Please Sign up or sign in to vote.
This article discusses the advantages of building HTML controls on the server side for AJAX calls and proposes an extensible factory pattern. Also different approaches for handling these custom calls using modules, handlers and ASPX pages and the leverage provided by ASP.NET libraries are discussed.

Introduction

The general approach taken for dynamically building pages using AJAX (Asynchronous JavaScript and XML) is to use client side scripting to call a web service (or a page) to retrieve data and populate a control which is already available on the client side. This approach has some advantages and disadvantages.

The advantage is that the GUI elements are already placed and the page layout is well known. The disadvantages are:

  • XML is transferred by the web service or page which requires strict XML contract (defined schema) between the client and the server.
  • Client side coding is required to load and parse XML using the bDOM document.
  • The code for adding data to HTML controls is different for each HTML element. For e.g. the drop down list (SELECT, OPTION) is different from a table (TABLE, TR, TD).

These problems can be addressed if the controls are created on the server side, populated with data and the HTML along with the data is serialized to the client. The only burden on the client side would be to have a place holder available for the HTML element returned.

ASP.NET web class libraries offer excellent support to create HTML elements as classes which support rendering methods that provide an excellent opportunity to address this problem.

To implement a factory method pattern I have separated every call into two sections:

  • The method that has to be invoked on the server is sent in the query string as a key named 'method'. This method name is retrieved by a processor class on the server side and dynamically invoked using Reflection (you can have a class name too).
  • The actual parameters required by the method (to keep the design simple) are sent as key value pairs in the query string. This way the method on the server side can retrieve the required parameters from the Request object.

To accommodate and render the HTML element returned by the processor on the client side a DIV tag is created as the target element. As soon as the asynchronous call returns the script will take the responseText and using the innerHTML property will add this code inside the DIV tag which will render the control on the page. If there are client side scripts associated with this dynamically created element, optionally the ID for the created element may be set on the server side (which requires it to be passed as another parameter in the query string like the setID in the demo program).

The code for the solution can be separated into the following:

  1. The initial AJAX calling sequence.
  2. Handling the code in ASP.NET (the server side code).
  3. Processing the response on the browser side.

The initial client call sequence

  • All the required parameters are added to a query string (this includes a connection string in my example). This is method specific. For e.g. see the GetTables() method. It requires only a connection string. So the client side JavaScript adds them to a query string and the server side method retrieves them.
  • Query string is appended to the URL. The URL and the extension are dependent on the server side code implementation by the module, handler or a simple ASPX page.
  • A target DIV tag is provided by the caller to display the returned results. A default DIV is assumed if nothing is provided. I have added a 'results' DIV in the demo for this purpose.
  • An asynchronous call is made using the XMLHTTP object with a call back function. Take care to verify this on all browsers. The following code in DataBrowser.js file makes this call:
    function SendRequest(target)
    {
        AppendConnectionParameters();
        if (ajaxObj != null)
        {
            target_div.innerHTML = 
                ""<B>Already in call. Please wait ..."</B>";
            return;    // object is busy
        }
        target_div = (target == null) ? dataholder : target;
        target_div.innerHTML = 
             "<B><I>Getting data. Please wait ..."</I>"<B>";
    
        // for Mozilla, Firefox, Safari, and Netscape
        if (window.XMLHttpRequest)
            ajaxobj = new XMLHttpRequest();
    
        // for Internet Explorer
        if (window.ActiveXObject)
            ajaxObj = new ActiveXObject("microsoft.xmlhttp");
    
        ajaxObj.onreadystatechange = ProcessResponse; // set the call back
    
        // You can make the call in any of the following 3 ways.
        // (1) opening a aspx call, the default asp.net page 
        //     handler will be called to process aspx page
                // ajaxObj.open("GET", "AjaxCallPage.aspx?" + querystring);
        // (2) use a custom ASP.NET extension handler
                // ajaxObj.open("GET", "Data.acall?" + querystring);
        // (3) use a custome extension with a ASP.NET module installed, 
        //     no change in call from above
                // ajaxObj.open("GET", "Data.acall?" + querystring);
    
        ajaxObj.open("GET", "Data.acall?" + querystring);
        ajaxObj.send(null);                // send the request
    }

Handling the code inside ASP.NET (the server side code)

The module, filter, page model provided by ASP.NET is very flexible and gives excellent opportunity to extend the ASP.NET functionality.

A custom call from a client can be handled in three ways:

  1. By using an ASPX page- This is the simplest and involves least development effort. In the example code provided the call is handled in the Init() method and the response stream is closed. This avoids the overhead of other events like loading, rendering and others at the page level.
  2. Providing a custom handler- This is bypassing the default ASP.NET page handler. Once all the modules configured in machine.config or web.config finish their processing the call is handed over to our handler. This approach gives the advantage of a custom extension and bypasses any overhead the default page handler has. This involves adding the extension in IIS and adding the handler in the CONFIG file (see below).
  3. Writing a custom module- A custom module can handle the call very early in the sequence of events inside ASP.NET and also provides the advantage of bypassing some modules and a handler. There are two scenarios in writing a custom module. The BeginRequest is the earliest event where the Request object is available to the module. If you do not want any authentication or authorization this is the quickest way to return the data back to the client. But if you are targeting security subscribe for Authenticated or Authorized event and handle the request. This approach like the handler requires registration of the extension with IIS and also adds the module to the CONFIG file (as shown below):
    <configuration>
      <system.web>
         <!--<span class="code-comment"> registering a module --></span>
         <httpModules>
             <ADD type="DataBrowser.AjaxCallModule,DataBrowser" 
                                                name="ajaxcalls" />
         </httpModules>
         <!--<span class="code-comment"> registering a handler --></span>
         <httpHandlers>
             <ADD type="DataBrowser.AjaxCallHandler,DataBrowser" 
                                                path="*.acall" verb="*"/>
         </httpHandlers>
    
          .......
    
      <system.web>
    <configuration>

To make all the above scenarios possible, the AjaxCallProcessor class is provided which handles all the logic. This class invokes a method dynamically using Reflection which returns the GUI element which is dynamically created and populated with data (depending on the client parameters read from the Request object).

The code below shows the ProcessAjaxCall() method on AjaxCallProcessor class. This can also be made a static method. I made it an instance method to simplify the code:

internal void ProcessAjaxCall()
{
    // clear any headers added to the response 
    // header by ASP.NET HTTP pipe
    _response.Clear();
    try
    {
        // prepare connection string, part of business 
        // logic in this demo
        PrepareConnectionString(); 

        // invoke the method requested by the client 
        // dynamically using reflection
        typeof(AjaxCallProcessor).GetMethod(_request["method"],
          BindingFlags.NonPublic|BindingFlags.Instance).Invoke(this, null);

        // create a HTMLTextWriter for the response stream 
        // and let the dynamic control to create it
        HtmlTextWriter hw = new HtmlTextWriter(_response.Output);

        // let the asp.net control render itself
        dynamicControl.RenderControl(hw);
        hw.Close();
        
        if (sqlConn.State == ConnectionState.Open) // just in case :-)
            sqlConn.Close();
    }
    catch (Exception ex)
    {
        // writing back an error message as HTML to response stream
        _response.Write("<STRONG><FONT color=red>Error : ");
        _response.Write(ex.Message);
        _response.Write("</FONT></STRONG>");
    }

    // close the response stream to end the call immediately
    // this will bypass any delay in ASP.NET HTTP pipe
    _response.End();
}

Processing the response on the browser side

  • Inside the call back function, check the response of the asynchronous call.
  • If readyState is 4 the call is completed and the object's responseText gives the reply from the server.
  • The response is expected to be complete HTML (as it is designed). Take the responseText and add it inside the provided DIV tag using the innerText property.

    Note: Sometimes you may not want to lose the actual control in place in case of an exception on the server side. You can check for a pre-fix or something in the responseText and handle it accordingly. For e.g. instead of the HTML pass the error message in the <CUSTOM_ERRROR> tag and check for this tag on the client side.

    function ProcessResponse()
    {
        // target_div.innerHTML += ajaxObj.readyState; // for testing only
        if (ajaxObj.readyState == 4)    // request completed
        {
            target_div.innerHTML = ajaxObj.responseText;
            ajaxObj = null;
            target_div = null;
        }
    }

The demo program included demonstrates the use of this technique. Please see the screen shots below which show you how a control is replaced on the client side:

References

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Sriram Chitturi
Architect
United States United States
No Biography provided

Comments and Discussions

 
QuestionHow to register custom extension in IIS PinmemberDennis McMahon9-Nov-05 5:58 
Great work. Wondering if you could provide quick instruction on how to register the custom extention in IIS specific to this sample. I can see the entries for the handlers in the web.config but I'm not sure how to configure IIS specifically to make this work, I take it from the article that some settings have to be made under IIS admin?
 
Thanks!
Maddog
AnswerRe: How to register custom extension in IIS PinmemberSriram Chitturi9-Nov-05 12:48 
GeneralRe: How to register custom extension in IIS PinmemberDennis McMahon10-Nov-05 3:39 
GeneralError downloading zip file Pinmemberranjit0919779-Nov-05 3:05 
GeneralRe: Error downloading zip file PinmemberSriram Chitturi9-Nov-05 12:39 
GeneralOuterHTML PinmemberHallyS7-Nov-05 23:56 
GeneralRe: OuterHTML PinmemberSriram Chitturi8-Nov-05 2:53 
GeneralMissing Source Code. PinmemberTony Bermudez7-Nov-05 19:29 
GeneralRe: Missing Source Code. PinmemberSriram Chitturi8-Nov-05 2:45 
GeneralNice work PinmemberKevin Ferron3-Nov-05 9:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 3 Nov 2005
Article Copyright 2005 by Sriram Chitturi
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid