【JavaScript】跨源资源共享CORS和其他跨域技术(Comet、JSONP、SSE、Web Sockets)

通过XHR实现Ajax通信的一个主要限制,来源于跨域安全策略。默认情况下XHR对象只能访问与包含它的页面位于同一个域中的资源,这种安全策略可以预防某些恶意行为。但是,实现合理的跨域请求也是很重要的。

一、CORS跨源资源共享

CORS(Cross-Origin Resource Sharing),跨源资源共享,是W3C的一个工作草案,定义了在必须访问跨源资源时,浏览器与服务器该如何沟通。其背后的基本思想使用自定义的HTTP头部,让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是应该失败。

比如:一个简单的使用get或者post发送的请求,它没有自定义的头部,在发送该请求时,需要给它附加一个额外的Origin头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。

1、IE对CORS的实现

IE8中引入了XDR(XDomainRequest)类型,这个对象与XHR类似,能够实现安全可靠的跨域通信。
XDR与XHR不同之处在于:
1. cookie不会岁请求发送,也不会随响应返回。
2. 智能设置请求头部信息中的 Content-Type字段
3. 不能访问响应头部信息
4. 只支持get和post请求

XDR使得CSRF(跨站点请求伪造)和XSS(跨站点脚本)的问题得到缓解。
XDR的使用与XHR相似。

1、创建XDR对象;
2、调用open()方法,这里open方法只有两个参数,因为所有XDR请求都是异步执行的;
3、调用send()方法。

在接收到响应后,只能访问响应的原始文本,没有办法确定响应的状态码。要检测错误,可以指定一个onerror事件处理程序

var xdr = new XDomainRequest();
xdr.onload = function () {
    alert(xdr.responseText);
};
xdr.onerror = function () { //错误处理程序
    alert("An error occurred.");
};
xdr.open("post","http://www.somewhert-else.com/page/");
xdr.contentType = "application/x-www-form-urlencoded";
xdr.send("name1=value1&name2=value2");

注:由于导致XDR请求失败的因素有很多,因此不要忘记通过onerror事件处理程序来捕获该事件

XDR与XHR一样,也支持timeout属性及ontimeout事件处理程序

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

Firefox3.5+/Safari4+/Chrome/iOS版Safari和Android平台中的WebKit都通过XMLHttpRequest对象实现了对CORS的原生支持。要请求位于另一个域中的资源,使用标准的XHR对象并在open()方法中传入绝对URL即可
与XDR不同,通过跨域XHR对象可以访问status和statusText属性,而且支持同步请求。其主要限制有:

1、不能使用setRequestHeader()设置头部信息
2、不能发送和接收cookie
3、调用getAllResponseHeaders()方法总会返回空字符串

Tips:对于本地资源,最好使用相对URL,对于远程资源再使用绝对URL

3、跨浏览器的CORS

检测XHR是否支持CORS的最简单的方式,就是检查是否存在withCredentials 属性。在结合检测XDomainRequest对象是否存在,就可以兼顾所有浏览器了。

