Now, let us research into another popular pattern leveraged
under the ASP.NET platform--the XMLHTTP+HttpHandler pattern.
Introduction
As you have seen, the first pattern, XMLHTTP+WebForm, is
simple with clear logics between the components. However, there are also
troubles with this pattern: with the project becoming even larger, there are
maybe hundreds of thousands of asynchronous requests sent out from the client
side pages, and when hundreds of Ajax pages require introduction to respond to
the client side acceptances. How will you efficiently maintain such awesome stuff?
To overcome the above how-do-you-do, some readers may put
forward the following kind of solution: make all the requests attached to a
single page, and then specify different requests with different names of types.
To some degree, this does help to abate the above contradiction. Nevertheless,
if you have a test with the above case by yourself, you will find out that the
application logics will become complicated, which will naturally result in the
difficult maintenance. On the other hand, a Web form is, after all, a common
page. Although the original idea is to make some computation on the server
side, a complete page life cycle will have to be executed. This is apparently a
big exhaustion for the server side resource.
So, how can we further improve the above solution? Well, the
answer lies right in the HttpHandler.
In the ASP.NET architecture, HttpHandler is one of the core
modules to process the HTTP protocol, which, in fact, implements the
functionality of an ISAPI Extension extending the means of processing Http
Requests and sending related Responses.
Digging further, you will find the functionality of the
HttpHandler is accomplished through interface IHttpHandler. Figure 4
illustrates the general processing flow of a HTTP request. With the number
order marked in the figure, it is easy to find out the following routine: a Web
request first reaches the IIS, and then through aspnet_isapi.dll the request is
passed over to the ASP.NET engine. Next, inside the ASP.NET engine the web
request will pass through a possible complex tunnel; after being processed by
several HTTP Modules, the request at last comes to the HTTP Handler. It is
through the HTTP Handler that the processed result again returns back to IIS
which bears the responsibility of sending the result back to the client side
that sends the request. Finally, the whole process comes to an end.
Figure 4 - The sketch map of the XMLHTTP+WebForm styled
implementing flow
As is shown in Figure 2, at the end of the ASP.NET channel
is the HTTP Handler (or Handler factory in the case of multi-handlers). In
essence, every ASP.NET Page class derives from the IHttpHandler interface, as
is indicated in Figure 5 captured using .NET Reflector.
Figure 5 - Use .NET Reflector to view the Page
derivation
Next, let us continue to examine the interface IHttpHandler.
Listing 3
public interface IHttpHandler
{
// Methods
void ProcessRequest(HttpContext context);
// Properties
bool IsReusable
{
get;
}
}
Here, you can use Response together
with Request to work with method ProcessRequest
to achieve your custom handler. The property IsReusable
is used to specify whether the implementing class for interface IHttpHandler
requires buffering.
The HttpHandler Styled Sample
In this section, we will start to create a simple HttpHandler
styled sample. First, let us consider how to implement the custom HttpHandler which
will be used to process the requested string. The following gives the complete
implementation of our custom HttpHandler HelloAjax.
Listing 4
namespace HelloAjax
{
public class HelloAjax:IHttpHandler
{
#region IHttpHandler member
public bool IsReusable
{
get
{
return true;
}
}
public void ProcessRequest(HttpContext context)
{
str += context.Request.UserHostAddress;
str += "<br/>The Datatime on the Server side:";
str += DateTime.Now.ToLocalTime ();
context.Response.Write(str);
}
#endregion
}
}
Here, the functionality of method ProcessRequest
is simple: just construct a simple string, and then send the string back to the
client side.
Author's Note: For the special .ashx
type of HttpHandler, we do not need to build up a special .DLL assemble to
encapsulate it and then to refer it in the target website. However, for the
general custom HttpHandler, you have to set up a .DLL assemble to encapsulate
it, modify the <httpHandlers> section in file web.config, and then to
refer to it in the target website. Only through the above three steps can you
use the custom HttpHandler on the fly; or else, you will fail.
Now, with everything about the custom HttpHandler getting ready,
let us shift our attention to the client side related programming. First, look
at the HTML code for the related sample page HttpHandlerForm.aspx.
Listing 5
<head runat="server">
<title>Hello Ajax---XmlHttp+HttpHandler</title>
<script type="text/javascript" src="js/ajax.js"></script>
<link href="StyleSheet.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div style="text-align:center;width:420px">
<h1>XmlHttp+HttpHandler Pattern</h1>
<br />
<button onclick="talktoServer('HelloAjax.ashx');">Submit</button>
</div>
</body>
I believe that your attention will surely be attracted by
the click handler of the "Submit" button, "talktoServer('HelloAjax.ashx');,"
because there is nothing peculiar with the other stuff. Let us go on with the
story to see the crucial JavaScript function.
Listing 6
function talktoServer(url){
var req = newXMLHttpRequest();
var callbackHandler = getReadyStateHandler(req);
req.onreadystatechange = callbackHandler;
req.open("POST", url, true);
//encode the url
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
//get the string entered into the textbox control
var testmsg = document.getElementById("testmsg");
var msg_value = testmsg.value;
//send the string to the server side
req.send("msg="+msg_value);
}
Here, we first call another method newXMLHttpRequest()
to create a XmlHttp object. Then we set up the client side callback function getReadyStateHandler() and pass as a parameter the
newly-created XmlHttp object to it. Next, we attach the callback function getReadyStateHandler() to the onreadystatechange
event of the XmlHttp object req. The subsequent several
sentences are easy to know since they are the common XmlHttp object related
expression. However, there is also one point deserved to be noticed.
Listing 7
req.open("POST", url, true);
Note the "url" above points to "HelloAjax.ashx,"
not a general .txt/.xml/.html/.asp/.aspx/.php/.jsp file. Then by executing the
server side method ProcessRequest of the HttpHandler "HelloAjax.HelloAjax,"
the specified string returns to the client side.
For brevity, we omitted the other two functions (newXMLHttpRequest
and getReadyStateHandler). You can refer to the downloadable source code for
details.
Now, when everything gets ready, you can press F5 to launch
the above sample page. And with everything smooth, you will get a snapshot most
like that in Figure 6.
Figure 6 - The server-side string is returned when
you click "Submit"
In contrast to the XMLHTTP+WebForm pattern in the first
part, the HttpHandler way is more light-weighted, helping to release the payload
of the whole system, but still trivial and annoying.
Next comes another Ajax pattern under the ASP.NET environment
- the ASP.NET 2.0/3.5 callback function pattern.