XMLHttpRequest Call






XMLHttpRequest Call

Call, Callback, Download, Grab, Live, Query, Remoting, RemoteScripting, Synchronise, Synchronize, Upload, XMLHttpRequest

XMLHttpRequest Call


Goal Story

Reta's purchasing some items from a wholesaler's web site. Each time she adds an item to the shopping cart, the web site issues an XMLHttpRequest to save the latest cart contents. There's no form submission, so the item is added instantaneously, which saves Reta time as well as helping her understand what's going on.

Problem

How can the browser communicate with the server?

Forces

  • Ajax Apps require browser-server communication. User-generated information must be uploaded and new server information must be downloaded.

  • Because Ajax Apps should have a smooth and continuous feel, browser-server communication must be unobtrusive.

  • Ajax Apps should be highly responsive, so calls should involve minimal data transfer.

  • As the network is often unreliable and performance is inconsistent, calls should be asynchronous, allowing the user to keep working while network calls are in progress.

Solution

Use XMLHttpRequest objects for browser-server communication. JavaScript lacks a portable mechanism for general network communication, a restriction that's always been in place for security reasons and will probably remain. But thanks to the XMLHttpRequest objectnow available in all major browsersJavaScript code can make HTTP calls back to its originating server and get hold of the results. Doing so enables you to make fine-grained server calls and deal with responses as you wish, unlike conventional form submissions, which cause a complete page refresh. Note: an online demo (http://ajaxify.com/run/xmlHttpRequestCall) illustrates the code concepts throughout this Solution and the code snippets loosely follow from the demo.

This pattern uses the sum web service described earlier in Web Service, with a URL like http://ajaxify.com/run/xmlHttpRequestCall/sumGet.phtml?figure1=5&figure2=10. It returns just the sum, "15" in this case. You can test that by typing the full URL in the browser, but here we want to call it from JavaScript and catch the result. Here's a very basic example:

  var xhReq = new XMLHttpRequest( );
  xhReq.open("GET", "sumGet.phtml?figure1=5&figure2=10", false);
  xhReq.send(null);
  var serverResponse = xhReq.responseText;
  alert(serverResponse); // Shows "15"

