Section 3.1. The XMLHttpRequest Object


3.1. The XMLHttpRequest Object

The foundation of Ajax is the XMLHttpRequest object, which enables you to make HTTP requests (and get responses), without performing a full page postback and refresh.

History of the XMLHttpRequest Object

The first implementation of XMLHttpRequest could be found in Internet Explorer 5, which was released in 1999. That release of the browser included an ActiveX object called XMLHttpRequest that did what the name suggests: make an HTTP request and get a message back. (The format of the returned message could be an XML message, but that was not a requirement.) Originally, the Internet Explorer engineers needed this functionality for the web frontend to Outlook, so they could make Outlook Web Access (OWA) behave more like a desktop application. For some time, the useful addition of the XMLHttpRequest object went unnoticed by web programmers. However, competing browser developers later incorporated a compatible version of XMLHttpRequest in their applications. Because only Internet Explorer supports ActiveX controls, other browsers implemented the XMLHttpRequest object natively in their browser.

After Internet Explorer, the first browser to support XMLHttpRequest was the Mozilla browser (not to be confused with the code name for early Netscape browsers) in its 1.0 version. Subsequent versions of Mozilla as well as derivatives, such as the Camino browser for Mac OS X and Firefox, implement XMLHttpRequest. Apple then added appropriate support in the 1.2 version of their Safari browser. Safari is based on the KHTML renderer that is part of Konqueror, the web browser of the KDE desktop environment for Linux. Apple engineers later back-ported support for the XMLHttpRequest object to Konqueror as well.

Opera 8.0 and later also included XMLHttpRequest support in their browser, as did the rather exotic system Open Laszlo from IBM.


