XHR2 & Cross-Origin Resource Sharing

Tiffany B. Brown, Opera Software

HTML5 & CSS3 LA User Group

What is XMLHttpRequest?

A method of making HTTP requests between a client and a server without the need for a page refresh. It's the X in Ajax.

How an XHR request works

XMLHttpRequest Advantages

Gmail Google Maps

View a demo

Basic XMLHttpRequest Syntax

var xhr = new XMLHttpRequest();            // Create a new object
var data = 'field1=value1&field2=value2';  // URL-encoded data string

// Define our readystatechange handler
var readyStateHandler = function(event){
   if( this.readyState == 4){
      // handle this.responseText or this.responseXML
   }
}
xhr.open('GET','/processing_script')        // open a connection
xhr.onreadystatechange = readyStateHandler  // set the handler
xhr.send(data);                             // send the data
		

XMLHttpRequest Limitations

XMLHttpRequest, Level 2

cheerleader

It’s awesome!!!1!ONE!

All of the advantages of
XMLHttpRequest + more goodies

Specification status: Working Draft

Current browser support, June 2011

IE8 IE9 FF FFMo Safari iOS Safari Chrome And. Webkit Opera OperaMo
XHR1 √ √ √ √ √ √ √ √ √ √
timeout √ √ x x x x x x x x
Cross-origin ~ ~ √ √ √ x √ √ x x
withCredentials x x √ √ √ x √ √ x x
FormData x x √ √ √ x √ √ x x
ProgressEvents x x √ √ √ x √ √ x x
overrideMimeType() x x √ √ √ √ √ √ x x

For details by version, see CanIUse.com. (Android data is for Android 3.0+)

Internet Explorer supports a proprietary XDomainRequest object instead of cross-domain XHR.

Support for XHR.upload depends on whether or not the platform supports uploads (iOS does not).

Setting and handling timeouts

var makeRequest = function(event){
    event.preventDefault();
    var xhr = new XMLHttpRequest();
    xhr.onload = function(event){ // process the response }
    xhr.ontimeout = function(event){ alert('Request timed out.'); }
    xhr.open('GET', 'bookmarks.json');
    xhr.timeout = 5000; // timeout in milliseconds. 5000ms = 5s
    xhr.send();
}
window.addEventListener('load', makeRequest, false);

View a demo

(Try Internet Explorer 8 or 9)

Using FormData objects

var xhr = new XMLHttpRequest();
var data = new FormData();
data.append('key','value');
data.append('name','Jane Doe');
xhr.open('GET','/processor');
xhr.send(data);

View a demo


(use Chrome, Firefox, or Safari 4+)

Using FormData objects to upload files

var xhr = new XMLHttpRequest();
var data = new FormData();
data.append('file',form['upload'].file[0]); // get file data*
xhr.open('GET','/processing_script');
xhr.send(data);

View a demo


(use Chrome, Firefox, or Safari 4+)

ProgressEvent interface

Applies to both the XMLHttpRequest and XMLHttpRequestUpload objects

attribute type Explanation Applies To
onloadstart loadstart When the request starts. Both
onprogress progress While loading and sending data. Both
onabort abort When the request has been aborted, whether by invoking the abort() method or navigating away from the page. Both
onerror error When the request has failed Both
onload load When the request has successfully completed. Both
ontimeout timeout When the author specified timeout has passed before the request could complete. Both
onloadend loadend When the request has completed, regardless of whether or not it was successful. Both

onreadystatechange only applies to XMLHttpRequest.

ProgressEvent interface

xhr.onprogress = function(event){ /* do something */ }
OR
var progressHandler = function(event){ /* do something */ }
xhr.addEventListener('progress',progressHandler,false);

XMLHttpRequestUpload Object

Part of the XMLHttpRequest interface.

Monitoring uploads with progress events

var xhr = new XMLHttpRequest();
var data = new FormData();
data.append('file',form['upload'].file[0]);

xhr.upload.onprogress = function(event){
     console.log( event.loaded / event.total );
}
xhr.upload.onerror = function(event){ alert('something went wrong!'); }
xhr.upload.onload = function(event){ alert('file uploaded!'); }

xhr.open('GET','/processing_script');
xhr.send(data);

View a demo

Monitoring uploads: Caveats

May be a lag between when the upload completes (when the load or loadend events are fired) and when the server returns a response.

the server is working.

Cross-Origin Requests

Look just like same-origin requests, but uses full URLs.

xhr.open('GET','http://datahost.example/data.xml');

Browser sends an additional header:


GET /data.xml HTTP/1.1
User-Agent: Fake UA String/5.0 (FakeOS; Platform 9.0) PrestoGeckoWebkit/3000 FakeBrowser/2.0
Host: datahost.example
…<snip>…
Origin: http://requestingserver.example/

Cross-Origin Resource Sharing

Response Headers Request Headers (browser set)
Access-Control-Allow-Origin Origin
Access-Control-Allow-Credentials Access-Control-Request-Method
Access-Control-Expose-Headers Access-Control-Request-Headers
Access-Control-Max-Age  
Access-Control-Allow-Methods  
Access-Control-Allow-Headers  

CORS Response Headers and what they do

Access-Control-Allow-Origin
Allow access to a particular originating domain.
Access-Control-Allow-Credentials
Permit requests with cookies
Access-Control-Expose-Headers
Indicates which headers are safe to reveal
Access-Control-Max-Age
How long the results of a "preflighted" request can be cached.
Access-Control-Allow-Methods
Specifies which HTTP request methods — ex: GET, POST, PUT, HEAD, DELETE — are allowed.
Access-Control-Allow-Headers
Which headers will be accepted (for example: X-CustomHeader).

Cross-Origin Resource Sharing

Minimum header necessary for a cross-origin request to succeed.

HTTP/1.1 200 OK
Date: Fri, 27 May 2011 21:27:14 GMT
Server: Apache/2
Last-Modified: Fri, 27 May 2011 19:29:00 GMT
Accept-Ranges: bytes
Content-Length: 1830
Keep-Alive: timeout=15, max=97
Connection: Keep-Alive
Content-Type:  application/xml; charset=UTF-8
Access-Control-Allow-Origin: *

View a demo

Setting CORS Headers

Varies by server type and programming language, but here is a roundup:

Apache Foundation Logo. Trademarked by the Apache Software FoundationApache
Header set Access-Control-Allow-Origin "http://foo.example"
LightHttpd logoLighttpd
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "http://foo.example" )
Nginx logoNginx
add_header Access-Control-Allow-Origin http://foo.example
IIS
<add name="Access-Control-Allow-Origin" value="*" />
Python logoPython
print "Access-Control-Allow-Origin: http://foo.example"
PHP logoPHP
header("Access-Control-Allow-Origin: http://foo.example");

Handling responses

Handling responses: An example

var makeRequest = function(event){
    event.preventDefault();
    var xhr = new XMLHttpRequest();
    xhr.onload = function(event){
    	var xhr = event.target;
    	console.log(xhr.response); // the response.
    }
    xhr.open('GET', 'bookmarks.json');
    xhr.send();
}
window.addEventListener('load', makeRequest, false);

Handling responses: overrideMimeType()

Enforce a MIME type for the response. In this case, the browser will treat data.xml as XML despite its text/html content type.

var xhr = new XMLHttpRequest();
xhr.open('GET', 'data.xml');
xhr.overrideMimeType('application/xml')
xhr.send();
Date: Sat, 04 Jun 2011 03:11:31 GMT
Server: Apache/2.2.17
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8

View a demo

Learn more

For more on XMLHttpRequest, Level 2 and associated technologies, see: