Section 20.1. Using XMLHttpRequest


20.1. Using XMLHttpRequest

Scripting HTTP with XMLHttpRequest is a three-part process:

  • Creating an XMLHttpRequest object

  • Specifying and submitting your HTTP request to a web server

  • Synchronously or asynchronously retrieving the server's response

The following subsections include more details on each step.

20.1.1. Obtaining a Request Object

The XMLHttpRequest object has never been standardized, and the process of creating one is different in Internet Explorer than on other platforms. (Fortunately, however, the API for using an XMLHttpRequest object, once created, is the same on all platforms.)

In most browsers, you create an XMLHttpRequest object with a simple constructor call:

 var request = new XMLHttpRequest(); 

Prior to Internet Explorer 7, IE does not have a native XMLHttpRequest() constructor function. In IE 5 and 6, XMLHttpRequest is an ActiveX object, and you must create it by passing the object name to the ActiveXObject() constructor:

 var request = new ActiveXObject("Msxml2.XMLHTTP"); 

Unfortunately, the name of the object is different in different releases of Microsoft's XML HTTP library. Depending on the libraries installed on the client, you may sometimes have to use this code instead:

 var request = new ActiveXObject("Microsoft.XMLHTTP"); 

Example 20-1 is a cross-platform utility function named HTTP.newRequest()for creating XMLHttpRequest objects.

Example 20-1. The HTTP.newRequest() utility

 // This is a list of XMLHttpRequest-creation factory functions to try HTTP._factories = [     function() { return new XMLHttpRequest(); },     function() { return new ActiveXObject("Msxml2.XMLHTTP"); },     function() { return new ActiveXObject("Microsoft.XMLHTTP"); } ]; // When we find a factory that works, store it here. HTTP._factory = null; // Create and return a new XMLHttpRequest object. // // The first time we're called, try the list of factory functions until // we find one that returns a non-null value and does not throw an // exception. Once we find a working factory, remember it for later use. // HTTP.newRequest = function() {     if (HTTP._factory != null) return HTTP._factory();     for(var i = 0; i < HTTP._factories.length; i++) {         try {             var factory = HTTP._factories[i];             var request = factory();             if (request != null) {                 HTTP._factory = factory;                 return request;             }         }         catch(e) {             continue;         }     }     // If we get here, none of the factory candidates succeeded,     // so throw an exception now and for all future calls.     HTTP._factory   = function() {         throw new Error("XMLHttpRequest not supported");     }     HTTP._factory(); // Throw an error } 

20.1.2. Submitting a Request

Once an XMLHttpRequest object has been created, the next step is to submit a request to a web server. This is itself a multistep process. First, call the open() method to specify the URL you are requesting and the HTTP method of the request. Most HTTP requests are done with the GET method, which simply downloads the content of the URL. Another useful method is POST, which is what most HTML forms use: it allows the values of named variables to be included as part of the request. HEAD is another useful HTTP method: it asks the server to just return the headers associated with the URL. This allows a script to check the modification date of a document, for example, without downloading the document content itself. Specify the method and URL of your request with the open() method:

 request.open("GET", url, false); 

By default, the open() method sets up an asynchronous XMLHttpRequest. Passing false as the third argument tells it to get the server's response synchronously instead. Asynchronous responses are generally preferred, but synchronous responses are slightly easier, so we will consider that case first.

In addition to the optional third argument, the open() method can also accept a name and password as optional fourth and fifth arguments. These are used when requesting a URL from a server that requires authorization.

The open() method does not actually send the request to the web server. It simply stores its arguments to use later when the request is actually sent. Before you send the request, you must set any necessary request headers. Here are some examples:[*]

[*] Details of the HTTP protocol are beyond the scope of this book. Refer to an HTTP reference for details on these and other headers that you can set when issuing an HTTP request.

 request.setRequestHeader("User-Agent", "XMLHttpRequest"); request.setRequestHeader("Accept-Language", "en"); request.setRequestHeader("If-Modified-Since", lastRequestTime.toString()); 

Note that the web browser automatically adds relevant cookies to the request you're building. You need to explicitly set a "Cookie" header only if you want to send a fake cookie to the server.

Finally, after creating the request object, calling the open() method, and setting headers, send the request to the server:

 request.send(null); 

The argument to the send() function is the body of the request. For HTTP GET requests, this is always null. For POST requests, however, it should contain the form data to be sent to the server (see Example 20-5). For now, simply pass null. (Note that the null argument is required. XMLHttpRequest is a client-side object andin Firefox, at leastits methods are not forgiving of omitted arguments as core JavaScript functions are.)

20.1.3. Obtaining a Synchronous Response

The XMLHttpRequest object holds not only the details of the HTTP request that is made but also represents the server's response. If you pass false as the third argument to open(), the send() method is synchronous: it blocks and does not return until the server's response has arrived.[*]

[*] XMLHttpRequest has wonderfully powerful features, but its API is not particularly well designed. The boolean value that specifies synchronous or asynchronous behavior really ought to be an argument of the send() method, for example.

send() does not return a status code. Once it returns, you can check the HTTP status code returned from the server with the status property of the request object. The possible values of this code are defined by the HTTP protocol. A status of 200 means that the request was successful and that the response is available. A status of 404, on the other hand, is a "not found" error that occurs when the requested URL does not exist.

The XMLHttpRequest object makes the server's response available as a string through the responseText property of the request object. If the response is an XML document, you can also access the document as a DOM Document object through the responseXML property. Note that the server must identify its XML documents with the MIME type "text/xml" in order for XMLHttpRequest to parse the response into a Document object.

