跨源资源共享(CORS)

一、CORS简介

在前一篇博客中,我介绍了利用JSONP实现跨域请求,但是在上篇文章中也指出了用JSONP实现跨域存在的一些缺点, 因此W3C 提出了另外一个跨域的方法:CORS,全称是”跨域资源共享”(Cross-origin resource sharing)。与JSONP相比,CORS更为为先进、方便和可靠:

  • JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
  • 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
  • JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS。

    关于浏览器对CORS的支持情况,读者可以参照网址http://caniuse.com/#feat=cors,该网站中对CORS的支持情况进行了总结(绿色代表支持):

    这里写图片描述

CORS 背后的基本思想,就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。比如一个简单的使用 GET 或 POST 发送的请求,它没有自定义的头部,而主体内容是 text/plain。在发送该请求时,需要给它附加一个额外的 Origin 头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。例如我们的

 Origin:http://localhost
  • 1

在服务器端如果认为这个请求可以接受,就在 Access-Control-Allow-Origin 头部中回发相同的源信息(如果是公共资源,可以回发”*”)。例如:

Access-Control-Allow-Origin: http://localhost
  • 1

二、IE对CORS的实现

微软在 IE8 中引入了 XDR(XDomainRequest)类型。这个对象与 XHR 类似,但能实现安全可靠的跨域通信。 XDR 对象的安全机制部分实现了 W3C 的 CORS 规范。

XDR 对象的使用方法与 XHR 对象非常相似。也是创建一个 XDomainRequest 的实例,调用 open()方法,再调用 send()方法。但与 XHR 对象的 open()方法不同, XDR 对象的 open()方法只接收两个参数:请求的类型和 URL。

所有 XDR 请求都是异步执行的,不能用它来创建同步请求。请求返回之后,会触发 load 事件,响应的数据也会保存在 responseText 属性中,如下是一个简单的示例:

var xdr = new XDomainRequest();

xdr.onload = function(){
    console.log(xdr.responseText);
    };

xdr.open("get", "http://182.254.146.112/demo.php");
xdr.send(null);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

demo.php文件是我放在远程服务器上的文件,文件内容如下:

<?php

header("Access-Control-Allow-Origin: http://localhost");  

$info=array("name"=>"sean","age"=>"25","city"=>"Nanjing");

echo json_encode($info);
?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

注意:特别需要注意的是,服务端的文件一定要在header中设置Access-Control-Allow-Origin

在IE<11版本浏览器中测试,会得到demo.php中的返回值,如下:

{"name":"sean","age":"25","city":"Nanjing"}
  • 1

这里需要注意的是:在IE11中测试时会报错“XDomainRequest 未定义”的错误,因此IE11已经不采用该方法了,而是采用下面的标准方法。

三、其他浏览器对CORS的实现

Firefox 3.5+、 Safari 4+、 Chrome、 iOS 版 Safari 和 Android 平台中的 WebKit 都通过 XMLHttpRequest对象实现了对 CORS 的原生支持。在尝试打开不同来源的资源时,无需额外编写代码就可以触发这个行为。 要请求位于另一个域中的资源,使用标准的 XHR 对象并在 open()方法中传入绝对 URL 即可:

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
    if (xhr.readyState == 4){
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
                    console.log(xhr.responseText);
            } else {
                    console.log("Request was unsuccessful: " + xhr.status);
                }
            }
        };

xhr.open("get", "http://182.254.146.112/demo.php", true);
xhr.send(null);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在chrome和Firefox浏览器中测试都能得到结果:

{"name":"sean","age":"25","city":"Nanjing"}
  • 1

IE11也选择了XMLHttpRequest对象实现 CORS ,而抛弃了XDomainRequest,在IE11浏览器中测试,也能得到上述结果。

四、跨浏览器的CORS

即使浏览器对 CORS 的支持程度并不都一样,但所有浏览器都支持简单的(非 Preflight 和不带凭据的)请求,因此有必要实现一个跨浏览器的方案。检测 XHR 是否支持 CORS 的最简单方式,就是检查是否存在 withCredentials 属性。再结合检测 XDomainRequest 对象是否存在,就可以兼顾所有浏览器了。

withCredentials 属性可以指定某个请求是否应该发送凭据,如果withCredentials 属性设置为 true,即请求应该发送凭据。如果服务器接受带凭据的请求,会用下面的 HTTP 头部来响应。

Access-Control-Allow-Credentials: true;
  • 1

跨浏览器的CORS简单代码如下:

function createCORSRequest(method, url){

    var xhr = new XMLHttpRequest();

    if ("withCredentials" in xhr){
            xhr.open(method, url, true);
        } else if (typeof XDomainRequest != "undefined"){
            vxhr = new XDomainRequest();
            xhr.open(method, url);
                } else {
                        xhr = null;
                }
    return xhr;
}

var request = createCORSRequest("get", "http://182.254.146.112/demo.php");
if (request){
        request.onload = function(){
        console.log(request.responseText);
        //对 request.responseText 进行处理
        };
        request.send();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

上述代码在几大主流浏览器中都能得到很好的支持。另外,Firefox、 Safari 、 Chrome 和 IE11 中的 XMLHttpRequest 对象与 IE<11 中的 XDomainRequest 对象都提供了一些接口,这两个对象共同的属性/方法如下:

方法/属性 说明
abort() 用于停止正在进行的请求
onerror 用于替代 onreadystatechange 检测错误
onload 用于替代 onreadystatechange 检测成功
responseText 用于取得响应内容
send() 用于发送请求

以上成员都包含在 createCORSRequest()函数返回的对象中,在所有浏览器中都能正常使用。我们常用到的就是onloadonerror分别代表当请求成功时触发和当请求失败时触发。其基本用法如下:

request.onload = function(){
                console.log(request.responseText);
                //对 request.responseText 进行处理
                };

request.onerror = function(){
                alert("There is an error!");
                };

猜你喜欢

转载自blog.csdn.net/aaa333qwe/article/details/80249107