Introduction to XMLHttpRequest Level 2
2015-02-03 12:29
253 查看
Introduction
What's
new in XMLHttpRequest
Setting
and handling timeouts
Requesting
data from another domain
An
overview of cross-origin resource sharing
The
origin header
Access-Control-Allow-Origin
response header
Sending
user credentials with requests
Sending
data as key-value pairs with
Using
an HTML form
Monitoring
data transfers with progress events
Monitoring
uploads
Enforcing
a response MIME type
Enforcing
a response type
Learn
more
to make HTTP and HTTPS requests
and modify the current page without reloading it. Submitting forms and retrieving additional content are two common uses.
Early forms of
requests to text, HTML and XML. Sending variables and values required syntax — URL-encoded strings — that were messy to read and write.
Early XHR was also subject to a same-origin
policy, that made cross-domain requests more difficult. You couldn't, for instance, share data between http://foo.example/ andhttp://bar.example/ without
an intermediary such as Flash-enabled
XHR or a proxy
server. Sharing data between subdomains (e.g. http://foo.example and http://www.foo.example) required setting a
scripts on both origins. Doing so, however carriedsecurity
risks.
Uploading files with earlier implementations of XHR? No can do. Instead we had to rely on workarounds such as SWFUpload,
which required a plugin. Or we had to use a hidden
which lacked client-side progress events.
We can do better than that, and we have. This article looks at improvements
to XMLHttpRequest, and the state of support in Opera 12.
With changes to the XMLHttpRequest specification and improved browser support, you can now:
Set request timeouts
Better manage data with
Transfer binary data
Monitor
the progress of data transfers
Make safer cross-origin
requests
Override the media type and encoding of responses.
Sometimes requests are slow to complete. This may be due to high latency in the network or a slow server response. Slow requests will make your application appear unresponsive, which is not good for the user experience.
XHR now provides a way for handling this problem: request timeouts. Using the
we can specify how many milliseconds to wait before the application does something else. In the example that follows, we've set a three second (3000 millisecond) timeout:
If more than three seconds pass before response data is received, we'll notify the user that the request is taking too long. Then we'll initiate a new request with a longer timeout limit (view
an XHR timeouts demo). Resetting the timeout limit within the
handler isn't strictly necessary. We've done so for this URL to avoid a loop since its response will always exceed the initial timeout value.
To date, Chrome and Safari do not support XHR timeouts. Opera, Firefox, and Internet Explorer 10 do. Internet Explorer 8 and 9 also support timeouts on the
One limitation of early XHR was the same-origin
policy. Both the requesting document and the requested document had to originate from the same scheme, host, and port. A request from http://www.foo.example to http://www.foo.example:5050 —
a cross-port request — would cause a security exception (except in older versions of Internet Explorer, which allowed cross-port requests).
Now XMLHttpRequest supports cross-origin requests, provided cross-origin
resource sharing(CORS) is enabled.
Internet Explorer 8 and 9 do not support cross-domain
though IE10 does. Instead, Internet Explorer 8 and 9 use the
which works similarly.
Cross-origin requests look just like same-origin requests, but use a full URL instead of a relative one:
The critical difference is that the target URL must permit access from the requesting origin by sending an
header.
For an in-depth look at CORS, read DOM
access control using cross-origin resource sharing. Here we'll just cover two headers: the
header, and the
header.
When making a cross-origin XHR request, Opera and other browsers will automatically include an
— see below for an example:
What's
new in XMLHttpRequest
Setting
and handling timeouts
Requesting
data from another domain
An
overview of cross-origin resource sharing
The
origin header
Access-Control-Allow-Origin
response header
Sending
user credentials with requests
Sending
data as key-value pairs with
FormDataobjects
Using
FormDatawith
an HTML form
Monitoring
data transfers with progress events
Monitoring
uploads
Enforcing
a response MIME type
Enforcing
a response type
Learn
more
Introduction
XMLHttpRequestallows developers
to make HTTP and HTTPS requests
and modify the current page without reloading it. Submitting forms and retrieving additional content are two common uses.
Early forms of
XMLHttpRequestlimited
requests to text, HTML and XML. Sending variables and values required syntax — URL-encoded strings — that were messy to read and write.
Early XHR was also subject to a same-origin
policy, that made cross-domain requests more difficult. You couldn't, for instance, share data between http://foo.example/ andhttp://bar.example/ without
an intermediary such as Flash-enabled
XHR or a proxy
server. Sharing data between subdomains (e.g. http://foo.example and http://www.foo.example) required setting a
document.domainin
scripts on both origins. Doing so, however carriedsecurity
risks.
Uploading files with earlier implementations of XHR? No can do. Instead we had to rely on workarounds such as SWFUpload,
which required a plugin. Or we had to use a hidden
iframe,
which lacked client-side progress events.
We can do better than that, and we have. This article looks at improvements
to XMLHttpRequest, and the state of support in Opera 12.
What's new in XMLHttpRequest
With changes to the XMLHttpRequest specification and improved browser support, you can now:Set request timeouts
Better manage data with
FormDataobjects
Transfer binary data
Monitor
the progress of data transfers
Make safer cross-origin
requests
Override the media type and encoding of responses.
Setting and handling timeouts
Sometimes requests are slow to complete. This may be due to high latency in the network or a slow server response. Slow requests will make your application appear unresponsive, which is not good for the user experience.XHR now provides a way for handling this problem: request timeouts. Using the
timeoutattribute,
we can specify how many milliseconds to wait before the application does something else. In the example that follows, we've set a three second (3000 millisecond) timeout:
function makeRequest() { var url = 'data.json'; var onLoadHandler = function(event){ // Parse the JSON and build a list. } var onTimeOutHandler = function(event){ var content = document.getElementById('content'), p = document.createElement('p'), msg = document.createTextNode('Just a little bit longer!'); p.appendChild(msg); content.appendChild(p); // Restarts the request. event.target.open('GET',url); // Optionally, set a longer timeout to override the original. event.target.timeout = 6000; event.target.send(); } var xhr = new XMLHttpRequest(); xhr.open('GET',url); xhr.timeout = 3000; xhr.onload = onLoadHandler; xhr.ontimeout = onTimeOutHandler; xhr.send(); } window.addEventListener('DOMContentLoaded', makeRequest, false);
If more than three seconds pass before response data is received, we'll notify the user that the request is taking too long. Then we'll initiate a new request with a longer timeout limit (view
an XHR timeouts demo). Resetting the timeout limit within the
timeoutevent
handler isn't strictly necessary. We've done so for this URL to avoid a loop since its response will always exceed the initial timeout value.
To date, Chrome and Safari do not support XHR timeouts. Opera, Firefox, and Internet Explorer 10 do. Internet Explorer 8 and 9 also support timeouts on the
XDomainRequestobject.
Requesting data from another domain
One limitation of early XHR was the same-originpolicy. Both the requesting document and the requested document had to originate from the same scheme, host, and port. A request from http://www.foo.example to http://www.foo.example:5050 —
a cross-port request — would cause a security exception (except in older versions of Internet Explorer, which allowed cross-port requests).
Now XMLHttpRequest supports cross-origin requests, provided cross-origin
resource sharing(CORS) is enabled.
Internet Explorer 8 and 9 do not support cross-domain
XMLHttpRequest,
though IE10 does. Instead, Internet Explorer 8 and 9 use the
XDomainRequestobject,
which works similarly.
Cross-origin requests look just like same-origin requests, but use a full URL instead of a relative one:
var xhr = new XMLHttpRequest(); var onLoadHandler = function(event) { /* do something with the response */ } xhr.open('GET','http://other.server/and/path/to/script'); xhr.onload = onLoadHandler; xhr.send();
The critical difference is that the target URL must permit access from the requesting origin by sending an
Access-Control-Allow-Originresponse
header.
An overview of cross-origin resource sharing
For an in-depth look at CORS, read DOMaccess control using cross-origin resource sharing. Here we'll just cover two headers: the
Originrequest
header, and the
Access-Control-Allow-Originresponse
header.
The origin header
When making a cross-origin XHR request, Opera and other browsers will automatically include an Originheader
— see below for an example:
GET /data.xml HTTP/1.1 User-Agent: Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.10.289 Version/12.00 Host: datahost.example Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1 Accept-Language: en,en-US Accept-Encoding: gzip, deflate Referer: http://requestingserver.example/path/to/askingdocument.html Connection: Keep-Alive Origin: http://requestingserver.example[/code]Origintypically contains the
scheme, host name, and port of the requesting document. It is not an author request header, meaning it can’t be set or modified using thesetRequestHeader()method;
user agents will ignore it if you try. Its entire purpose is to inform the target server about the origins of this request. Bear in mind that there is no trailing slash.The
TheAccess-Control-Allow-Originresponse headerAccess-Control-Allow-Originheader
is sent by the target server in response to a cross-origin request. It tells the user agent whether access should be granted to the requesting origin. DOM
operations involving a cross-origin XHR request will not be completed unless the requested URL allows it. An example follows: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: *
In this case, we’re using a wild card (*) to allow access from any origin.
This is fine if you are offering a public-facing API.
For most other uses, you'll want to set a more specific origin value.There may be occasions when you will want to send cookie data along with your cross-domain request. That’s where the
Sending user credentials with cross-domain requestswithCredentialsattribute
comes in handy. It is a boolean attribute that alerts the browser that it should send user
credentials along with the request. By default, the credentials flag isfalse.
In the code below, let’s assume that our request is going from http://foo.example to http://other.server:var xhr = new XMLHttpRequest(); var onLoadHandler = function(event) { doSomething(event.target.responseText); } xhr.open('GET','http://other.server/and/path/to/script'); xhr.withCredentials = true; xhr.onload = onLoadHandler; xhr.send();
In our XHR
credentials demo, we are using a counter cookie to track the number of visits. If you examine the request and response data (you can do this with Dragonfly' Network panel), you will see that the browser is sending request cookies and receiving response
cookies. Our server-side script will return text containing the new visit count, and update the value of the cookie.
Keep the following in mind when making requests with credentials:withCredentialsis only necessary
for cross-origin requests.
TheAccess-Control-Allow-Originheader
of the requested URI can not contain a wildcard (*).
TheAccess-Control-Allow-Credentialsheader
of the requested URI must be set totrue.
Only a subset of response headers will be available togetAllRequestHeaders(),
unless theAccess-Control-Expose-Headersheader
has been set.
Same-origin requests will ignore the credentials flag. A wildcardAccess-Control-Allow-Originheader
value will cause an exception. If the value ofAccess-Control-Allow-Credentialsisfalse, cookies
will still be sent and received, however they will not be available to the DOM.In previous implementations, data sent via XHR had to be submitted as a string, either using URL-encoding, or JSON (with
Sending data as key-value pairs withFormDataobjectsJSON.stringify()).
The example below uses URL-encoding:var msg = 'field1=foo&field2=bar'; var xhr = new XMLHttpRequest(); xhr.open('POST','/processing_script'); xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhr.send(msg);
Now, we can use theFormDataobject,
and its ordered, key-value pairs. The syntax offers three benefits:
Scripts are more readable
Data is sent in key-value pairs, as with regular HTML formsFormDataobjects are sent withmultipart/form-dataencoding,
making it possible to use XHR for sending binary data.
If you've ever worked withURLVariablesin
ActionScript 3.0,FormDatawill
feel familiar. First create aFormDataobject,
then add data using theappend()method.
Theappend()method requires
two parameters:keyandvalue.
EachFormDatakey becomes a
variable name available to your server-side script. An example follows:var xhr = new XMLHttpRequest(); var dataToSend = new FormData(); // create a new FormData object xhr.open('POST','/processing_script'); dataToSend.append('name','Joseph Q. Public'); // add data to the object dataToSend.append('age','52'); dataToSend.append('hobby','knitting'); xhr.send(dataToSend); // send the object
We’ve passed theFormDataobject
as the argument of thesend()method:xhr.send(dataToSend).
We did not set aContent-Typeheader
on ourXMLHttpRequestobject.
Let's take a look at the request headers sent by Opera:POST /processing_script HTTP/1.1 User-Agent: Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8) Presto/2.10.289 Version/12.00 Host: datahost.example Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1 Accept-Language: en,en-US Accept-Encoding: gzip, deflate Expect: 100-continue Referer: http://datahost.example/upload/ Connection: Keep-Alive Content-Length: 4281507 Content-Type: multipart/form-data; boundary=----------J2GMKTyAkjRjNgFzKv3VBJ
Opera has added theContent-Typeheader
for us because we are using aFormDataobject.
Other browsers do the same.You can also send the values from a form with
UsingFormDatawith an HTML formFormData,
by passing the form to theFormDataobject
as shown below (view
an XHR FormData demo).var submitHandler = function(event) { var dataToSend = new FormData(event.target), xhr = new XMLHttpRequest(); xhr.open('POST','/processing_script'); xhr.send(dataToSend); } var form = document.getElementById('myform'); form.addEventListener('submit',submitHandler,false);FormDatais still untrusted
data. Treat input from aFormDataobject
as you would any other kind of form submission.XMLHttpRequest now provides progress event attributes that allow us to monitor data transfers. Previously, we would listen to the
Monitoring data transfers with progress eventsreadystatechangeevent,
as in the example below:var xhr = new XMLHttpRequest(); var onReadyStateHandler = function(event) { if( event.target.readyState == 4 && event.target.status == 200){ /* handle the response */ } } xhr.open('GET','/path_to_data'); xhr.onreadystatechange = onReadyStateHandler; xhr.send();
Though it works well for alerting us that all of our data has downloaded,readystatechangedoesn’t
tell us anything about how much data has been received. For backward compatibility, it remains a part of the specification. TheProgressEventinterface,
however, is far more robust. It adds seven events that are available to both theXMLHttpRequestand
theXMLHttpRequestUploadobjects.
The differentXMLHttpRequestProgress
Events are as follows:ProgressEventinherits from
the DOM,
Level 2EventTargetinterface
so we can either use event attributes such asonload,
or theaddEventListenermethod
in our code. In the examples above, we've used event attributes. In our next example, we’ll useaddEventListener.All
Monitoring uploadsXMLHttpRequest-based file
uploads create anXMLHttpRequestUploadobject,
which we can reference with theuploadattribute
ofXMLHttpRequest. To monitor
upload progress, we’ll need to listen for events on theXMLHttpRequestUploadobject.
In the code below, we’re listening for theprogress,loadanderrorevents:var onProgressHandler = function(event) { if(event.lengthComputable) { var howmuch = (event.loaded / event.total) * 100; document.querySelector('progress').value = Math.ceil(howmuch); } else { console.log("Can't determine the size of the file."); } } var onLoadHandler = function() { displayLoadedMessage(); } var onErrorHandler = function() { displayErrorMesssage(); } xhr.upload.addEventListener('progress', onProgressHandler, false); xhr.upload.addEventListener('load', onLoadHandler, false); xhr.upload.addEventListener('error', onErrorHandler, false);
Pay special attention to thelengthComputable,loadedandtotalproperties
used in theonProgressHandlerfunction.
Each of these are properties of the progress event object. ThelengthComputableproperty
reveals whether or not the browser can detect the input file size, whileloadedandtotalreveal
how many bytes have been uploaded and the total size of the file. You can view
an XHR progress events demo.
These events only monitor the browser’s progress in sending data to or receiving data from the server. When uploading, you may experience a lag between when theloadevent
is fired and when the server sends a response. How long of a lag will depend on the size of the file, the server’s resources, and network speeds.
In the example above, we’re setting event listeners on theXMLHttpRequestUploadobject.
To monitor file downloads, add event listeners to theXMLHttpRequestobject
instead.MIME-type mismatches are pretty common on the web. Sometimes XML data will have a
Enforcing a response MIME typeContent-type: text/htmlresponse header, which will cause the value ofxhr.responseXMLto
benull.
To ensure that the browser handles such responses in the way we’d like, we can use theoverrideMimeType()method.
In the example below, data.xml returns the following response headers:Date: Sat, 04 Jun 2011 03:11:31 GMT Server: Apache/2.2.17 Access-Control-Allow-Origin: * Keep-Alive: timeout=5, max=100 Connection: Keep-Alive Content-Type: text/html; charset=UTF-8
That’s the wrong content type for an XML document. So let’s guarantee that the browser treats data.xml as
XML, and populates theresponseXMLattribute:var xhr = new XMLHttpRequest(); xhr.open('GET','data.xml'); xhr.overrideMimeType('application/xml'); xhr.send(); xhr.addEventListener('load', function(event) { console.log( event.target.responseXML ); }, false);
Now the value ofxhr.responseXMLis
aDocumentobject, which means
we can parse the data as we would any other XML document. View
an XHR override MIME type demo.It's also possible to tell the browser to handle a response as
Enforcing a response typetext,json,
anarraybuffer, ablobor
anddocumentusing theresponseTypeproperty.
As withoverrideMimeType, theresponseTypeproperty
must be set before the request is sent. In the example below, we are telling Opera to treat the response as adocument,
and write thefirstChildto
the console (view
an enforcing response type demo):var xhr = new XMLHttpRequest(); xhr.open('GET','data.xml'); xhr.responseType = 'document'; xhr.send(); xhr.addEventListener('load', function(e) { console.log( event.target.response.firstChild ); } false);
ThoughresponseTypeallows developers
to, say, handle image data as a byte array instead of a binary string, it does not work miracles. Changingdocumenttojsonin
the example above would cause our response property to benullbecause
XML is not JSON. Similarly, invalid JSON data will also causeresponseto
benull. When setting aresponseType,
you still need to ensure that your data is both valid and compatible with the specified type.
Note: As of publication, Opera does not supportblobas
a value, and only supports XML and not HTML for thedocumenttype.
Chrome and Safari do not yet supportjsonas
a value.These XMLHttpRequest improvements are a leap forward for client-side interactivity. For more on XMLHttpRequest, CORS, and related APIs, see the following resources:
Learn more
XMLHttpRequest
specification
DOM
access control using cross-origin resource sharing
The
W3C file API by Bruce Lawson
XDomainRequest
Object (Internet Explorer 8 & 9)
Note: Cover image — Ajax and Achilles Gaming —
by Sharon
Mollerus.
attribute | type | Explanation |
---|---|---|
onloadstart | loadstart | When the request starts. |
onprogress | progress | While loading and sending data. |
onabort | abort | When the request has been aborted, either by invoking theabort()method or navigating away from the page. |
onerror | error | When the request has failed. |
onload | load | When the request has successfully completed. |
ontimeout | timeout | When the author specified timeout has passed before the request could complete. |
onloadend | loadend | When the request has completed, regardless of whether or not it was successful. |
相关文章推荐
- (转)Ajax_XMLHttpRequest Level 2
- XMLHttpRequest Level 2 使用指南
- XMLHttpRequest Level 2 使用指南
- 利用XMLHttpRequest level 2 实现文件异步上传
- XMLHttpRequest Level 2 使用指南
- XMLHttpRequest Level 2 使用指南
- XMLHttpRequest Level 2 使用指南
- XMLHttpRequest Level 2 使用指南
- XMLHttpRequest Level 2 使用指南
- HTML5-XMLHttpRequest Level 2
- XMLHttpRequest Level 2 FormData 提交二进制文件
- 【JavsScript】XMLHttpRequest Level 2 使用指南
- [转载]XMLHttpRequest Level 2 使用指南
- xmlhttprequest level 2
- XMLHttpRequest Level 2 使用指南
- [读书笔记]Communication API 之 XMLHttpRequest Level 2
- XMLHttpRequest Level 2 使用指南
- XMLHttpRequest Level 2 使用指南
- XMLHttpRequest Level 2的简单例子
- XMLHttpRequest Level 2 使用指南——ajax实现http(s)协议的原理