A significant portion of the web browser market does support XMLHttpRequest and therefore is Ajax-compatible. According to a study conducted by Net Applications (http://www.netapplications.com) in November 2005, approximately 99 percent of the browsers in use are Internet Explorer 5 or later, Mozilla 1.0 or later, Firefox 1.0 or later, Opera 8 or later, Safari 1.2 or later, or KDE 3 or later. So does this mean that almost everybody can experience Ajax applications?

XMLHttpRequest and Standards

Despite being supported on most browsers, the XMLHttpRequest object is still nonstandard, since it is not part of the ECMAScript specification. There is, however, a W3C specification that defines similar functionality, namely dynamically loading and sending XML back to the server. The specification is called "DOM Level 3 Load and Save," and has been a W3C recommendation since April 2004 (http://www.w3.org/TR/DOM-Level-3-LS). This standard has not been implemented in any popular browser yet, and it will probably take time before browser developers start working on it.

On the other hand, W3C recently started an initiative to standardize XMLHttpRequest; refer to http://www.w3.org/TR/XMLHttpRequest for more information.


The answer, unfortunately, is no. Depending on which study you trust, between 5 to 15 percent of web users have disabled JavaScript in their browser, perhaps because of recurring reports of security vulnerabilities in browsers or because of corporate policies. As a result, it's possible a significant portion of your users cannot use applications that rely on JavaScript, which includes Ajax applications, in spite of the widespread adoption of up-to-date browsers. Therefore, you always need a fallback plan for those times your application encounters an Ajax-resistant browser.

3.1.1. Programming the XMLHttpRequest Object

How you instantiate the XMLHttpRequest object depends on the browser in which your code executes. For Internet Explorer 5 and later versions, the code shown in the following snippet does the work. It tries two methods to instantiate XMLHttpRequest, because different versions of Internet Explorer have different versions of the Microsoft XML library installed on the system. To avoid error messages when one of the methods fails, two TRy-catch blocks are used.

 var XMLHTTP = null; try {   XMLHTTP = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {   try {     XMLHTTP = new ActiveXObject("Microsoft.XMLHTTP");   } catch (e) {   } } 

For browsers other than Internet Explorer, a simpler syntax is available:

 XMLHTTP = new XMLHttpRequest(); 

So all that is required is to determine which browser type is in use and then instantiate the XMLHttpRequest object accordingly. For instance, the following code checks whether an ActiveX object can be instantiated by testing the ActiveXObject property of the window object; if this code works, the browser must be Internet Explorer.

 if (window.ActiveXObject) {   // it's probably IE } 

Similarly, you can use the following snippet to check for the presence of an XMLHttpRequest object, which, if found, means that you are using Mozilla and its derivatives, or that you are using Opera, Konqueror, or Safari:

 if (XMLHttpRequest) {   // it's probably not IE } 

However, checking for the XMLHttpRequest object directly causes Internet Explorer to display the error message "XMLHttpRequest is undefined," as shown in Figure 3-1. This will change with release of Internet Explorer 7 (currently in beta test), which will provide a native XMLHttpRequest object.

Figure 3-1. Internet Explorer does not like our code


What's needed instead is an approach that uses all of the tests shown here. The JavaScript typeof operator is used to determine the type of an expression 47nd returns "undefined" as a string if the expression evaluates to "undefined." This feature enables you to detect browsers that are not Internet Explorer, as shown in this code:

 if (typeof XMLHttpRequest != "undefined") {   //it's not IE <= 6 } 

Here's code for a function, getXMLHTTP(), that aggregates the previous snippets to return an XMLHttpRequest object regardless of which Ajax-enabled, JavaScript-activated browser is used.

 function getXMLHTTP() {   var XMLHTTP = null;   try {     XMLHTTP = new ActiveXObject("Msxml2.XMLHTTP");   } catch (e) {     try {       XMLHTTP = new ActiveXObject("Microsoft.XMLHTTP");     } catch (e) {       if (typeof XMLHttpRequest != "undefined") {         XMLHTTP = new XMLHttpRequest();       }     }   }   return XMLHTTP; } 

Another approach is to use standard JavaScript to determine browser capabilities and check window.XMLHttpRequest instead of just XMLHttpRequest to find out whether the native XMLHttpRequest object is supported by the browser. Using this technique, the function to return the object can be written slightly differently, as shown in the following code:

 function getXMLHTTP() {   var XMLHTTP = null;   if (window.ActiveXObject) {     try {       XMLHTTP = new ActiveXObject("Msxml2.XMLHTTP");     } catch (e) {       try {         XMLHTTP = new ActiveXObject("Microsoft.XMLHTTP");       } catch (e) {       }     }   } else if (window.XMLHttpRequest) {     try {       XMLHTTP = new XMLHttpRequest();     } catch (e) {     }   }   return XMLHTTP; } 

The XMLHttpRequest object, no matter which browser created it, has a set of properties and methods that are used for sending HTTP requests and receiving the server's response. In most scenarios, the following four steps must be taken to create an HTTP request and evaluate the return values:

  1. Create an XMLHttpRequest object as shown in the preceding examples.

  2. Call the object's open() method to prepare the request.

    The open() method expects up to five parameters, but usually you only need the first two: the method type of the request (usually "GET" or "POST"), and the target URL (relative or absolute).

    The third parameter of open() defaults to true, meaning that the request is an asynchronous one. If you set it to false, the request is synchronous, meaning that the script halts until the response has completed. Generally, you want the asynchronous behavior, so you either omit the parameter or set it to TRue. If the HTTP request requires authentication, you can use the fourth and fifth parameter to provide a username and a password.

  3. Provide a reference to a callback function in the onreadystatechange property.

    This function will be called when the server returns an HTTP response to the HTTP request.

  4. Send the HTTP request with the send() method.

    This starts the HTTP request; the script continues executing, if asynchronous communication is used.

Since all JavaScript code is evaluated client-side, there is no reliable way to prevent users from having a look at the source code. There are several ways that can help the situation a bit, including JavaScript code to disable right-clicking, or client-side JavaScript code obfuscation, but all of these can be defeated. In general, your JavaScript code is not safe, so it is not a good idea to put sensitive information like a username and a password verbatim into the JavaScript code. Therefore, the fourth and fifth parameter of open() are very rarely used.


Setting the onreadystatechange property of the XMLHttpRequest object provides the callback mechanism for the HTTP response. The property name is related to the name of another XMLHttpRequest property, readyState, which indicates the state of XMLHttpRequest object with five possible values, as listed in Table 3-1.

Table 3-1. Possible values for readyState

Value of readyState

Description

0

Object is uninitialized

1

Request is loading

2

Request is fully loaded

3

Request is waiting for user interaction

4

Request is complete


Whenever the value of readyState changes, the function provided in the onreadystatechange property is called. In this function, you first have to check the value of readyState; typically, you are determining if the value is 4.

Then some other properties of the XMLHttpRequest object come into play. The status property contains the HTTP status returned by the request; if everything worked, the status is 200. The statusText property holds the associated textual description of the HTTP status. For instance, for HTTP status 200, the value of statusText is "OK". Checking the status property, however, is more reliable, because different web servers might return different text for the status codes.

Two properties provide access to the return value from the server:


responseText

Returns the response data as a string


responseXML

Returns the response data as an XML document (detailed later in the section "The XMLDocument Object")

The following script is a small example that illustrates how to use the XMLHttpRequest object. In the example, the request is made to an ASP.NET page named ajax.aspx. In the first step, the getXMLHTTP() function is used to create the XMLHttpRequest object. If that works (that is, the return value of the function is not null), a GET request is sent to the server with the parameter sendData=ok (an arbitrary value, just for the example). Then the onreadystatechange property is set to a function, and finally the request is sent to the server.

 var XMLHTTP = getXMLHTTP(); if (XMLHTTP != null) {   XMLHTTP.open("GET", "ajax.aspx?sendData=ok");   XMLHTTP.onreadystatechange = stateChanged;   XMLHTTP.send(null); } 

The stateChanged() function might look something like the following (with error reporting omitted). This script displays whatever text the server has sent as the response.

 function stateChanged() {   if (XMLHTTP.readyState == 4 &&       XMLHTTP.status == 200) {     window.alert(XMLHTTP.responseText);   } } 

Anonymous JavaScript Functions

To provide the client-side functionality when readyState changes, instead of referencing a standalone function you can use JavaScript anonymous functions. These are functions without names that are declared as part of an expression. Here is how this can look in the given example:

 var XMLHTTP = getXMLHTTP(); if (XMLHTTP != null) {   XMLHTTP.open("GET", "ajax.aspx?sendData=ok");   XMLHTTP.onreadystatechange = function() {     if (XMLHTTP.readyState == 4 &&         XMLHTTP.status == 200) {       window.alert(XMLHTTP.responseText);     }   };   XMLHTTP.send(null); } 


Note that the function that is called when readyState changes does not accept any parameters. Therefore, the XMLHttpRequest object must be global; otherwise, you cannot access it from within the function invoked by the asynchronous call.

Of course, you must also have server code to handle the request made by the XMLHttpRequest object. The following code shows a Page_Load event handler in an ASP.NET page that can respond to the asynchronous request made by the XMLHttpRequest object.

 void Page_Load() {   if (Request.QueryString["sendData"] != null &&     Request.QueryString["sendData"] == "ok")   {     Response.Write("Hello from the server!");     Response.End();   } } 

You can put all of these pieces together (both client script and server code) into a single page named ajax.aspx, as shown in Example 3-1.

To see this example in action, you must run it as a page named ajax.aspx using a web server on a computer where the .NET Framework is installed.


Example 3-1. A simple example combining Ajax and ASP.NET.

 ajax.aspx <%@ Page Language="C#" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server">   void Page_Load()   {     if (Request.QueryString["sendData"] != null &&         Request.QueryString["sendData"] == "ok")     {       Response.Write("Hello from the server!");       Response.End();     }   } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server">   <title>Ajax with ASP.NET</title>   <script language="Javascript" type="text/javascript"> function getXMLHTTP() {   var XMLHTTP = null;   if (window.ActiveXObject) {     try {       XMLHTTP = new ActiveXObject("Msxml2.XMLHTTP");     } catch (e) {       try {         XMLHTTP = new ActiveXObject("Microsoft.XMLHTTP");       } catch (e) {       }     }   } else if (window.XMLHttpRequest) {     try {       XMLHTTP = new XMLHttpRequest();     } catch (e) {     }   }   return XMLHTTP; } var XMLHTTP = getXMLHTTP(); if (XMLHTTP != null) {   XMLHTTP.open("GET", "ajax.aspx?sendData=ok");   XMLHTTP.onreadystatechange = stateChanged;   XMLHTTP.send(null); } function stateChanged() {   if (XMLHTTP.readyState == 4 &&       XMLHTTP.status == 200) {     window.alert(XMLHTTP.responseText);   } }   </script> </head> <body>   <p>Wait and see ...</p> </body> </html> 

If you see the text "Wait and see..." but the browser never displays a dialog box with "Hello from the server!", double-check that you are working with the filename ajax.aspx.


As you can see in Figures 3-2, 3-3, and 3-4, this code works beautifully in Internet Explorer, Firefox, and Konqueror (using Mono for ASP.NET), the most commonly used browsers. It should work equally well in any other browser you choose to test.

Figure 3-2. The example works in Internet Explorer


Figure 3-3. The example works in Firefox


Figure 3-4. The example works in Konqueror and other browsers


This is one of the few places in this book where I've taken screenshots for more than one browser. Generally, the listings in this book work with ASP.NET 2.0 on the server and any JavaScript-enabled, reasonably recent browser on the client. Most screenshots in this book are taken with Firefox 1.5. If we noticed discrepancies in using the examples with different browsers, this is noted. However, at the time of writing, Atlas has not been released in a final version yet, so there may also be bugs in Atlas that have yet to be fixed.


If you want to use a POST command for the HTTP request, just set the first parameter of the open() method appropriately. Using PO0ST is especially important when you are sending 500 bytes or more of data (you might exceed the maximum URL length for the server) or when you want to avoid 53aching by proxy servers. The data you want to send is provided in the send() function, in name-value pairs and URL-encoded, if needed, as shown in the following snippet:

 XMLHTTP.open("POST", "ajax.aspx"); XMLHTTP.onreadystatechange = stateChanged; XMLHTTP.send("sendData=ok&returnValue=123"); 

Data sent with a POST command can be read on the server, in the case of ASP.NET using Request.Form for POST instead of the Request.QueryString property used to read GET requests.

For web service calls that use the SOAP protocol, you may have to send XML directly, without URL-encoding. However, for this to work with the Safari and Konqueror browsers (and therefore to maximize your potential audience), you have to explicitly set the request content type to text/xml. (Other browsers do not require this content specification.) The following snippet shows how to do this:

 XMLHTTP.open("POST", "ajax.aspx"); XMLHTTP.onreadystatechange = stateChanged; XMLHTTP.setRequestHeader("Content-Type", "text/xml"); XMLHTTP.send("<soap:Envelope>...</soap:Envelope>"); 

A complete reference of properties and methods of the XMLHttpRequest object is available in Appendix A.


A word regarding security: by default, XMLHttpRequest can access resources only in the same domain as the client script. Unfortunately, this limits the capabilities of the technology, since there is no easy way to call a web service using Ajax. (However, Chapter 10 shows some ways to get around this limitation.) Mozilla browsers support accessing remote servers in another domain by explicitly prompting the user for additional privileges; Figure 3-5 shows the message prompting the user for those. However, this approach generates several additional issues of its own and is not browser-agnostic, which is why this is very rarely in use nowadays and not used in this book. So all HTTP requests illustrated in this book are to the server from which the page itself originates.

Figure 3-5. Requesting additional privileges in Mozilla browsers





Programming Atlas
Programming Atlas
ISBN: 0596526725
EAN: 2147483647
Year: 2006
Pages: 146

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net