Using XMLHttpRequest

  • Revision slug: DOM/XMLHttpRequest/Using_XMLHttpRequest
  • Revision title: Using XMLHttpRequest
  • Revision id: 2238
  • Created:
  • Creator: Sheppy
  • Is current revision? No
  • Comment 275 words added, 257 words removed

Revision Content

XMLHttpRequest makes sending HTTP requests very easy.  You simply create an instance of the object, open a URL, and send the request.  The HTTP status of the result, as well as the result's contents, are available in the request object when the transation is completed.

Synchronous and asynchronous requests

XMLHttpRequest supports both synchronous and asynchronous communications.

Example: Synchronous request

This example demonstrates how to make a simple synchronous request.

Note: As a general rule, you should avoid using synchronous XMLHttpRequests because they block the application's user interface.
var req = new XMLHttpRequest();
req.open('GET', 'http://www.mozilla.org/', false); 
req.send(null);
if(req.status == 200)
  dump(req.responseText);

Line 1 instantiates the XMLHttpRequest object.  Line 2 then opens a new request, specifying that a GET request will be used to fetch the Mozilla.org home page, and that the operation should not be asynchronous.

Line 3 sends the request.  The null parameter indicates that no body content is needed for the GET request.

Line 4 checks the status code after the transaction is completed.  If the result is 200 -- HTTP's "OK" result -- the document's text content is output to the console.

Example: Non-HTTP synchronous request

Despite its name, XMLHttpRequest can be used for non-HTTP requests.  This example shows how to use it to fetch a file from the local file system.

var req = new XMLHttpRequest();
req.open('GET', 'file:///home/user/file.json', false); 
req.send(null);
if(req.status == 0)
  dump(req.responseText);

The key thing to note here is that the result status is being compared to 0 for success instead of 200.  This is because the file and ftp schemes do not use HTTP result codes.

Example: Asynchronous request

If you use XMLHttpRequest from an extension, you should use it asynchronously.  In this case, you receive a callback when the data has been received, which lets the browser continue to work as normal while your request is being handled.

var req = new XMLHttpRequest();
req.open('GET', 'http://www.mozilla.org/', true);
req.onreadystatechange = function (aEvt) {
  if (req.readyState == 4) {
     if(req.status == 200)
      dump(req.responseText);
     else
      dump("Error loading page\n");
  }
};
req.send(null); 

Line 2 specifies true for its third parameter to indicate that the request should be handled asynchronously.

Line 3 creates an event handler function object and assigns it to the request's onreadystatechange attribute.  This handler looks at the request's readyState to see if the transaction is complete in line 4, and if it is, and the HTTP status is 200, dumps the received content.  If an error occurred, an error message is displayed.

Line 11 actually initiates the request.  The callback routine is called whenever the state of the request changes.

Handling binary data

Although XMLHttpRequest is most commonly used to send and receive textual data, it can be used to send and receive binary content.

Receiving binary data

The load_binary_resource() function shown below loads binary data from the specified URL, returning it to the caller.

function load_binary_resource(url) {
  var req = new XMLHttpRequest();
  req.open('GET', url, false);
  //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
  req.overrideMimeType('text/plain; charset=x-user-defined');
  req.send(null);
  if (req.status != 200) return '';
  return req.responseText;
}

The magic happens in line 5, which overrides the MIME type, forcing Firefox to treat it as plain text, using a user-defined character set.  This tells Firefox not to parse it, and to let the bytes pass through unprocessed.

var filestream = load_binary_resource(url);
var abyte = filestream.charCodeAt(x) & 0xff; // throw away high-order byte (f7)

The example above fetches the byte at offset x within the loaded binary data.  The valid range for x is from 0 to filestream.length-1.

See downloading binary streams with XMLHttpRequest for a detailed explanation. See also downloading files.

Sending binary data

This example transmits binary content asynchronously, using the POST method.

var req = new XMLHttpRequest();
req.open("POST", url, true);
// set headers and mime-type appropriately
req.setRequestHeader("Content-Length", 741);
req.sendAsBinary(aBody);

Line 4 sets the Content-Length header to 741, indicating that the data is 741 bytes long.  Obviously you need to change this value based on the actual size of the data being sent.

Line 5 uses the sendAsBinary() method to initiate the request.

You can also send binary content by passing an instance of the {{ interface("nsIFileInputStream") }} to send().  In that case, you don't have to set the Content-Length header yourself, as the information is fetched from the stream automatically:

// Make a stream from a file.
var stream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                       .createInstance(Components.interfaces.nsIFileInputStream);
stream.init(file, 0x04 | 0x08, 0644, 0x04); // file is an nsIFile instance   