When a request is synchronous, the code that follows send() usually looks something like this:

 if (request.status == 200) {     // We got the server's response. Display the response text.     alert(request.responseText); } else {     // Something went wrong. Display error code and error message.     alert("Error " + request.status + ": " + request.statusText); } 

In addition to the status codes and the response text or document, the XMLHttpRequest object also provides access to the HTTP headers returned by the web server. getAllResponseHeaders() returns the response headers as an unparsed block of text, and getresponseHeader() returns the value of a named header. For example:

 if (request.status == 200) {  // Make sure there were no errors     // Make sure the response is an XML document     if (request.getResponseHeader("Content-Type") == "text/xml") {         var doc = request.responseXML;         // Now do something with the response document     } } 

There is one serious problem with using XMLHttpRequest synchronously: if the web server stops responding, the send() method blocks for a long time. JavaScript execution stops, and the web browser may appear to have hung (this is platform-dependent, of course). If a server hangs during a normal page load, the user can simply click the browser's Stop button and try another link or another URL. But there is no Stop button for XMLHttpRequest. The send() method does not offer any way to specify a maximum length of time to wait, and the single-threaded execution model of client-side JavaScript does not allow a script to interrupt a synchronous XMLHttpRequest once the request has been sent.

The solution to these problems is to use XMLHttpRequest asynchronously.

20.1.4. Handling an Asynchronous Response

To use an XMLHttpRequest object in asynchronous mode, pass TRue as the third argument to the open() method (or simply omit the third argument: true is used by default). If you do this, the send() method sends the request to the server and then returns immediately. When the server's response arrives, it becomes available through the XMLHttpRequest object via the same properties described earlier for synchronous usage.

An asynchronous response from the server is just like an asynchronous mouse click from a user: you need to be notified when it happens. This is done with an event handler. For XMLHttpRequest, the event handler is set on the onreadystatechange property. As the name of this property implies, the event-handler function is invoked whenever the value of the readyState property changes. readyState is an integer that specifies the status of an HTTP request, and its possible values are enumerated in Table 20-1. The XMLHttpRequest object does not define symbolic constants for the five values listed in the table.

Table 20-1. XMLHttpRequest readyState values

readyState

Meaning

0

open() has not been called yet.

1

open() has been called, but send() has not been called.

2

send() has been called, but the server has not responded yet.

3

Data is being received from the server. readyState 3 differs somewhat in Firefox and Internet Explorer; see Section 20.1.4.1.

4

The server's response is complete.


Since XMLHttpRequest has only this one event handler, it is invoked for all possible events. A typical onreadystatechange handler is invoked once when open() is called and again when send() is called. It is invoked again when the server's response starts arriving, and one final time when the response is complete. In contrast to most events in client-side JavaScript, no event object is passed to the onreadystatechange handler. You must check the readyState property of the XMLHttpRequest object to determine why your event handler was invoked. Unfortunately, the XMLHttpRequest object is not passed as an argument to the event handler, either, so you'll have to make sure that the event-handler function is defined in a scope from which it can access the request object. A typical event handler for an asynchronous request looks like this:

 // Create an XMLHttpRequest using the utility defined earlier var request = HTTP.newRequest(); // Register an event handler to receive asynchronous notifications. // This code says what to do with the response, and it appears in a nested // function here before we have even submitted the request. request.onreadystatechange = function() {     if (request.readyState == 4) {  // If the request is finished         if (request.status == 200)  // If it was successful             alert(request.responseText);  // Display the server's response     } } // Make a GET request for a given URL. We don't pass a third argument, // so this is an asynchronous request request.open("GET", url); // We could set additional request headers here if we needed to. // Now send the request. Since it is a GET request, we pass null for // the body. Since it is asynchronous, send() does not block but // returns immediately. request.send(null); 

20.1.4.1. Notes on readyState 3

The XMLHttpRequest object has never been standardized, and browsers differ in their handling of readyState 3. For example, during large downloads, Firefox invokes the onreadystatechange handler multiple times in readyState 3, to provide download progress feedback. A script might use these multiple invocations to display a progress indicator to the user. Internet Explorer, on the other hand, interprets the event handler name strictly, and invokes it only when the readyState value actually changes. This means that it is invoked only once for readyState 3, no matter how large the downloaded document is.

Browsers also differ as to what part of the server's response is available in readyState 3. Even though state 3 means that some part of the response has arrived from the server, Microsoft's documentation for XMLHttpRequest explicitly states that it is an error to query responseText in this state. In other browsers, it appears to be an undocumented feature that responseText returns whatever portion of the server's response is available.

Unfortunately, none of the major browser vendors have produced adequate documentation of their XMLHttpRequest objects. Until there is a standard or at least clear documentation, it is safest to ignore any value of readyState other than 4.

20.1.5. XMLHttpRequest Security

As part of the same-origin security policy (see Section 13.8.2.), the XMLHttpRequest object can issue HTTP requests only to the server from which the document that uses it was downloaded. This is a reasonable restriction, but you can circumvent it if you need to, by using a server-side script as a proxy to fetch the content of some off-site URL for you.

This XMLHttpRequest security restriction has one very important implication: XMLHttpRequest makes HTTP requests and does not work with other URL schemes. It cannot work with URLs that use the file:// protocol, for example. This means that you cannot test XMLHttpRequest scripts from your local filesystem. You must upload your test scripts to a web server (or run a server on your desktop). Your test scripts must be loaded into your web browser via HTTP in order for them to make HTTP requests of their own.




JavaScript. The Definitive Guide
JavaScript: The Definitive Guide
ISBN: 0596101996
EAN: 2147483647
Year: 2004
Pages: 767

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