Synchronous Requests Take a look at a following simple code example of a synchronous operation and then take a look at the explanation of exactly what is occurring that follows.. Asynchr
Trang 1while the user is still manipulating the information within the browser This represents the heart of Ajax and is the core advantage that it represents within traditional browser applications A user can continue
to work within the browser application uninterrupted, while in the background a request is sent and a response that contains the result of some server-side processing is received
Synchronous Requests
Take a look at a following simple code example of a synchronous operation and then take a look at the explanation of exactly what is occurring that follows Note: The code for the inclusion of the script include file mentioned previously has been omitted for brevity
function MakeXMLHTTPCall() {
var xmlHttpObj;
xmlHttpObj = CreateXmlHttpRequestObject();
if (xmlHttpObj) {
xmlHttpObj.open(“GET”,”http://” + location.host +
“/XmlHttpExample1/DataFile.xml”, false);
xmlHttpObj.send(null);
alert(“Request/Response Complete.”);
} } How It Works The preceding code sample is very simple, however it does show the basic usage of the XMLHTTP object If you examine the code in detail, you’ll see the following:
1. First you create a new XMLHTTP object and assign it to a variable.
2. After checking if the object is not null, that is, that the object creation in Step 1 was successful, you execute the openmethod passing in three parameters:
xmlHttpObj.open(“GET”,”http://” + location.host + “/XmlHttpExample1/DataFile.xml”, false);
❑ The first parameter, “GET”, is the type of request to make (this can be any of the stan-dard HTTP verbs “GET”, “POST”, “PUT”, or “HEAD”)
❑ The second parameter is the server address, or endpoint, to make the request to In this case, it’s an XML file located at http://localhost/XmlHttpExample1DataFile.xml
❑ The third parameter, false, indicates whether a synchronous or asynchronous request should take place In this case, falseindicates that a synchronous request should occur
3. The sendmethod is executed on the XMLHTTP object instance to perform the actual request xmlHttpObj.send(null);
Trang 24. Since you specified that a synchronous operation should occur, the next alertstatement to dis-play a message box is not shown until the request has completed executing and returns from the server
alert(“Request/Response Complete.”);
As already mentioned, the previous example shows a synchronous operation that offers no real change from the standard request/response paradigm that is prevalent in web applications A request is issued, the user waits until a response is received, and the user can continue Now, we can change this to be asynchronous
Asynchronous Requests
Examine the code that follows, which performs exactly the same operation as the previous example, but operates in an asynchronous manner Note: The code for the inclusion of the script include file men-tioned previously has been omitted for brevity
function MakeXMLHTTPCall()
{
var xmlHttpObj;
xmlHttpObj = CreateXmlHttpRequestObject();
if (xmlHttpObj) {
xmlHttpObj.open(“GET”,”http:// “ + location.host +
“/XmlHttpExample1/DataFile.xml”, true);
xmlHttpObj.onreadystatechange = function() {
if ( xmlHttpObj.readyState == READYSTATE_COMPLETE ) {
alert(“Request/Response Complete”);
} } xmlHttpObj.send(null);
} }
How It Works
There are two main differences in this example as compared to the initial synchronous example:
xmlHttpObj.open(“GET”,”http://” + location.host +
“/XmlHttpExample1/DataFile.xml”, true);
The first difference is the use of the trueparameter as the last argument to the openmethod to indicate that the request should be executed in an asynchronous manner
Trang 3The second difference is the use of the onreadystatechangeevent and the readyStateproperty value The code:
xmlHttpObj.onreadystatechange = function()
assigns a function or event handler to the onreadystatechangeevent When the state of the object changes, this event is triggered and the function is executed The code:
if ( xmlHttpObj.readyState == READYSTATE_COMPLETE ) checks the state of the object to determine if any action should be taken The ready state of the XMLHttpRequestobject actually contains a numeric value representing its state The numeric value being checked for in the preceding example is a value of 4, which is represented by the variable READYSTATE_COMPLETE The valid list of XMLHttpRequestready states is listed in the following table:
A convenient place to put the values of the XMLHttpRequestready states is in the library that was defined earlier to house the function that created an XMLHttpRequestobject This makes it possible to add meaning to what appears to be an arbitrary value The following code:
/* Common values for the ReadyState of the XMLHttpRequest object */
var READYSTATE_UNINITIALIZED = 0;
var READYSTATE_LOADING = 1;
var READYSTATE_LOADED = 2;
var READYSTATE_INTERACTIVE = 3;
var READYSTATE_COMPLETE = 4;
has been added to the JavaScript common library defined earlier in the chapter This means that the pre-vious example, showing an asynchronous call, equates to the following line:
if ( xmlHttpObj.readyState == 4 ) However, the line currently is written as:
if ( xmlHttpObj.readyState == READYSTATE_COMPLETE ) which makes it much more readable and adds clarity as to what is being tested in the code fragment
So, the JavaScript code in the example simply checks the ready state of the object to determine if the asynchronous request has completed If the ready state is complete, then you continue processing your response data (if any)
Trang 4So far, you have seen to how to perform an asynchronous request to the server and wait for the response
by assigning an event handler This forms the basis of all asynchronous request functionality, and you will use this as the foundation for more complex examples as you progress
Dealing with Response Data
In most scenarios, you will want to retrieve and process some response data from the server as part of your asynchronous call In the previous examples, you have requested an XML data file but have not attempted to process the data returned You have simply indicated the request has completed
To retrieve data from the response stream once the request is completed, you can use the following properties:
❑ responseText —This property returns the response data as a string
❑ responseXML —This property returns the response data as an XML document object This object can be examined and parsed using the standard methods and properties available from the W3C Document Object Model (DOM) node tree in a read-only (cannot be updated) fashion
A Note on Security
It is worth noting at this point that there are security restrictions that are placed on the
use of the XMLHttpRequestobject All browsers that implement the XMLHttpRequest
object implement a security policy called the “same origin” policy This means that a
request issued using an XMLHttpRequestobject must be to a server of the same origin
from which it was loaded
For example, if a web page were loaded from www.yourserver.com/somepage
.aspx, then all requests issued using the XMLHttpRequestmust be serviced by the
same host, that is, www.yourserver.com Any deviation to the server/host name,
pro-tocol, or the port will break the security policy For example, given the previous
exam-ple server, if a subsequent XMLHttpRequestwere issued to www.anotherserver
.com/anotherpage.aspx, then this would fail the security policy Similarly, trying to
access www.yourserver.com/somepage.aspxwill also fail because the protocol is
specified as https: whereas previously the code originated from an http: address
This policy makes sense because it means that web pages cannot simply issue requests
to any server they choose and potentially become yet another client capable of
initiat-ing denial-of-service attacks to servers (that is, floodinitiat-ing the servers with requests in
order to bring down or disable the service/server)
Unfortunately, different browsers implement the “same origin” security policy
differ-ently Most will fail in some way if a request is issued to a server that is not deemed the
origin, and it is very hard to accommodate all browsers when performing these types
of requests and handling the errors The recommended practice is simply to not use
requests that break the same origin policy Issue a request back to the originating server
as already discussed and let the server make any cross-domain/nonoriginating server
calls on your behalf using standard server-based programming techniques
Trang 5Using the responseText Property The responseTextproperty is the simplest of the methods to utilize data from the response All data that is part of the response is returned as a single string This property is useful for simple operations where only a singular piece of data is returned and manipulated or displayed by the page Alternatively, the data returned may be in a proprietary format that requires specialized parsing by the client To have
a look at how you use this property in code, you can enhance the existing sample to display data returned from the asynchronous call on the web page
Try It Out Returning Response Data As a String This web page will be a very simple page with a button and a section to display the response data The HTML fragment that follows shows the “body” section of the HTML page
<body>
<form id=”Form1” method=”post” runat=”server”>
<input type=”button” onclick=”MakeXMLHTTPCall();” value=”Test XMLHTTP Call” />
<br />
<br />
<div id=”divResults”>{no results}</div>
</form>
</body>
The example JavaScript code will now look like the fragment that follows The emphasized text shows what has been added compared to the previous examples:
<script type=”text/javascript” src=”CommonAJAXLibrary.js”></script>
<script type=”text/javascript”>
function MakeXMLHTTPCall(){
var xmlHttpObj = CreateXmlHttpRequestObject();
if (xmlHttpObj) {
xmlHttpObj.open(“GET”,”http://” + location.host +
“/XmlHttpExample1/DataFile.xml”, true);
xmlHttpObj.onreadystatechange = function() {
if ( xmlHttpObj.readyState == READYSTATE_COMPLETE ) {
// Extract the response text here and place in the div element document.getElementById(“divResults”).childNodes[0].nodeValue =
xmlHttpObj.responseText;
} } xmlHttpObj.send(null);
} }
</script>
Trang 6The code sets the value of the text property of the page’s single divelement to the responseTextof the returned data For this simple example, the DataFile.xmlfile that is requested using the
XMLHttpRequestobject contains the following:
<?xml version=”1.0” encoding=”utf-8” ?>
<Customers>
<Customer>
<Firstname>Joe</Firstname>
<Lastname>Bloggs</Lastname>
<email>joe@bloggs.com</email>
</Customer>
<Customer>
<Firstname>Alan</Firstname>
<Lastname>Anonymous</Lastname>
<email>anon@ymous.com</email>
</Customer>
<Customer>
<Firstname>Marvin</Firstname>
<Lastname>Martian</Lastname>
<email>marvin@mars.com</email>
</Customer>
</Customers>
When the page executes, and the button is clicked, the web browser displays the result shown in Figure 4-1
Figure 4-1
Trang 7As you can see, the data contained within the XML file is rendered in the page in exactly the same way it
is stored within the file
As mentioned previously, this might be okay for simple scenarios that require only a single unit of data
or data that is in a proprietary format, but for more complex scenarios you’ll want to use the responseXMLproperty
Using the responseXML Property
In a majority of scenarios, you will want to return multiple result items This might be a list of names to display in a drop-down list, a list of customers, or an object representation The XML format is ideally suited to this, and direct support for this format is provided by the responseXMLproperty of the XMLHttpRequestobject This property is a standard XML document object that can be examined and parsed using W3C DOM node tree methods and properties
For detailed reference material on the XML Document Object Model, visit http://msdn microsoft.com/library/en-us/xmlsdk30/htm/xmmscxmlreference.asp.
You can continue to modify the code example to extract values from the XML data that was retrieved and displayed in the previous example Rather than reproduce the entire previous code sample, we will show only the modified section that replaces the added code from the previous example that used the responseTextproperty Examine the following code:
if ( xmlHttpObj.readyState == READYSTATE_COMPLETE ) {
var doc = xmlHttpObj.responseXML;
var node = doc.selectSingleNode(“//Customers/Customer/Firstname/text()”);
document.getElementById(“divResults”).childNodes[0].nodeValue = node.nodeValue; }
Instead of simply displaying the entire set of returned data, you are extracting the text value of the first instance of the <Firstname>node You do this by utilizing the selectSingleNodemethod, which takes an X Path expression as an argument and returns an XML node object if one is found or NULLif the
X Path query is unsuccessful With the returned node object, you assign the nodeValueproperty of the first element of the childNodesproperty, which itself is a property of the divResultselement to the text value of the node
For those unfamiliar with the X Path language, you can think of it as a dynamic query language for XML documents that operates in a similar way to ANSI SQL for databases Nodes, elements, and data can be queried and returned using X Path expressions X Path expressions can contain conditional expressions and be quite complex For a detailed reference on X Path expressions and the associated syntax, visit the W3C site at www.w3.org/TR/xpath, or for something a little more readable, try
www.topxml.com/xsl/XPathRef.asp
This example is still fairly simplistic, though Now, you’re going to take your newfound knowledge and apply this by creating a simple web page that allows you to make a selection from a list of customers and display the fullname and email address by doing a server-side lookup into your XML data file asynchronously
Trang 8Enhancing Usability
One of the primary reasons that the asynchronous capability of the XMLHttpRequestobject has received
so much attention lately is that it can remove the interruption of the user experience that would nor-mally occur when a postback, or server-side call, is required to gather some server-side data
In the example that follows, you will provide a list of names via a drop-down list gathered from the XML data file When the user selects a name, a server call is issued to retrieve that data and extract the email address Without using the asynchronous capability of the XMLHttpRequestobject, this would require a postback, and the user would be forced to wait while the request was sent to the server and processed, and the results returned and displayed on the browser Now that you have the ability to per-form a server request asynchronously, the call to retrieve data can be perper-formed without interrupting the
user interaction on the browser It is, in effect, an invisible postback — a server request that is executed
behind the scenes and in parallel to any user interface interaction
Try It Out Performing a Server Request Asynchronously
Examine the code that follows, which is a fragment listing of a web page:
<body onload=”LoadCustomers();”>
<form id=”form1” runat=”server”>
<div>
<select id=”ddlCustomers” onchange=”DisplayCustomerDetails();”>
<option value=””>- Select a Customer -</option>
</select>
<hr />
<div>
<p>Details:</p>
<span id=”spnDetailDisplay”>(You have not made a selection yet.)</span>
</div>
</div>
</form>
</body>
</html>
Within the ASP.NET form element <form id=”form1” runat=”server”>is a <select>element that acts as your customer drop-down list and a <span>element where you can display the customer details You will also notice that the onloadevent of the document has a LoadCustomers()function assigned
to it to initially load in the list of customers and that the onchangeevent of the <select>item has a DisplayCustomerDetails()function assigned to it to display the selected customers details once a selection is made
Firefox does not currently support the use of the selectSingleNode()and
selectNodes()methods to access data within an XML document To address this,
the script include library includes some JavaScript code to enable this support The
code included was taken from the site http://km0ti0n.blunted.co.uk/mozXPath
.xap A full explanation of the details of this support is beyond the scope of this
chapter; suffice it to say that it allows support of the selectSingleNode()and
selectNodes()methods in the same way that Internet Explorer does.
Trang 9Next listed in the following code blocks are the JavaScript functions that accompany the page listing The first function is a generic function to simply create an XMLHttpRequest object that you can use:
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head runat=”server”>
<title>Untitled Page</title>
<script type=”text/javascript”>
// A “global” variable that is our XMLHttpRequest object reference
var xmlHttpObj = CreateXmlHttpRequestObject();
The next function is what is called when the document first loads and deals with loading the customer data from the server using the XMLHttpRequestobject:
// Function to load the customer selection data into the <SELECT> drop list control function LoadCustomers()
{
if (xmlHttpObj) {
// We want this request synchronous xmlHttpObj.open(“GET”,”http:// “ + location.host +
“/XmlHttp_Chap4/DataFile.xml”, false);
// Execute the request xmlHttpObj.send(null);
// If the request was ok (ie equal to a Http Status code of 200)
if (xmlHttpObj.status == 200) {
var xmlDoc = xmlHttpObj.responseXML;
// Our list of <CUSTOMER> nodes selected using the X Path argument //var nodes = xmlDoc.selectNodes(“//Customers/Customer”);
var nodes = xmlDoc.selectNodes(“//Customers/Customer/Lastname/text()”); // Obtain a reference to the <SELECT> drop list control
var ctrl = document.getElementById(“ddlCustomers”);
for (var i=0; i < nodes.length; i++) {
// Get the lastname element from our XML data document var lastName = nodes[i].nodeValue;
// Create a new <OPTION> node
var htmlCode = document.createElement(‘option’);
// Add the new <OPTION> node to our <SELECT> drop list ctrl.options.add(htmlCode);
// Set the <OPTION> display text and value;
htmlCode.text = lastName;
htmlCode.value = lastName;
} } else
Trang 10{ alert(‘There was a problem accessing the Customer data on the server.!’);
} } }
In the preceding code, you will notice the use of a literal number 200 in the following line of code:
if (xmlHttpObj.status == 200)
This is another perfect candidate to place into the common script include file The code can be defined in the following manner:
/* Common values for HTTP status codes */
var HTTPSTATUS_OK = 200;
which means that any code comparing the HTTP status codes as in the previous example can be
replaced with the following line of code:
if (xmlHttpObj.status == HTTPSTATUS_OK)
This makes the code much easier to read and maintain
Finally, you have the JavaScript function that deals with displaying a customer’s details once a selection
is made from the drop-down list:
function DisplayCustomerDetails()
{
if (xmlHttpObj) {
// We want this request asynchronous xmlHttpObj.open(“GET”,”http:// “ + location.host +
“/XmlHttp_Chap4/DataFile.xml”, true);
xmlHttpObj.onreadystatechange = function() {
if ( xmlHttpObj.readyState == READYSTATE_COMPLETE ) {
var ctrl = document.getElementById(“ddlCustomers”);
var doc = xmlHttpObj.responseXML;
var lastName = ctrl.options[ctrl.selectedIndex].value;
var node = doc.selectSingleNode(“//Customers/Customer[Lastname=’” + lastName + “‘]”);
var details = ‘Fullname: ‘ + node.selectSingleNode(‘Firstname/text()’).nodeValue +
‘ ‘ + lastName + ‘ Email: ‘ + node.selectSingleNode(‘email/text()’).nodeValue;
document.getElementById(“spnDetailDisplay”).childNodes[0].nodeValue
= details;
}