// Try to determine the MIME type of the file
var mimeType = "text/plain";
try {
  var mimeService = Components.classes["@mozilla.org/mime;1"]
          .createInstance(Components.interfaces.nsIMIMEService);
  mimeType = mimeService.getTypeFromFile(file); // file is an nsIFile instance
}
catch(e) { /* eat it; just use text/plain */ }

// Send    
var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
                    .createInstance(Components.interfaces.nsIXMLHttpRequest);
req.open('PUT', url, false); /* synchronous! */
req.setRequestHeader('Content-Type', mimeType);
req.send(stream);

Monitoring progress

XMLHttpRequest provides the ability to listen to various events that can occur while the request is being processed. This includes periodic progress notifications, error notifications, and so forth.

In Firefox 3.1 and later

{{ fx_minversion_note("3") }}

var req = new XMLHttpRequest();

req.addEventListener("progress", updateProgress, false);
req.addEventListener("load", transferComplete, false);
req.addEventListener("error", transferFailed, false);
req.addEventListener("abort", transferCanceled, false);

req.open();

...

function updateProgress(evt) {
  if (evt.lengthComputable) {
    var percentComplete = evt.loaded / evt.total;
    ...
  } else {
    // Unable to compute progress information since the total size is unknown
  }}
}

function transferComplete(evt) {
  alert("The transfer is complete.");
}

function transferFailed(evt) {
  alert("An error occurred while transferring the file.");
}

function transferCanceled(evt) {
  alert("The transfer has been canceled by the user.");
}