function createCORSRequest (method,url) {
    var xhr = createXHR();
    if ("withCredentails" 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 = createCORSRequest("get","http://www.somewhere-else.com");
if(request) {
    request.onload = function (){
    //相关代码
    }
request.send();
}

二、其他跨域技术

1、图像Ping

一个网页可以从任何网页中加载图像,不用担心跨域不跨域。图像Ping是与服务器进行简单/单向的跨域通信的一种方式。请求的数据是通过查询字符串的方式发送的。图像Ping最常用于跟踪用户点击页面或动态广告曝光次数。
其主要缺点有:1、只能发送get请求;2、无法访问服务器响应文本

var img = new Image();
img.onload = function () {
    alert("Done!");
};
img.onerror = function () {
    alert("Wrong!");
};
img.src = "http://www.example.com/test?name=Jay";

2、JSONP

JSONP是JSON with padding(填充式JSON/参数式JSON)的简写,是包含在函数调用中的JSON。

JSONP由两部分组成:回调函数和数据
回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的,而数据就是传入回调函数中的JSON数据。

JSONP是通过动态script元素来使用的,使用时可以为src属性指定一个跨域的URL。
优点:能够直接访问响应文本,支持在浏览器与服务器之间的双向通信
缺点:1、JSONP是从其他域加载代码执行,若其他域不安全,很可能会在响应中夹杂恶意代码;2、确定JSONP请求是否失败并不容易


<html>
<head>
    <meta charset="utf-8"/>
    <title>JSONP跨域请求title>
head>
<body>
    <div id="box">div>
    <script type="text/javascript">
    function handle (response) { //回调函数,response就是响应数据
        var box = document.getElementById("box");
        var html = "";
        html += "响应数据为:" + response.words;
        box.innerHTML = html;
        console.log(html);
    }
    function jsonp (url,callback) {
        var script = document.createElement("script");//动态创建script
        script.src = url;
        var header = document.getElementsByTagName("head")[0];
        header.appendChild(script);
    }
    jsonp("data.json",handle);
    script>
body>
html>

data.json

handle(
{
    "words":"hello JSONP"
}
    )

【JavaScript】跨源资源共享CORS和其他跨域技术(Comet、JSONP、SSE、Web Sockets)_第1张图片
【JavaScript】跨源资源共享CORS和其他跨域技术(Comet、JSONP、SSE、Web Sockets)_第2张图片

注:一定要注意,JSONP是在函数调用里的JSON

3、Comet

Ajax是一种从页面向服务器请求数据的技术,而Comet是一种服务器向页面推送数据的技术。Comet能够让信息近乎实时的被推送到页面上,非常适合处理体育比赛的分数和股票报价。

有两种实现Comet的方式:长轮询和流。

(1)长轮询

长轮询是短轮询的一个翻版,即浏览器定时向服务器发送请求,看有没有更新的数据。页面发起一个到服务器的请求,然后服务器一直保持连接打开,直到有数据可发送。发送完数据之后,浏览器关闭连接,接着又发起一个到服务器的新请求。这一过程在页面打开期间一直持续不断。
长轮询(long polling)模式涉及了打开连接的技术。连接由服务器端保持着打开的状态,只要一有事件发生,响应就会被提交,然后连接关闭。接下来。一个新的长轮询连接就会被正在等待新事件到达的客户端重新打开。

【JavaScript】跨源资源共享CORS和其他跨域技术(Comet、JSONP、SSE、Web Sockets)_第3张图片

(2)HTTP流

浏览器向服务器发送一个请求,而服务器保持连接打开,然后周期性的向浏览器发送数据。
使用XHR对象实现HTTP流:

/*=====================使用XHT对象实现HTTP流=======================*/
function createStreamingClient (url,progress,finished) {
    var xhr = new XMLHttpRequest();
    var received = 0;
    xhr.open("get",url,true);
    xhr.onreadystatechange = function () {
        var result;
        if (xhr.readyState == 3) {
            //只取得最近数据并调整计数器
            result = xhr.responseText.substring(received);
            received += result.length;

            //调用progress函数
            progress(result);
        } else if (xhr.readyState == 4) {
            finished(xhr.responseText);
        }
    }
    xhr.send(null);
    return xhr;
}
var client = createStreamingClient("s.php",function(data){
    alert("Received" + data);
},function (data) {
    alert("Done!")
});

4、SSE 服务器发送事件

SSE是围绕只读Comet交互推出的API,用于创建到服务器的单向连接,服务器通过这个连接可以发送任意数量的数据。

首先,要创建一个新的EventSource对象,并传入一个入口点,传入的URL必须与原页面同源。EventSource的实例有一个readyState属性,值为0表示正连接到服务器,1为表示打开了连接,2为表示关闭了连接。

5、Web Sockets

其目标是在一个单独的持久连接上提供全双工/双向通信。

在JavaScript中创建了Web Sockets之后,会有一个HTTP请求发送到服务器以发起连接,在取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为Web Socket 协议。
使用自定义协议的好处是:能够在客户端和服务器之间发送非常少量的数据,而不必担心HTTP那样字节级的开销。

1、实例WebSocket对象,并传入URL

var socket = new WebSocket("ws://www.example.com/server.php");//必须传入绝对URL

实例化WebSocket对象后,浏览器会马上尝试创建连接。
2、发送和接收数据

socket.send("hello");
var message ={
    time: new Date(),
    text:"hello world"
}
socket.send(JSON.stringify(message));//WebSocket只能通过连接发送纯文本数据,因此将数据序列化为JSON字符串

你可能感兴趣的:(javascript)