js中的跨域请求应该也算是一个重点,具体什么叫跨域,在这里我就不展开了,可以查一下浏览器的同源策略和跨域的定义。原来只知道常用的jsonp和document.domain这两种方式,这几天学习了一下其他几种跨域请求的方式,正好一起做个总结。
第一种方式:jsonp请求
jsonp请求应该是大家最为熟悉的一种(至少是我知道的第一种跨域请求方式)。jsonp的原理是利用
第二种方式:document.domain
这种方式用在主域名相同子域名不同的跨域访问中,举个例子:http://a.frame.com和http://b.frame.com 他们的主域名都是frame.com 这两个域名中的文件可以用这种方式进行访问,通过在两个域中具体的文件中设置document.domain="frame.com"就可达到跨域访问的目的。
实际应用中常常用在iframe中窗口之间的访问,根据浏览器的同源策略,浏览器中不同域的框架之间是不能进行js的交互操作的,所以一个窗口是不能拿到另一个窗口中的contentWindow对象的属性和方法的(注意是能拿到contentWindow对象的,只是属性和方法都不可用)。为了能拿到数据,只要在两个iframe中分别写入document.domain="主域名",这样设置之后,就能拿到contentWindow对象的属性和方法了。代码就这么简单的一行,我就不写小例子了。
第三种方式:window.name
window的name属性有个特征:在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
这是什么意思呢?通俗来讲,就是比如我在a.html这个页面中设置了window.name="a";然后让window重新加载b.html页面,然后在b.html页面中输出window.name会发现window.name=“a”。所以就算a.html和b.html这两个页面不是在同一个域中的,也可以在b页面中拿到a页面设置的window.name的值,跨域的核心思路就是这个原理。
实际应用中也是常常用在两个iframe之间(需要结合iframe的特性来用),先上一张从别人那边借鉴过来的原理图,我再按照自己的理解进行分析:
图中有三个页面,getDomainData.html是获取数据的页面,null.html是一个和getDomainData.html同域的空页面,它的作用是作为一个中转站,进行数据的过渡。data.html是要获取数据的所在页面,这里有着我们要的数据,它和getDomainData.html处于不同域中,所以取数据是跨域访问。具体的过程是这样的:在getDomainData.html中建立一个子页面iframe,把这个iframe的src指向b.com/data.html,这样当这个iframe加载完成后就可以访问到data.html中的window.name的数据,之后再将iframe的src改为a.com/null.html,跳回getDomainData.html的同一个域,这样根据同源策略,getDomainData.html就可以访问到null.html中取得的data.html的数据了。获取数据以后最好销毁这个iframe,释放掉内存,也保证了安全。下面附上代码小例子:
getDomainData.html:
data.html:
最后的iframe销毁:
第四种方式:window.postMessage
window.postMessages是html5中实现跨域访问的一种新方式,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源。
该方式的使用还是十分简单的,给要发送数据的页面中的window对象调用一个postMessage(message,targetOrigin)方法即可,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,直接使用通配符 * 。再让接收数据页面的window对象监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。简单的小例子如下:
test.html(发送页面):
test2.html(接收页面)
关于这个用法困扰了我好久,困扰1:因为最后弹出的消息是在test.html,而不是在test2.html中,我不确定是因为test.html包含了test2.html,所以浏览器渲染才弹出的alert,还是test2.html接收到消息的反馈。后来查阅了权威的文档,才有了进一步的理解,应该是test2.html收到消息,以iframe在test.html中渲染加载出的。
困扰2:postMessage的调用对象是目标窗口还是发送窗口,是否能以window形式调用?
查阅文档后得出结论:*postMessage的调用对象,是其他窗口的一个引用,即目标窗口,不是要发送的窗口,(这里比较出乎意料) 而且postMessage想要通信必须使得一个窗口以iframe的形式存在于另一个窗口,或者一个窗口是从另一个窗口通过window.open()或者超链接的形式打开的(同样可以用window.opener获取源窗口);换句话说,你要交换数据,必须能获取目标窗口(target window)的引用,不然两个窗口之间毫无联系,想通信也无能为力,所以不能直接以主页面window的形式调用。
具体的权威解释请看这个链接: window.postMessage
第五种方式:CORS
CORS(Corss-Origin-Resource Sharing,跨源资源共享),是一种网络浏览器的技术规范,它为Web服务器定义了一种方式,允许网页从不同的域访问其资源,而这种访问是被同源策略所禁止的。CORS系统定义了一种浏览器和服务器交互的方式来确定是否允许跨域请求。 它是一个妥协,有更大的灵活性,但比起简单地允许所有这些的要求来说更加安全。
CORS背后的基本思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是应该失败。
CORS的使用还是十分简便的,比如一个简单的GET或者POST请求,在发送的时候给它附加一个额外的origin头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给以响应。下面是javascript高级程序设计书上的一个小例子:
Origin:http://www.nczonline.net
如果服务器认为这个请求可以接受,就在Access-Control-Allow-Origin头部中回发相同的源消息(如果是公共资源,可以回发“ * ”)。例如:
Access-Control-Allow-Origin:http://www.nczonline.net
这样设置之后,服务器与浏览器就可以进行跨域信息的交换了。具体的在不同浏览器上的支持和使用,我就不展开了,js高级程序设计书上提到了很多,网上查一下也有很多。
第六种方式:Web Sockets
Web Sockets是一种新浏览器API,能在一个单独的持久连接上提供全双工、双向通信,使用ws(代替http://)或wss(代替https://)协议,可用于任意的客户端和服务器程序。
web sockets原理:在JS创建了web socket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议。
使用的小例子:
// 创建一个Socket实例
var socket = new WebSocket('ws://www.example.com/server.php');
// 打开Socket
socket.onopen = function(event) {
// 发送消息
socket.send('a secret');
// 监听消息的接收
socket.onmessage = function(event) {
var data=event.data;
//处理data...
};
// 监听socket的关闭
socket.onclose = function(event) {
console.log('socket has closed',event);
};
// 关闭Socket
//socket.close()
};
同样附上权威文档供参考:Web Workers API
总结
除此之外还有一些跨域访问的方式:比如Comet、图像Ping、SSE等,感兴趣的可以直接查找这些内容。在这些跨域访问方式上,各有各的适用访问和相应的限制,需要结合实际来适用。我在这里有个疑问:我自己运用的比较多的就是jsonp这种方式,那么在实际开发中,比较推崇的跨域访问方式是哪种呢?还有就是html5的postMessage是不是可以取代window.name(同样都需要一个iframe作为中间媒介)这类访问方式,这个新API方式是不是在实际中很有效呢?希望有经验的大牛可以解答,不胜感激。