Lines 3-6 add event listeners for the various events that are sent while performing a data transfer using XMLHttpRequest.  See {{ interface("nsIDOMProgressEvent") }} and {{template.interface("nsIXMLHttpRequestEventTarget") for details on these events.

The progress event handler, specified by the updateProgress() function in this example, receives the total number of bytes to transfer as well as the number of bytes transferred so far in the event's total and loaded fields.  However, if the lengthComputable field is true, the total length is not known and will be zero.

In Firefox 3 and earlier

If, for example, you wish to provide progress information to the user while the document is being received, you can use code like this:

function onProgress(e) {
  var percentComplete = (e.position / e.totalSize)*100;
  // ...
}

function onError(e) {
  alert("Error " + e.target.status + " occurred while receiving the document.");
}

function onLoad(e) {
  // ...
}
// ...
var req = new XMLHttpRequest();
req.onprogress = onProgress;
req.open("GET", url, true);
req.onload = onLoad;
req.onerror = onError;
req.send(null);

The onprogress event's attributes, position and totalSize, indicate the current number of bytes received and the total number of bytes expected, respectively.

All of these events have their target attribute set to the XMLHttpRequest they correspond to.

Note: Firefox 3 properly ensures that the values of the target, currentTarget, and this fields of the event object are set to reference the correct objects when calling event handlers for XML documents represented by XMLDocument. See {{ Bug("198595") }} for details.

Bypassing the cache

Normally, XMLHttpRequest tries to retrieve content from the cache, if it's available.  To bypass this, do the following:

 var req = new XMLHttpRequest();
req.open('GET', url, false);
req.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
req.send(null);
Note: This approach will only work in Gecko-based software, as the channel attribute is Gecko-specific.

An alternate, cross-browser compatible approach is to append a timestamp to the URL, being sure to include a "?" or "&" as appropriate.  For example:

http://foo.com/bar.html

becomes

http://foo.com/bar.html?12345

and

http://foo.com/bar.html?foobar=baz

becomes

http://foo.com/bar.html?foobar=baz&12345

Since the local cache is indexed by URL, this causes every request to be unique, thereby bypassing the cache.

You can automatically adjust URLs using the following code:

var req = new XMLHttpRequest();
req.open("GET", url += (url.match(/\?/) == null ? "?" : "&") + (new Date()).getTime(), false);
req.send(null);

 

Downloading JSON and JavaScript from extensions

For security reasons, extensions should never use eval() to parse JSON or JavaScript code downloaded from the web.  See Downloading JSON and JavaScript in extensions for details.

Using XMLHttpRequest from XPCOM components

Instantiating XMLHttpRequest from an XPCOM component works a little differently; it can't be instantiated using the XMLHttpRequest() constructor from a JavaScript XPCOM component. The constructor is not defined inside components and the code results in an error. You'll need to create and use it using a different syntax.

Instead of this:

var req = new XMLHttpRequest();
req.onprogress = onProgress;
req.onload = onLoad;
req.onerror = onError;
req.open("GET", url, true);
req.send(null);

Do this:

var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
                    .createInstance(Components.interfaces.nsIXMLHttpRequest);
req.onprogress = onProgress;
req.onload = onLoad;
req.onerror = onError;
req.open("GET", url, true);
req.send(null);

For C++ code you would need to QueryInterface the component to an nsIEventTarget in order to add event listeners, but chances are in C++ using a channel directly would be better.

See also

  1. MDC AJAX introduction
  2. XMLHttpRequest - REST and the Rich User Experience
  3. XULPlanet documentation
  4. Microsoft documentation
  5. Apple developers' reference
  6. "Using the XMLHttpRequest Object" (jibbering.com)
  7. The XMLHttpRequest Object: W3C Working Draft

Revision Source

<p><code>XMLHttpRequest</code> makes sending HTTP requests very easy.  You simply create an instance of the object, open a URL, and send the request.  The HTTP status of the result, as well as the result's contents, are available in the request object when the transation is completed.</p>
<h2>Synchronous and asynchronous requests</h2>
<p><code>XMLHttpRequest</code> supports both synchronous and asynchronous communications.</p>
<h3>Example: Synchronous request</h3>
<p>This example demonstrates how to make a simple synchronous request.</p>
<div class="note"><strong>Note:</strong> As a general rule, you should avoid using synchronous <code>XMLHttpRequests</code> because they block the application's user interface.</div>
<pre class="brush: js">var req = new XMLHttpRequest();
req.open('GET', 'http://www.mozilla.org/', false); 
req.send(null);
if(req.status == 200)
  dump(req.responseText);</pre>
<p>Line 1 instantiates the <code>XMLHttpRequest</code> object.  Line 2 then opens a new request, specifying that a <code>GET</code> request will be used to fetch the Mozilla.org home page, and that the operation should not be asynchronous.</p>
<p>Line 3 sends the request.  The <code>null</code> parameter indicates that no body content is needed for the <code>GET</code> request.</p>
<p>Line 4 checks the status code after the transaction is completed.  If the result is 200 -- HTTP's "OK" result -- the document's text content is output to the console.</p>
<h3>Example: Non-HTTP synchronous request</h3>
<p>Despite its name, <code>XMLHttpRequest</code> can be used for non-HTTP requests.  This example shows how to use it to fetch a file from the local file system.</p>
<pre class="brush: js">var req = new XMLHttpRequest();
req.open('GET', 'file:///home/user/file.json', false); 
req.send(null);
if(req.status == 0)
  dump(req.responseText);</pre>
<p>The key thing to note here is that the result status is being compared to 0 for success instead of 200.  This is because the <code>file</code> and <code>ftp</code> schemes do not use HTTP result codes.</p>
<h3>Example: Asynchronous request</h3>
<p>If you use <code>XMLHttpRequest</code> from an extension, you should use it asynchronously.  In this case, you receive a callback when the data has been received, which lets the browser continue to work as normal while your request is being handled.</p>
<pre class="brush: js">var req = new XMLHttpRequest();
req.open('GET', 'http://www.mozilla.org/', true);
req.onreadystatechange = function (aEvt) {
  if (req.readyState == 4) {
     if(req.status == 200)
      dump(req.responseText);
     else
      dump("Error loading page\n");
  }
};
req.send(null); </pre>
<p>Line 2 specifies <code>true</code> for its third parameter to indicate that the request should be handled asynchronously.</p>
<p>Line 3 creates an event handler function object and assigns it to the request's <code>onreadystatechange</code> attribute.  This handler looks at the request's <code>readyState</code> to see if the transaction is complete in line 4, and if it is, and the HTTP status is 200, dumps the received content.  If an error occurred, an error message is displayed.</p>
<p>Line 11 actually initiates the request.  The callback routine is called whenever the state of the request changes.</p>
<h2>Handling binary data</h2>
<p>Although <code>XMLHttpRequest</code> is most commonly used to send and receive textual data, it can be used to send and receive binary content.</p>
<h3>Receiving binary data</h3>
<p>The <code>load_binary_resource()</code> function shown below loads binary data from the specified URL, returning it to the caller.</p>
<pre class="brush: js">function load_binary_resource(url) {
  var req = new XMLHttpRequest();
  req.open('GET', url, false);
  //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
  req.overrideMimeType('text/plain; charset=x-user-defined');
  req.send(null);
  if (req.status != 200) return '';
  return req.responseText;
}
</pre>
<p>The magic happens in line 5, which overrides the MIME type, forcing Firefox to treat it as plain text, using a user-defined character set.  This tells Firefox not to parse it, and to let the bytes pass through unprocessed.</p>
<pre class="brush: js">var filestream = load_binary_resource(url);
var abyte = filestream.charCodeAt(x) &amp; 0xff; // throw away high-order byte (f7)</pre>
<p>The example above fetches the byte at offset <code>x</code> within the loaded binary data.  The valid range for <code>x</code> is from 0 to <code>filestream.length-1</code>.</p>
<p>See <a class="external" href="http://mgran.blogspot.com/2006/08/downloading-binary-streams-with.html">downloading binary streams with XMLHttpRequest</a> for a detailed explanation. See also <a href="/en/Code_snippets/Downloading_Files" title="en/Code_snippets/Downloading_Files">downloading files</a>.</p>
<h3>Sending binary data</h3>
<p>This example transmits binary content asynchronously, using the <code>POST</code> method.</p>
<pre class="brush: js">var req = new XMLHttpRequest();
req.open("POST", url, true);
// set headers and mime-type appropriately
req.setRequestHeader("Content-Length", 741);
req.sendAsBinary(aBody);</pre>
<p>Line 4 sets the Content-Length header to 741, indicating that the data is 741 bytes long.  Obviously you need to change this value based on the actual size of the data being sent.</p>
<p>Line 5 uses the <code>sendAsBinary()</code> method to initiate the request.</p>
<p>You can also send binary content by passing an instance of the {{ interface("nsIFileInputStream") }} to send().  In that case, you don't have to set the Content-Length header yourself, as the information is fetched from the stream automatically:</p>
<pre class="brush: js">// Make a stream from a file.
var stream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                       .createInstance(Components.interfaces.nsIFileInputStream);
stream.init(file, 0x04 | 0x08, 0644, 0x04); // file is an nsIFile instance   

// Try to determine the MIME type of the file
var mimeType = "text/plain";
try {
  var mimeService = Components.classes["@mozilla.org/mime;1"]
          .createInstance(Components.interfaces.nsIMIMEService);
  mimeType = mimeService.getTypeFromFile(file); // file is an nsIFile instance
}
catch(e) { /* eat it; just use text/plain */ }

// Send    
var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
                    .createInstance(Components.interfaces.nsIXMLHttpRequest);
req.open('PUT', url, false); /* synchronous! */
req.setRequestHeader('Content-Type', mimeType);
req.send(stream);
</pre>
<h2>Monitoring progress</h2>
<p><code>XMLHttpRequest</code> provides the ability to listen to various events that can occur while the request is being processed. This includes periodic progress notifications, error notifications, and so forth.</p>
<h3>In Firefox 3.1 and later</h3>
<p>{{ fx_minversion_note("3") }}</p>
<pre class="brush: js">var req = new XMLHttpRequest();

req.addEventListener("progress", updateProgress, false);
req.addEventListener("load", transferComplete, false);
req.addEventListener("error", transferFailed, false);
req.addEventListener("abort", transferCanceled, false);

req.open();

...

function updateProgress(evt) {
  if (evt.lengthComputable) {
    var percentComplete = evt.loaded / evt.total;
    ...
  } else {
    // Unable to compute progress information since the total size is unknown
  }}
}

function transferComplete(evt) {
  alert("The transfer is complete.");
}

function transferFailed(evt) {
  alert("An error occurred while transferring the file.");
}

function transferCanceled(evt) {
  alert("The transfer has been canceled by the user.");
}
</pre>
<p>Lines 3-6 add event listeners for the various events that are sent while performing a data transfer using XMLHttpRequest.  See {{ interface("nsIDOMProgressEvent") }} and {{template.interface("nsIXMLHttpRequestEventTarget") for details on these events.</p>
<p>The progress event handler, specified by the <code>updateProgress()</code> function in this example, receives the total number of bytes to transfer as well as the number of bytes transferred so far in the event's <code>total</code> and <code>loaded</code> fields.  However, if the <code>lengthComputable</code> field is true, the total length is not known and will be zero.</p>
<h3>In Firefox 3 and earlier</h3>
<p>If, for example, you wish to provide progress information to the user while the document is being received, you can use code like this:</p>
<pre class="brush: js">function onProgress(e) {
  var percentComplete = (e.position / e.totalSize)*100;
  // ...
}

function onError(e) {
  alert("Error " + e.target.status + " occurred while receiving the document.");
}

function onLoad(e) {
  // ...
}
// ...
var req = new XMLHttpRequest();
req.onprogress = onProgress;
req.open("GET", url, true);
req.onload = onLoad;
req.onerror = onError;
req.send(null);
</pre>
<p>The <code>onprogress</code> event's attributes, <code>position</code> and <code>totalSize</code>, indicate the current number of bytes received and the total number of bytes expected, respectively.</p>
<p>All of these events have their <code>target</code> attribute set to the <code>XMLHttpRequest</code> they correspond to.</p>
<div class="note"><strong>Note:</strong> <a href="/en/Firefox_3_for_developers" title="en/Firefox_3_for_developers">Firefox 3</a> properly ensures that the values of the <code>target</code>, <code>currentTarget</code>, and <code>this</code> fields of the event object are set to reference the correct objects when calling event handlers for XML documents represented by <code>XMLDocument</code>. See {{ Bug("198595") }} for details.</div>
<h2>Bypassing the cache</h2>
<p>Normally, XMLHttpRequest tries to retrieve content from the cache, if it's available.  To bypass this, do the following:</p>
<pre class="brush: js"> var req = new XMLHttpRequest();
req.open('GET', url, false);
<strong>req.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;</strong>
req.send(null);</pre>
<div class="note"><strong>Note:</strong> This approach will only work in Gecko-based software, as the channel attribute is Gecko-specific.</div>
<p>An alternate, cross-browser compatible approach is to append a timestamp to the URL, being sure to include a "?" or "&amp;" as appropriate.  For example:</p>
<pre>http://foo.com/bar.html</pre>
<p>becomes</p>
<pre>http://foo.com/bar.html?12345</pre>
<p>and</p>
<pre>http://foo.com/bar.html?foobar=baz</pre>
<p>becomes</p>
<pre>http://foo.com/bar.html?foobar=baz&amp;12345</pre>
<p>Since the local cache is indexed by URL, this causes every request to be unique, thereby bypassing the cache.</p>
<p>You can automatically adjust URLs using the following code:</p>
<pre class="brush: js">var req = new XMLHttpRequest();
req.open("GET", url += (url.match(/\?/) == null ? "?" : "&amp;") + (new Date()).getTime(), false);
req.send(null);
</pre>
<p> </p>
<h2>Downloading JSON and JavaScript from extensions</h2>
<p>For security reasons, extensions should never use <code><a href="/en/Core_JavaScript_1.5_Reference/Global_Functions/eval" title="en/Core_JavaScript_1.5_Reference/Global_Functions/eval">eval()</a></code> to parse JSON or JavaScript code downloaded from the web.  See <a href="/en/Downloading_JSON_and_JavaScript_in_extensions" title="en/Downloading_JSON_and_JavaScript_in_extensions">Downloading JSON and JavaScript in extensions</a> for details.</p>
<h2>Using XMLHttpRequest from XPCOM components</h2>
<p>Instantiating <code>XMLHttpRequest</code> from an XPCOM component works a little differently; it can't be instantiated using the <code>XMLHttpRequest()</code> constructor from a JavaScript XPCOM component. The constructor is not defined inside components and the code results in an error. You'll need to create and use it using a different syntax.</p>
<p>Instead of this:</p>
<pre class="brush: js">var req = new XMLHttpRequest();
req.onprogress = onProgress;
req.onload = onLoad;
req.onerror = onError;
req.open("GET", url, true);
req.send(null);
</pre>
<p>Do this:</p>
<pre class="brush: js">var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
                    .createInstance(Components.interfaces.nsIXMLHttpRequest);
req.onprogress = onProgress;
req.onload = onLoad;
req.onerror = onError;
req.open("GET", url, true);
req.send(null);
</pre>
<p>For C++ code you would need to <code>QueryInterface</code> the component to an <code>nsIEventTarget</code> in order to add event listeners, but chances are in C++ using a channel directly would be better.</p>
<h2>See also</h2>
<ol> <li><a href="/en/AJAX/Getting_Started" title="en/AJAX/Getting_Started">MDC AJAX introduction</a></li> <li><a class="external" href="http://www.peej.co.uk/articles/rich-user-experience.html">XMLHttpRequest - REST and the Rich User Experience</a></li> <li><a class="external" href="http://www.xulplanet.com/references/objref/XMLHttpRequest.html">XULPlanet documentation</a></li> <li><a class="external" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/xmlsdk/html/xmobjxmlhttprequest.asp">Microsoft documentation</a></li> <li><a class="external" href="http://developer.apple.com/internet/webcontent/xmlhttpreq.html">Apple developers' reference</a></li> <li><a class="external" href="http://jibbering.com/2002/4/httprequest.html">"Using the XMLHttpRequest Object" (jibbering.com)</a></li> <li><a class="external" href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object: W3C Working Draft</a></li>
</ol>
Revert to this revision