XMLHttpRequest 等级 2 介绍
2013-06-21 10:59
393 查看
介绍
XMLHttpRequest可以让开发者发送
HTTP 与 HTTPS 请求并在不重載当前页面的情况下更动页面。一般常用的两个使用情景是:提交表单与取得更多内容。
在之前的
XMLHttpRequest中,请求的内容只能是文本、HTML 或 XML。要发送索引键值配对需要使用读取与写入皆不干净的语法 − URL 编码字符串。
之前的 XHR 必须遵守让跨域请求相当困难的相同来源政策。举例来说,要是没有像是
Flash 补助 XHR 或是代理服务器之类的中介,开发者不能在 http://foo.example/ 与 http://bar.example/ 之间分享数据。要在子域间(例如 http://foo.example 与 http://www.foo.example)分享数据,开发者必须在两个来源的脚本中设置
document.domain,但是这样做又会有安全风险。
以前的 XHR 可以上传档案吗?不行。开发者必须使用依赖插件的 SWFUpload,不然就是需要一个隐藏
iframe,但是后者又欠缺用户端的进展事件。
本文将会踏入 XMLHttpRequest 的一些最新进展并详述 Opera 12 的支持情形。
XMLHttpRequest 有什么新东西
在XMLHttpRequest规范与浏览器支持有所进步的现在,开发者可以:
设置请求超时
透过
FormData对象管理数据
传输二位元数据
监听数据传输的进展
进行更安全的跨域请求
覆盖媒体类型与响应的媒体形态
设置并处理超时
有时候有可能是因为网路频宽不够或是服务器响应时间太长导致请求的完成速度很慢,这会造成应用反应迟钝,对用户体验有不良的影响。XHR 现在已经有处理这个问题的方法了:请求超时。开发者可以用
timeout属性指定应用的等待时间,如果超过了这个时间,开发者可以让应用进行别的工作。下面的例子设置了三秒(3000 微秒)的超时:
function makeRequest() { var url = 'data.json'; var onLoadHandler = function(event){ // 解析 JSON 并建一个数组。 } var onTimeOutHandler = function(event){ var content = document.getElementById('content'), p = document.createElement('p'), msg = document.createTextNode('再等一下!'); p.appendChild(msg); content.appendChild(p); // 重新启动请求。 event.target.open('GET', url); // 也可以用一个更长的超时,盖掉原来的。 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);
当经过了三秒但是响应数据还没到达时,上面例子会告知用户请求要花多一点时间,并启动一个设有更长超时时间的请求(观看 XHR 超时演示)。在
timeout事件监听中重设超时时间不是必要的,但是在这个例子中,由于新的 URL 的响应总是会超过原来的超时时间,这里才为了避免循环重设超时时间。
到目前为止,Chrome 与 Safari 不支持 XHR 超时。Opera、Firefox 与 IE 10 则支持。IE 8 与 9 也支持
XDomainRequest对象上的超时。
从别的网域请求数据
之前 XHR 的一个限制是相同来源政策:发出请求的文档和被请求的文档必须有相同的协议、域名、端口。一个从 http://www.foo.example 至 http://www.foo.example:5050 的跨端口请求会导致浏览器抛出安全例外(除了允许跨端口请求的旧版本 IE 以外)。而现在
XMLHttpRequest已经在跨来源资源共享(CORS)启动的时候支持跨来源请求了。
尽管 IE10 支持跨域
XMLHttpRequest,但是 IE 8 与 9 不支持。然而,在 IE 8 与 9 下可以用作用差不多的
XDomainRequest对象。
除了使用绝对 URL 而不是相对 URL 以外,跨来源请求长得跟相同来源请求差不多:
var xhr = new XMLHttpRequest(); var onLoadHandler = function(event) { /* 用响应作点事 */ } xhr.open('GET','http://other.server/and/path/to/script'); xhr.onload = onLoadHandler; xhr.send();
重要的不同点在于目标 URL 必须透过发送
Access-Control-Allow-Origin响应标头来表示允许存取。
跨来源资源共享概论
请参阅《DOM access control using cross-origin resource sharing》以更深入了解 CORS,这里仅介绍两个标头:Origin请求标头,与
Access-Control-Allow-Origin响应标头。
Origin
请求标头
Opera 与其他浏览器会在进行跨来源 XHR 请求的时候自动加入 Origin标头,如同下面的例子:
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]Origin一般包含请求文档的协议、域名、端口。Origin不是一个“网页作者请求标头”,也就是说Origin不能用setRequestHeader()方法设置或更改 ― 用户代理会忽略这种设置。Origin标头唯一的目的就是告知目标服务器该请求的来源。请注意Origin标头的结尾没有斜线。
Access-Control-Allow-Origin响应标头Access-Control-Allow-Origin标头是在目标服务器收到跨来源请求时回应的标头。Access-Control-Allow-Origin告诉用户代理是否应给予发出请求的来源存取
权限。 除非被请求的 URL 允许跨域存取,否则与跨来源 XHR 请求相关的 DOM 操作皆不会完成。以下是例子: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: *
这个例子用通配符(*)让任何来源的文档都可以存取此 URL,这在开发者要提供一个公用的
API 的时候是无所谓,但是在其他的使用情景里,开发者应该设置比较具体的值。在跨域请求中发送认证讯息
开发者可能会有要在跨域请求中发送 Cookie 数据的状况,这时候withCredentials属性就登场了。withCredentials属性是一个布尔属性,用来告知浏览器是否应该随同请求发送用户认证讯息。默认情况下,认证讯息旗帜为false。在下面的代码中,我们假设请求是一个从 http://foo.example 送到 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();
XHR 认证讯息演示使用 Cookie 作为计数器来追踪访问量。去检视请求与响应的数据(可以用 Dragonfly 的网路面板)就会看到浏览器的确在发送请求 Cookie 并收到响应 Cookie。服务器端脚本会回传包含新的访问计数的文本,并更新 Cookie 的值。
请记得在使用代有认证讯息的请求的时候:
仅在跨来源请求的时候需要设置withCredentials。
请求 URI 的Access-Control-Allow-Origin标头不能包含通配符(*)。
请求 URI 的Access-Control-Allow-Credentials标头必须设为true。
除非设有Access-Control-Expose-Headers标头,getAllRequestHeaders()仅可以取得一部分的响应标头。
在相同来源的情况下,浏览器求会忽略认证讯息旗帜。代有通配符的Access-Control-Allow-Origin标头会让浏览器抛出异常。若Access-Control-Allow-Credentials是false,浏览器会发送并接受 Cookie,但是不能从 DOM 中取得。透过
在之前的版本中,透过 XHR 发送的数据必须是一个 URL 编码或是(利用FromData对象发送索引键值配对JSON.strigify()得到的)JSON 字符串。下面的例子使用 URL 编码: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);
但是现在开发者可以使用FormData对象跟它提供的索引键值配对语法。这个语法有三种个特点:
让脚本更容易读。
跟一般的 HTML 表单一样,数据以索引键值配对发送。FormData对象会使数据以multipart/form-data编码发送,让 XHR 可以发送二进制的数据。FormData跟 ActionScript 3.0 的URLVariables使用起来的感觉很像:先创建一个FormData对象,再使用append()方法添加数据。append()方法使用两个参数:键与值。FormData的每一个键会变成服务器端脚本可以使用的一个变量名。以下是个例子:var xhr = new XMLHttpRequest(); var dataToSend = new FormData(); // 创建一个 FormData 对象 xhr.open('POST','/processing_script'); dataToSend.append('name', '哆啦A梦'); // 在对象上添加数据 dataToSend.append('age', '11'); dataToSend.append('hobby', '吃铜锣烧'); xhr.send(dataToSend); // 发送对象
在最后面的地方FormData对象被当成send()方法的参数:xhr.send(dataToSend)。上面的例子并没有在XMLHttpRequest对象上设置Content-Type标头。来看看 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
由于例子里用了FormData对象,Opera 帮忙添加了Content-Type标头。其他浏览器也是这样的情形。由 HTML 表单创建
开发也可以使用FormDataFormData发送表单的值,只要将表单传给FormData对象就可以了(XHR FormData 演示),如下: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);FormData仍是不受信任的数据。请将由FromData对象来的数据当成与一般的表单提交得到的数据无异。使用进度事件监测数据传输
现在的XMLHttpRequest已经提供了让开发者监测数据传输的进度事件属性。在之前的时候,开发者需要监听readystatechange事件,如下面的例子:var xhr = new XMLHttpRequest(); var onReadyStateHandler = function(event) { if( event.target.readyState == 4 && event.target.status == 200){ /* 处理响应 */ } } xhr.open('GET','/path_to_data'); xhr.onreadystatechange = onReadyStateHandler; xhr.send();
虽然readystatechange在开发者仅注意所有数据的下载完全时间的情形下足够用了,但是readystatechange并不能提供到底已经接收了多少数据的即时信息。为了向后兼容,readystatechange仍是规范的一部分,但是新的ProgressEvent接口可靠多了。新的规范为XMLHttpRequest与XMLHttpRequestUpload对象加上了七个事件。
以下是XMLHttpRequest的ProgressEvent列表:
属性 | 形态 | 解释 |
---|---|---|
onloadstart | loadstart | 请求开始的时候。 |
onprogress | progress | 读取与发送数据的时候会不断触发本事件。 |
onabort | abort | 经由调用 abort()方法或浏览到别的页面使得请求退出的时候。 |
onerror | error | 请求发生错误的时候。 |
onload | load | 请求成功完成的时候。 |
ontimeout | timeout | 开发者指定的超时时间已经超过了但是请求仍未能完成的时候。 |
onloadend | loadend | 不管请求成功与否,请求已经完成的时候。 |
XMLHttpRequest与
XMLHttpRequestUpload继承
EventTarget接口,开发者者可以在代码中使用事件属性(如
onload)或是
addEventListener方法。下面的范例使用
addEventListener。
监测上传的进度
所有基于XMLHttpRequest的档案上传都会产生一个
XMLHttpRequestUpload对象,开发者可以通过
XMLHttpRequest的
upload属性取得该对象。要监测上传进度,开发者需要监听
XMLHttpRequestUpload对象上的事件。
下面的代码监听了
progress、
load与
error事件:
var onProgressHandler = function(event) { if(event.lengthComputable) { var howmuch = (event.loaded / event.total) * 100; document.querySelector('progress').value = Math.ceil(howmuch); } else { console.log("无法取得档案的大小。"); } } 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);
情特别注意
onProgressHandler函式里用的
lengthComputable、
loaded与
total,这些都是进度事件对象上的属性。
lengthComputable属性告诉开发者浏览器可否侦测上传档案的大小,而
loaded与
total告诉开发者已上传了多少个字节与档案的总大小。请看
XHR 进度事件的演示。
这些事件监测了浏览器传输数据到服务器或服务器接收数据的进度。在上传的情况下,有可能在触发
load事件与服务器回传响应之间有一个时间差。这个时间差的长度取决于档案的大小、服务器的资源与网路的速度。
上面的例子为
XMLHttpRequestUpload设置了事件监听,要监档案下载要在
XMLHttpRequest对象上添加事件监听。
强制变更响应的 MIME 形态
MIME 不正确的情况在万维网上十分常见。有时候 XML 数据的响应标头有Content-type: text/html,使得
xhr.responseXML变成
null。
为了确保浏览器依照开发者的意思处理这样的响应,开发者可以使用
overrideMimeType()方法。在下面的例子里,data.xml 会回传下列的响应标头:
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
也就是 XML 文档的内容形态是错误的。下面代码确保浏览器将 data.xml 视为 XML并设置
responseXML属性:
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);
现在
xhr.responseXML的之已经是一个
Document对象了,也就是开发者可以像碰到正常的 XML 文档一样解析数据了。观看覆盖 XHR 的 MIME 形态的演示。
强制使用某种数据形态
开发者也可以使用responseType属性让浏览器将响应当成
text、
json、
arraybuffer、
blob或
document。
开发者必须在送出请求前设置
responseType属性,就像
overrideMimeType一样。下面的例子让 Opera 把响应当作是
document,并将
firstChild写到控制台里(观看强制使用某种数据形态的演示):
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);
虽然
responseType可以让开发者将图像数据当作一个字节数组而不是二位元字符串,但是它不会创造奇迹:在上面的例子中将
document改成
json会让
response属性变成
null,因为 XML 不是 JSON。同样的,不合法的 JSON 数据也会让
response变成
null。开发者需要在设置
responseType的时候确保数据符合指定的形态而且合法。
注:在文章发布的这个时间点,Opera 尚未支持
blob值,在
document形态下也只之持 XML 而不支持 HTML。Chrome 与 Safari 不支持
json值。
学习更多
这些XMLHttpRequest的进步对于用户端的交互性能是一种跃升。请阅读以下资源以获取更多关于
XMLHttpRequest、CORS 与相关 API 的讯息:
XMLHttpRequest specification
DOM access control using cross-origin resource sharing
Bruce Lawson 写的
The W3C file API
XDomainRequest Object (Internet Explorer 8 & 9)
相关文章推荐
- AJAX初探,XMLHttpRequest介绍
- XMLHttpRequest用法介绍
- XMLHttpRequest介绍
- AJAX的初步介绍以及XMLHttpRequest对象的五步使用法
- XMLHttpRequest介绍
- window.XMLHttpRequest 详细介绍
- XMLHttpRequest对象介绍——1
- XMLHttpRequest对象介绍
- Ajax核心XMLHttpRequest对象、(发送请求、接收)方法和属性介绍、AJAX开发框架、数据格式提要(XML、JSON、HTML)
- XmlHttpRequest对象介绍及属性
- AJAX概念介绍:2.2 XMLHttpRequest对象的创建、请求、响应
- (转)XMLHTTPRequest对象原理介绍与应用
- XMLHttpRequest用法介绍
- AJAX核心XMLHttpRequest的属性和方法介绍_javascript
- XMLHttpRequest介绍
- (转)XMLHTTPRequest对象原理介绍与应用
- XMLHTTPREQUEST详细介绍
- 【Ajax】XMLHttpRequest介绍以及在前端如何实现Ajax
- 初识AJAX与XMLHttpRequest介绍
- Ajax核心XMLHttpRequest对象、(发送请求、接收)方法和属性介绍、AJAX开发框架、数据格式提要(XML、JSON、HTML)