The sequence begins by creating a new instance of XMLHttpRequest. xhReq.open( ) then prepares a call on the test service, sumGet.phtml (the code's running from the same path, so the domain and path need not be qualified). The GET signifies the request method to be used. The false argument says the call is synchronous, meaning that the code will block until a response comes back. The send command completes the request. Because the call is synchronous, the result is ready as soon as the next line is executed. The XMLHttpRequest object has saved the response from the server, and you can access it with the responseText field.

The above example shows that the fundamental technology is pretty simple. However, be aware that it's a very basic usage that's not yet fit for production. Fundamental questions remain, which are answered throughout this Solution:

  • How do you get hold of an XMLHttpRequest?

  • How do asynchronous calls work?

  • How do you handle errors?

  • What if the service requires a POST or PUT request rather than a GET?

  • What constraints apply to external domains?

  • How can you deal with XML responses?

  • What's the API?

As you read all this, be aware that many, many libraries are available to handle remoting (see Appendix A). Most developers shouldn't need to touch XMLHttpRequest directly. Having said that, it's good to be aware of the capabilities and limitations of XMLHttpRequest Calls, along with the other web-remoting techniques. This knowledge will help you select the most appropriate library and help with any bugs you might encounter.

Creating XMLHttpRequest objects

In most browsers, XMLHttpRequest is a standard JavaScript class, so you just create a new instance of XMLHttpRequest. However, Microsoft is the inventor of XMLHttpRequest, and until IE7, IE offered it only as an ActiveX object. To make things even more fun, there are different versions of that object. The following code shows a factory function that works on all browsers that support XMLHttpRequest:

  function createXMLHttpRequest( ) {
      try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
      try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
      try { return new XMLHttpRequest( ); } catch(e) {}
      alert("XMLHttpRequest not supported");
      return null;
    }
    ...
  var xhReq = createXMLHttpRequest( );

You really need to use a function like this for maximum portability. Once you have the object, its basic functionality and API are pretty consistent across browsers, but be sure to test carefully as there are a few subtle implementation differences in some browsers. (If you're curious, the "Solution" in HTTP Streaming [later in this chapter] highlights one such inconsistency.)

You can also reuse an XMLHttpRequest; it's worthwhile doing so in order to prevent memory leaks. To be safe, start a new call only when there's not one already in progress. As explained below, it's possible to inspect the status of a call, and you should only start a call if the status is 0 or 4. So if it's anything else, first call the abort( ) method to reset status.

Asynchronous calls

I previously mentioned in the "Solution" that under synchronous mode, "the code will block until a response comes back." Some hardened readers probably writhed uncomfortably at the thought. We all know that some requests take a long time to process, and some don't come back at all. Pity the user when a server script is buried in an infinite loop.

In practice, XMLHttpRequest Calls should almost always be asynchronous. That means the browser and the user can continue working on other things while waiting for a response to come back. How will you know when the response is ready? The XMLHttpRequest's readyState always reflects the current point in the call's lifecycle. When the object is born, it's at 0. After open( ) has been called, it's 1. The progression continues until the response is back, at which point the value is 4.

So, to catch the response, you need to watch for a readyState of 4. That's easy enough, because XMLHttpRequest fires readystatechange events. You can declare a callback function using the onreadystatechange field. The callback will then receive all state changes. The states below 4 aren't especially useful and are somewhat inconsistent across browser types anyway (http://www.quirksmode.org/blog/archives/2005/09/xmlhttp_notes_r_2.html). So most of the time, all we're interested in is, "Are you in state 4 (i.e., complete) or not?"

Based on all that, here's an asynchronous version of the code shown earlier:

  var xhReq = createXMLHttpRequest( );
  xhReq.open("GET", "sumGet.phtml?figure1=5&figure2=10", true);
  xhReq.onreadystatechange = onSumResponse;
  xhReq.send(null);
  ...
  function onSumResponse( ) {
    if (xhReq.readyState != 4)  { return; }
    var serverResponse = xhReq.responseText;
    ...
  }

As shown, you declare the callback method in XMLHttpRequest's onreadystatechange property. In addition, the third argument of open( ) is now true. This argument is actually called the "asynchronous flag," which explains why we're now setting it to true. The callback function, onSumResponse, is registered using onreadystatechange and contains a guard clause to ensure the readyState is 4 before any processing can occur. At that point, we have the full response in responseText.

JavaScript also supports "closures"a form of anonymous functionwhich suggests a more concise boilerplate structure for asynchronous calls:

  var xhReq = createXMLHttpRequest( );
  xhReq.open("get", "sumget.phtml?figure1=10&figure2=20", true);
  xhReq.onreadystatechange = function( ) {
    if (xhReq.readyState != 4)  { return; }
         var serverResponse = xhReq.responseText;
         ...
  };
  xhReq.send(null);

Use closures sparingly, because you're defining a new function each time. It's slower than referring to an existing one and might also lead to memory leaks.

Asynchronous calls are essential, but also more error-prone. If you look at the callback mechanism, you might notice the potential for a subtle, but serious, bug. The problem arises when the same instance of XMLHttpRequest is simultaneously used for different calls. If Call 2 is issued while the object is still waiting for the response of Call 1, what will the callback function receive? In fact, it's even possible the callback function itself is changed before the first call returns. There are ways to deal with this problem, and they're the topic of the Call Tracking (Chapter 10) pattern.

Detecting errors

Sometimes, a request doesn't come back as you expected it, or maybe not at all. You scripted the call wrong, or there's a bug in the server, or some part of the infrastructure just screwed up. Thinking asynchronously is the first step to dealing with these problems, because at least your application isn't blocked. But you need to do more than that.

To detect a server error, you can check the response status using XMLHttpRequest's status flag. This is just a standard HTTP code. For example, if the resource is missing, XMLHttpRequest.status will take on the famous "404" value. In most cases, you can assume anything other than 200 is an error situation. This suggests adding a new check to the callback function of the previous section:

  xhReq.onreadystatechange = function( ) {
    if (xhReq.readyState != 4)  { return; }
    if (xhReq.status != 200)  {
    var serverResponse = xhReq.responseText;
    ...
  };

That's great if the browser knows a problem occurred, but sometimes the request will be lost forever. Thus, you usually want some kind of timeout mechanism (http://ajaxblog.com/archives/2005/06/01/async-requests-over-an-unreliable-network) as well. Establish a Scheduling timer to track the session. If the request takes too long, the timer will kick in and you can then handle the error. XMLHttpRequest has an abort( ) function that you should also invoke in a timeout situation. Here's a code sample:

  var xhReq = createXMLHttpRequest( );
  xhReq.open("get", "infiniteLoop.phtml", true); // Server stuck in a loop.
  var requestTimer = setTimeout(function( ) {
  xhReq.onreadystatechange = function( ) {
    if (xhReq.readyState != 4)  { return; }
    clearTimeout(requestTimeout);
    if (xhReq.status != 200)  {
      // Handle error, e.g. Display error message on page
      return;
    }
    var serverResponse = xhReq.responseText;
    ...
  };

Compared to the previous example, a timer has been introduced. The onreadystatechange( ) callback function will clear the timer once it receives the full response (even if that response happens to be erroneous). In the absence of this clearance, the timer will fire, and in this case, the setTimeout sequence stipulates that abort( ) will be called and some recovery action can then take place.

Handling POSTs and other request types

Up to this point, requests have been simple GET queriespass in a URL and grab the response. As discussed in the RESTful Service (Chapter 9), real-world projects need to work with other request types as well. POST, for example, is suited to calls that affect server state or upload substantial quantities of data. To illustrate, let's now create a new service, sumPostGeneric.phtml, that does the same thing as postGet.phtml but with a POST message. It's called "generic" because it reads the full message body text, as opposed to a CGI-style form submission. In this case, it expects a body such as "Calculate this sum: 5+6" and returns the sum value:

  <?
    $body = readBody( );
    ereg("Calculate this sum: ([0-9]+)\+([0-9]+)", $body, $groups);
    echo $groups[1] + $groups[2];

    // A PHP method to read arbitrary POST body content.
    function readBody( ) {
      $body="";
      $putData = fopen("php://input", "r");
      while ($block = fread($putData, 1024)) {
        $body = $body.$block;
      }
      fclose($putData);
      return $body;
    }
  ?>

To POST an arbitrary body, we give XMLHttpRequest a request type of POST and pass the body in as an argument to send( ). (Note that with GET queries, the send( ) argument is null as there's no body content).

  var xhreq = createxmlhttprequest( );
  xhreq.open("post", "sumPostGeneric.phtml", true);
  xhreq.onreadystatechange = function( ) {
    if (xhreq.readystate != 4) { return; }
    var serverResponse = xhreq.responsetext;
    ...
  };
  xhreq.send("calculate this sum: 5+6");

Quite often, though, you'll be posting key-value pairs, so you want the message to look as if it were submitted from a POST-based form. You'd do that because it's more standard, and server-side libraries make it easy to write web services that accept standard form data. The service shown in the code example following the next one, sumPostForm.php, shows how PHP makes light work of such submissions, and the same is true for most languages:

  <?
    echo $_POST["figure1"] + $_POST["figure2"];
  ?>

For the browser script to make a CGI-style upload, two additional steps are required. First, declare the style in a "Content-Type" header; as the example below shows, XMLHttpRequest lets you directly set request headers. The second step is to make the body a set of name-value pairs:

  var xhreq = createxmlhttprequest( );
  xhreq.open("post", "sumPostForm.phtml", true);
  xhReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  xhreq.onreadystatechange = function( ) {
    if (xhreq.readystate != 4) { return; }
    var serverresponse = xhreq.responsetext;
    ...
  };
  xhreq.send("calculate this sum: 5+6");

GET and POST are virtually ubiquitous, but RESTful Service points out there's a time and place for other request methods too, such as PUT and DELETE. You don't have to do anything special with those other methods; just set the request type in the open( ) call and send( ) an appropriate body (the item you're putting in the case of PUT; a null argument in the case of DELETE).

Constraints on external domains

On discovering XMLHttpRequest, a common reaction is to start dreaming up an interface that pulls in content from popular web sites and mashes it altogether to into one big Web 2.0 soufflé. Unfortunately, it's not so simple because of a key security rule imposed by all major browsers: XMLHttpRequest can only access content from the originating server. If your application lives at http://ajax.shop/admin, then your XMLHttpRequest objects can happily reach http://ajax.shop/admin/products.html and http://ajax.shop/products/contents.html, shouldn't be able to reach http://books.ajax.shop/contents.html, and definitely won't have access to http://google.com.

This "same-origin policy" (or "same-domain policy") (http://www.mozilla.org/projects/security/components/same-origin.html) will be familiar to developers of Java applets and Flash, where the policy has always been in place. It's there to prevent all kinds of abuse, such as a malicious script grabbing confidential content from one server and uploading it to another server under their own control. Some have suggested it's possibly overkill, that most of the risks it tries to prevent are already possible by other means (http://spaces.msn.com/members/siteexperts/Blog/cns!1pNcL8JwTfkkjv4gg6LkVCpw!2085.entry). However, restrictions like this won't be lifted lightly; the rule's likely to be around for the long term, so we had better learn to work with it.

Given same-origin restrictions, then, how do all those Ajax mashup sites work (http://housingmaps.com)? The answer is that the cross-domain transfers usually run through the originating server, which acts as a kind of proxyor tunnelallowing XMLHttpRequests to communicate with external domains. Cross-Domain Proxy (Chapter 10) elaborates on the pattern, and its "Alternatives" section lists some clever workarounds that do allow the originating server to be bypassed.

XML responses

The discussion here has swiftly ignored the big elephant in the room: XML. XMLHttpRequest, as its name suggests, was originally designed with, yes, XML in mind. As we've already seen, it will actually accept any kind of response, so what's special about XML? With XMLHttpRequest, any responses can be read via the responseText field, but there's also an alternative accessor: responseXML. If the response header indicates the content is XML, and the response text is a valid XML string, then responseXML will be the DOM object that results from parsing the XML.

The Display Maniputlation (Chapter 5) patterns have already illustrated how JavaScript supports manipulation of DOM objects. In those patterns, we were only interested in one particular DOM object, the HTML (or XHTML) document representing the current web page. But you can manipulate any other DOM object just as easily. Thus, it's sometimes convenient to have a web service output XML content and manipulate the corresponding DOM object.

The prerequisite here is a Web Service (see earlier in this chapter) that outputs valid XML. There are many libraries and frameworks around for automatically generating XML from databases, code objects, files, or elsewhere. But don't think you have to start learning some fancy XML library in order to create XML web services, because it's fairly easy to hand code them too, at least for simple data. The service just needs to output an XML Content-type header followed by the entire XML document. Here's an XML version of the sum service shown earlierit outputs an XML document containing the input figures as well as the sum result:

  <?
    header("Content-Type: text/xml");
    $sum = $_GET["figure1"] + $_GET["figure2"];
    echo <<< END_OF_FILE
  <sum>
    <inputs>
      <figure id="1">{$_GET["figure1"]}</figure>
      <figure id="2">{$_GET["figure2"]}</figure>
    </inputs>
    <outputs>$sum</outputs>
  </sum>
  END_OF_FILE
  ?>

The call sequence is the same as before, but the callback function now extracts the result using responseXML. It then has a first-class DOM object and can interrogate it using the standard DOM API:

  var xhReq = createXMLHttpRequest( );
  xhReq.open("GET", "sumXML.phtml?figure1=10&figure2=20", true);
  xhReq.onreadystatechange = function( ) {
    if (xhReq.readyState != 4) { return; }
    xml = xhReq.responseXML;
    var figure1 = xml.getElementsByTagName("figure")[0].firstChild.nodeValue;
    var figure2 = xml.getElementsByTagName("figure")[1].firstChild.nodeValue;
    var sum = xml.getElementsByTagName("outputs")[0].firstChild.nodeValue;
    ...
  };
  xhReq.send(null);
});

The name "XMLHttpRequest" relates to its two primary functions: handling HTTP requests and converting XML responses. The former function is critical and the latter is best considered a bonus. There are certainly good applications for XML responsessee XML Message (Chapter 9), and XML Data Island and Browser-Side XSLT (Chapter 11)but keep in mind that XML is not a requirement of Ajax systems.

You can also upload XML from browser to server. In this case, XMLHttpRequest doesn't offer any special XML functionality; you just send the XML message as you would any other message, and with an appropriate request type (e.g., POST or PUT). To support the receiving web service, the JavaScript should generally declare the XML content type in a request header:

  xhReq.setRequestHeader('Content-Type',  "text/xml");

The XMLHttpRequest API: a summary

We've looked at how to achieve typical tasks with XMLHttpRequest, and now here's a quick summary of its properties and methods based on an Apple Developer Connection article (http://developer.apple.com/internet/webcontent/xmlhttpreq.html). The API is supported by IE5+, the Mozilla family (including all Firefox releases), and Safari 1.2+.

XMLHttpRequest has the following properties:


onreadystatechange

The callback function that's notified of state changes. 0=UNINITIALIZED, 1=LOADING, 2=LOADED, 3=INTERACTIVE, and 4=COMPLETE. (As explained earlier in "Asynchronous calls," states 1-3 are ambiguous and interpretations vary across browsers.)


readyState

The state within the request cycle.


responseText

The response from the server, as a String.


responseXML

The response from the server, as a Document Object Model, provided that the response "Content-Type" header is "text/html," and the responseText is a valid XML string.


status

HTTP response code (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) received from the server. This should normally be 200; most values indicate an error.


statusText

The HTTP response code description received from the server; e.g., "Not Found."

And these are XMLHttpRequest's methods:


abort( )

Stops the request and resets its readyState back to zero. (See "Detecting errors," earlier in this chapter.)


getAllResponseHeaders( )

Returns a string of all response headers, separated by a newline as in the original message.


getResponseHeader(headerField)

Returns the value for a particular header field.


open(requestMethod, url, asynchronousFlag, username, password)

Prepares XMLHttpRequest (See the "Solution," earlier.). Only the first two parameters are optional. username and password can be used for authentication.


send(bodyContent)

Sends the message along with specified body content (null if no body content is to be sent; e.g., for GET requests). (See the "Solution," earlier.)


setRequestHeader(headerField, headerValue)

Sets a request header. (See "Handling POSTs and other request types," earlier.)

Decisions

What kind of content will web services provide?

As mentioned in the solution, XML is not the only kind of content that XMLHttpRequest can deal with. As long as you can parse the message in JavaScript, there are various response types possible. The patterns on web services highlight a number of response types, including HTML, XML, JSON, and plain-text.

How will caching be controlled?

It's possible that an XMLHttpRequest response will be cached by the browser. Sometimes, that's what you want and sometimes it's not, so you need to exert some control over caching.

With cache control, we're talking about GET-based requests. Use GET for read-only queries and other request types for operations that affect server state. If you use POST to get information, that information usually won't be cached. Likewise, if you use GET to change state, you run the risk that the call won't always reach the server, because the browser will cache the call locally. There are other reasons to follow these this advice too; see RESTful Service (Chapter 9).

Often, you want to suppress caching in order to get the latest server information, in which case, a few techniques are relevant. Since browsers and servers vary, the standard advice is spread the net as wide as possible by combining some of these techniques:

  • You can make the URL unique by appending a timestamp (http://www.howtoadvice.com/StopCaching) (a random string, or a string from an incrementing sequence, is sometimes used too). It's a cheap trick, but surprisingly robust and portable:

      var url = "sum.phtml?figure1=5&figure2=1&timestamp=" + new Date().getTime( );
    

  • You can add a header to the request:

      xhReq.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2005 00:00:00 GMT");
    

  • In the Web Service set response headers to suppress caching (http://www.stridebird.com/articles/?showarticle=1&id=33). In PHP, for example:

      header("Expires: Sat, 1 Jan 2005 00:00:00 GMT");
      header("Last-Modified: ".gmdate( "D, d M Y H:i:s")."GMT");
      header("Cache-Control: no-cache, must-revalidate");
      header("Pragma: no-cache");
    

  • Use POST instead of GET. Requests that are of POST type will sometimes cause caching to be suppressed. However, this particular technique is not recommended because, as explained in RESTful Service, GET and POST have particular connotations and shouldn't be treated as interchangeable. In any event, it won't always work, because it's possible some resources will actually cache POST responses.

On the other hand, caching is a good thing when the service is time-consuming and unlikely to have changed recently. To encourage caching, you can reverse the above advice; e.g., set the Expires headers to a suitable time in the future. In addition, a good approach for smaller data is to cache it in the program itself, using a JavaScript data structure. Browser-Side Cache (Chapter 13) explains how.

How will you deal with errors?

The section on error detection left open the question of what to do once we discover a server timeout or nonstandard error code. There are three possible actions:


Try again

Retry a few times before giving up.


Inform the user

Tell the user what's gone wrong and what the consequences are. For instance, inform him that his data hasn't been submitted and he should try again in a few minutes.


Do nothing

Sometimes, you have the luxury of ignoring the response (or lack thereof). That might be because you're issuing low-importance Fire-and-Forget calls (http://www.ajaxian.com/archives/2005/09/ajaxian_fire_an.html), where you're uploading some data without waiting for any response.

Real-World Examples

Lace Chat

Brett Stimmerman's Lace Chat (http://www.socket7.net/lace/) is an Ajax chat application that uses XMLHttpRequest in two ways: to upload messages you type and to download all the latest messages from the server (Figure).

Lace Chat


Backbase

Backbase's Demo RSS Reader (http://www.backbase.com/demos/RSS) uses XMLHttpRequest to pull down titles of recent articles (Figure). When you click on one of those titles, a new XMLHttpRequest will pull down the entire content.

Backbase RSS Reader


Anyterm

Phil Endecott's Anyterm (http://anyterm.org/demos.html) is an Ajax terminal emulator allowing you to run telnet or SSH within the browser. It uses XMLHttpRequest Calls to upload keystrokes and download the latest screen state.

Mint

Mint (http://haveamint.com/) is a web site statistics package. Site owners include Mint JavaScript on each page, which quietly inspects the user's browser settings and uploads them using an XMLHttpRequest.

Code Example: AjaxPatterns TestAjaxCaller

The example (http://ajaxpatterns.org/xmlHttpRequestCall/) referenced in the "Solution," earlier, covers most typical XMLHttpRequest usage. In practice, many people adopt Ajax frameworks and libraries rather than calling XMLHttpRequest directly. That's the approach taken with all of the Ajax Patterns demos, which use a library called ajaxCaller.js that was developed in parallel to the demos themselves. It's a fairly basic library, but offers a simple interface for the functionality that's typically required of XMLHttpRequest. In this section, I'll introduce the library by showing a few usages within the AjaxCaller Test Demo (http://ajaxify.com/run/testAjaxCaller).

The simplest call is getting some plain-text: just specify the URL and the callback function.

  ajaxCaller.getPlainText(url, onResponse);

For all calls, the callback function always takes three arguments. The first argument is the result, either a string or a DOM object. The second is an associative array mapping header fields to header values. The third is a "calling context." Think of calling context as an optional value that travels alongside the request and the corresponding response, returned to the callback function in exactly the same form as you passed it in when the call was issued. Usually it holds information about the call; e.g., if the call was made to send off a purchase order, the calling context might contain the item that was ordered. Then, ajaxCaller will pass the context into the callback function, which can mark the item as successfully ordered. In reality, the calling context is not actually passed to and from the server; ajaxCaller keeps it locally and tracks each pending request. If this all sounds a bit complicated, check out Call Tracking (Chapter 10).

The callback function looks as follows:

  function onResponse(text, headers, callingContext) {
    // Use text (a string), headers, and callingContext
  }

And since it's only the text that's used most of the time, the function can also be declared in a simpler form.[*]

[*] Because of the way JavaScript handles function calls, the library call will still go to the function in this form, even though the call contains three arguments.

  function onResponse(text) {
    // Use text (a String)
  }

getPlainText( ) is one of four commonly used methods. The others are getXML( ), postForPlainText( ), and postForXML( ). Together, these four cover both common request types (GET and POST) and both response types (text and XML).

  ajaxCaller.getXML(url, callbackFunction);
  ajaxCaller.postForXML(url, vars, callbackFunction);
  ajaxCaller.getPlainText(url, callbackFunction, callbackContext);
  ajaxCaller.postForPlainText(url, callbackFunction, callbackContext);

There are also a number of more general methodsfor example, get( ) provides more flexible GET requests. In addition to a URL and a callback function, get( ) lets you specify some variables to be appended to the URL, a flag to indicate whether the response is XML, and the callingContext as discussed above.

  var vars = {
    flavour: "chocolate",
    topping: "nuts"
  };
  ajaxCaller.get("httpLogger.php", vars, onResponse, false, "iceCreamRequest");

There are general operations for other request types too. postVars( ) creates a CGI-style POST upload and postBody( ) creates an arbitrary-body POST upload. There are similar methods for other request types; e.g., PUT, TRACE, OPTIONS, DELETE, and HEAD.

Alternatives

This section lists all alternatives I'm aware of, some more limited than others. The more obscure techniques are included for the sake of completeness and also in the hope they might spark a few ideas.

Page refreshes

The conventional way to communicate with the server is for the browser to request an entirely new page, which is pretty extreme when you stop and think about it. It might be appropriate if the user's navigating to a completely different part of a web site, but it's overkill if you want to update a football score at the bottom of the page or upload some user input. The most familiar kind of full page refresh is the hyperlink, which causes the browser to issue a GET request, clear the current page, and output the response. The other kind of full page refresh is a form submission, which causes the browser to pass some parameters with the requestwhich will be GET, POST, or some other methodand, as with a hyperlink, replace the previous page with the new response. With web remoting, any user-interface changes are completely at the discretion of the script running inside the page. These conventional techniques are still available, but most server communication uses XMLHttpRequest Call and related technologies.

IFrame Call

IFrame Call (see later in this chapter) is the main alternative to XMLHttpRequest. Like XMLHttpRequest, it allows for remote calls using GET, POST, and other request types. But whereas XMLHttpRequest is designed specifically for web remoting, IFrame Call exploits the IFrame to do something it was never really intended to do, and the code shows it. Here's a summary of XMLHttpRequest's strengths over IFrame Calls:

  • Being designed specifically for web remoting, the XMLHttpRequest API is easier to use, especially when it comes to non-GET request types. However, this is no great advantage as it's generally recommended that you use a wrapper library to avoid working with either API. (XMLHttpRequest's API may be better, but it's not great!)

  • XMLHttpRequest offers functionality not available to IFrame Call, such as the ability to abort a call and track the call's state. This can have important performance implications (http://www.ajaxian.com/archives/2005/09/ajaxian_fire_an.html).

  • XMLHttpRequest is typically faster, especially with shorter responses (http://me.eae.net/archive/2005/04/02/xml-http-performance-and-caching/).

  • XMLHttpRequest parses XML in a simple, portable, manner; IFrame is unrelated to XML.

  • On those browsers that do support XMLHttpRequest, the API is more consistent than that of IFrame.

  • XMLHttpRequest is rapidly gaining the virtue of widespread familiarity. This not only helps other developers understand your code, but also means you benefit from tools such as those which monitor XMLHttpRequest traffic (see Traffic Sniffing [Chapter 18]).

For all these reasons, XMLHttpRequest should be the default choice. However, there are some specialized situations where IFrame Call is superior:

  • IFrame works on many older browsers that don't actually support XMLHttpRequest.

  • IFrame happens to have some specialized properties (if only by complete fluke) for browser history and bookmarkability, at least for IE, as discussed in Unique URLs (Chapter 17).

  • XMLHttpRequest on IE won't work if security measures disable ActiveX, a policy sometimes enforced in the enterprise (http://verens.com/archives/2005/08/12/ajax-in-ie-without-activex/).

  • IFrame may offer a more portable solution for HTTP Streaming as discussed in that pattern.

HTTP Streaming

HTTP Streaming (see later in this chapter) also allows for web remoting, and unlike XMLHttpRequest, the connection remains open. Functionally, the key advantage over XMLHttpRequest is that the server can continuously push new information to the browser. From a resource perspective, streaming is good insofar as there's less starting and stopping of connections, but there are serious scaleability issues as it's rarely feasible to keep open a huge amounts of connections and maintain numerous server-side scripts.

Richer Plugin

The Richer Plugin (Chapter 8) pattern discusses Java, Flash, and other plugins and extensions. These components often have permission to call the server programmatically. and in some cases, can be used as proxies available to JavaScript code.

On-Demand JavaScript

On-Demand JavaScript (see later in this chapter) describes a couple of ways to download JavaScript on the fly. One involves XMLHttpRequests (and therefore isn't foundational in itself), but the other is an alternative transport mechanism, a different technique for web remoting. It works by adding a script element to the document body, which has the effect of automatically pulling down a named JavaScript file.

Image-Cookie Call

Brent Ashley's RSLite library (http://www.ashleyit.com/rs/rslite/) is an unusual alternative based on images and cookies. An image's source property is set to the service URL, which is simply a means of invoking the service. The service writes its response into one or more cookies, which will then be accessible from the JavaScript once the call has completed.

Stylesheet Call

Another way to get at server state is to dynamically change a CSS stylesheet. Just like setting a new JavaScript or image source, you set a stylesheet's href property to point to the web service. In Julien Lamarre's demo of this technique (http://zingzoom.com/ajax/ajax_with_stylesheet.php), the web service actually outputs a stylesheet, and the response is embedded in the URL of a background-image property!

204 Response

An oldand pretty much obsoletetrick is to have the server respond with a 204 "No Content" response code. Browsers won't refresh the page when they see this code, meaning that your script can quietly submit a form to such a service with no impact on the page. However, you can only use the 204 trick for "fire-and-forget" callswhile the response may contain information embedded in the headers, there's no way for a browser script to access it.

Import XML Document

There's a technique specifically for retrieving XML documents that uses similar technology to XMLHttpRequest. Peter-Paul Koch described the technique back in 2000 (http://www.quirksmode.org/dom/importxml.html), and recently suggested it can now be written off (http://www.quirksmode.org/blog/archives/2005/01/with_httpmapsea.html).

Metaphor

An XMLHttpRequest Call is like the browser having a side conversation with the server while carrying on the main conversation with the user.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows