XHR 对象只能访问与包含它的页面位于同一个中的资源. 这种安全策略可以预防某些恶意行为.
CORS(Cross-Origin Resource Sharing, 跨域资源共享)是 W3C 的一个工作草案, 定义了在必须访问跨域资源时, 浏览器与服务器应该如何沟通. CORS 基本思想: 使用自定义的 HTTP 头部让浏览器与服务器进行沟通, 从而决定请求或响应是应该成功还是失败.
如: 一个使用 GET 或 POST 发送的请求, 没有自定义的头部, 主体内容是 text/plain. 发送请求时, 需要给它附加一个额外的 Origin 头部, 其中包含请求页面的源信息(协议, 域名和端口), 以便服务器根据这个头部信息来决定是否给与响应.
Origin 头部如:
Origin: http://www.nczonline.net
若服务器认为这个请求可以接受, 就在 Access-Control-Allow-Origin 头部中回发相同的源信息(若是公共资源, 可以回发 "*"). 如:
Access-Control-Allow-Origin: http://www.nczonline.net
若没有这个头部, 或有这个头部但源信息不匹配, 浏览器就会驳回请求. 正常情况下, 浏览器会处理请求.
注: 请求和响应都不包含 cookie 信息.
1,IE 对 CORS 的实现
微软在 IE8 中引入了 XDR(XDomainRequest)类型, 能够实现安全可靠的跨域通信. XDR 对象的安全机制部分实现了 W3C 的 CORS 规范.
XDR 与 XHR 的一些不同之处:
* cookie 不会随请求发送, 也不会随响应返回.
* 只能设置请求头部信息中的 Content-Type 字段.
* 不能访问响应头部信息.
* 只支持 GET 和 POST 请求.
被请求的资源可以根据它认为合适的任意数据 (用户代理, 来源页面等) 来决定是否设置 Access-Control-Allow-Origin 头部. 作为请求的一部分, Origin 头部的值表示请求的来源域, 以便远程资源明确的识别 XDR 请求.
XDR 对象的使用方法与 XHR 对象非常相似. 也是创建一个 XDomainRequest 的实例, 调用 open()方法, 再调用 send()方法. 但与 XHR 对象的 open()方法不同, XDR 对象的 open()只接收 2 个参数: 请求的类型和 URL.
所有 XDR 请求都是异步执行的. 请求返回之后, 会触发 load 事件, 响应的数据也会保存在 responseText 属性中, 如:
- var xdr = new XDomainRequest();
- xdr.onload = function(){
- alert(xdr.responseText);
- };
- xdr.open("get", "http://www.somewhere-else.com/page/");
- xdr.send(null);
在接收到响应后, 只能访问响应的原始文件, 没有办法确定响应的状态代码. 而且只要响应有效就会触发 load 事件, 如果失败就会触发 error 事件. 遗憾的是, 除了错误本身之外, 没有其他信息可用, 因此唯一能够确定的就只有请求未成功了. 要检测错误, 可以指定一个 onerror 事件处理程序:
- var xdr = new XDomainRequest();
- xdr.onload = function(){
- alert(xdr.responseText);
- };
- xdr.onerror = function(){
- alert("An error occurred.");
- };
- xdr.open("get", "http://www.somewhere-else.com/page/");
- xdr.send(null);
注: 导致 XDR 请求失败的因素很多, 所以建议通过 onerror 事件处理程序来捕获该事件; 否则, 即使请求失败也不会有任何提示.
在请求返回前调用 abort()方法可以终止请求:
xdr.abort(); // 终止请求
XDR 对象也支持 timeout 属性以及 ontimeout 事件处理程序. 如:
- var xdr = new XDomainRequest();
- xdr.onload = function(){
- alert(xdr.responseText);
- };
- xdr.onerror = function(){
- alert("An error occurred.");
- };
- xdr.timeout = 1000;
- xdr.ontimeout = function(){
- alert("Request took too long.");
- };
- xdr.open("get", "http://www.somewhere-else.com/page/");
- xdr.send(null);
运行 1s 后超时, 并随机调用 ontimeout 事件处理程序.
为支持 POST 请求, XDR 对象提供了 contentType 属性, 用来表示发送数据的格式, 如:
- var xdr = new XDomainRequest();
- xdr.onload = function(){
- alert(xdr.responseText);
- };
- xdr.onerror = function(){
- alert("An error occurred.");
- };
- xdr.open("post", "http://www.somewhere-else.com/page/");
- xdr.contentType = "application/x-www-form-urlencoded";
- xdr.send("name1=value1&name2=value2");
这个属性是通过 XDR 对象影响头部信息的唯一方式.
2, 其他浏览器对 CORS 的实现
- var xhr = createXHR();
- xhr.onreadystatechange = function(){
- if(xhr.readyState ==4){
- if((xhr.status>= 200 && xhr.status < 300) || xhr.status == 304){
- alert(xhr.responseText);
- }else{
- alert("Request was unsuccessful:" + xhr.status);
- }
- }
- };
- // 跨域请求的 url 为绝对的链接
- xhr.open("get", "http://www.somewhere-else.com/page/", true);
- xhr.send(null);
限制:
* 不能使用 setRequestHeader()设置自定义头部.
* 不能发送和接收 cookie.
* 调用 getAllResponseHeaders()方法总会返回空字符串.
对于本地资源, 最好使用相对 URL, 访问远程资源时使用绝对 URL, 可以消除歧义, 避免出现限制访问头部或本地 cookie 信息等问题.
3, 跨浏览器的 CORS
所有浏览器都支持简单的 (非 Preflight 和不带凭据的) 请求, 因此有必要实现一个跨浏览器的方案.
- function createCORSRequest(method, url){
- var xhr = new XMLHttpRequest();
- if("withCredentials" in xhr){
- xhr.open(method, url, true);
- }else if(typeof XDomainRequest != "undefined"){
- xhr = new XDomainRequest();
- xhr.open(method, url);
- } else {
- xhr = null;
- }
- return xhr;
- }
- var request = cresteCORSRequest("get", "http://www.somewhere-else.com/page/");
- if(request){
- request.onload = function(){
- // 对 request.responseText 进行处理
- };
- request.send();
- }
Firefox,Safari,Chrome 中的 XMLHttpRequest 对象与 IE 中的 XDomainRequest 对象类似, 都提供了够用的接口, 因此以上模式还是相当有用的.
这两个对象共同的属性 / 方法如下:
* abort(): 用于停止正在进行的请求.
* onerror: 用于替代 onreadystatechange 检测错误.
* onload: 用于替代 onreadystatechange 检测成功.
* responseText: 用于取得相应内容.
* send(): 用于发送请求.
以上成员都包含在 createCORSRequest()函数返回的对象中, 在所有浏览器中都能正常使用.
来源: http://www.bubuko.com/infodetail-3113546.html