详解Javascript几种跨域方式总结
在客户端编程语言中如javascript,同源策略规定跨域之间的脚本是隔离的,一个域的脚本不能访问和操作另外一个域的绝大部分属性和方法。只有当两个域具有相同的协议,相同的主机,相同的端口时,我们就认定他们是相同的域。可是在实际开发中我们经常需要获取其他域的资源,这个时候各种不同的跨域资源方式就各显神通了,今天主要来总结一下工作中常用的几种跨域方式,以备查询。
1.window.name
window 对象的name属性是一个很特别的属性,当在 frame 中加载新页面时,name 的属性值依旧保持不变。那么我们可以在页面 a中用iframe加载其他域的页面b,而页面b中用javascript把需要传递的数据赋值给window.name,iframe加载完成之后,此时 name 属性值可被获取到,以访问 web 服务发送的信息。但 name 属性仅对相同域名的 frame 可访问。这意味着为了访问 name 属性,当远程 web 服务页面被加载后,必须导航 frame 回到原始域。即页面a修改iframe的地址,将其变成同域的一个地址,然后就可以读出window.name的值了。一旦 name 属性获得,销毁 frame 。这个方式非常适合单向的数据请求,而且协议简单、安全。
页面b(www.jesse.com/data.html)代码如下:
<script type="text/javascript"> window.name = 'i was there!'; // 这里是要传输的数据,大小一般为2m,ie和firefox下可以大至32m左右 // 数据格式可以自定义,如json、字符串 </script>
页面a(www.jack.com/index.html)代码如下:
<script type="text/javascript"> var state = 0, iframe = document.createelement('iframe'), loadfn = function() { if (state === 1) { var data = iframe.contentwindow.name; // 读取数据 console.log(data); //弹出'i wasthere!' (function(){ //获取数据以后销毁这个iframe。 iframe.contentwindow.document.write(''); iframe.contentwindow.close(); document.body.removechild(iframe); })(); } else if (state === 0) { state = 1; // 设置的代理页面使其回原始域 iframe.contentwindow.location = "http://www.jack.com/proxy.html"; } }; iframe.src = 'http://www.jesse.com/data.html'; if (iframe.attachevent) { iframe.attachevent('onload', loadfn); } else { iframe.onload = loadfn; } document.body.appendchild(iframe); </script>
2.具备src的标签
虽然浏览器默认禁止了跨域访问,但并不禁止在页面中用标签的src属性引用其他域的文件。根据这一点,可以方便地通过创建具有src属性的节点方法来实现完全跨域的通信。使用这种原理的跨域方式有以下几种:
动态创建script
例如我要从域a的页面pagea加载域b的数据,那么在域b的页面pageb中我以javascript的形式声明pagea需要的数据,然后在 pagea中用script标签把pageb加载进来,那么pageb中的脚本就会得以执行。
pagea(www.jack.com/index.html)代码如下:
function getdata(data){ //这里是对获取的数据的相关操作 console.log(data); //数据获取到后移除创建的script标签 document.body.removechild(origindata); } var origindata = document.createelement('script'); origindata.src = 'http://www.jesse.com/data.js'; origindata.setattribute("type", "text/javascript"); document.body.appendchild(origindata);
pageb(www.jesse.com/data.js)代码如下:
getdata('这里是远程跨域获取的数据');//数据格式可以自定义,如json、字符串
jsonp
在用$.ajax()获取远程数据时,如果是跨域资源则可以使用jsonp方法,以前一直以为jsonp是ajax的一种,后来才明白他们根本就不是一回事。ajax是以xhr方式请求数据的,而jsonp是以script方式请求数据的,这个就是和上面的动态创建script方式一样。
pagea(www.jack.com/index.html)代码如下:
$.ajax({ //jsonp不支持post方式 type:"get", url:"http://www.jesse.com/data.php", datatype:"jsonp", //自定义的jsonp回调函数名称,默认为jquery自动生成的随机函数名,也可以写"?",jquery会自动为你处理数据 jsonpcallback:"getdata", success: function(data){ console.log(data); }, error: function(){ console.log('fail'); } })
pageb(www.jesse.com/data.js)代码如下:
<?php $callback = $_get['callback'];//得到回调函数名,这里是getdata $data = array('a','b','c');//要返回的数据 echo $callback.'('.json_encode($data).')';//输出 ?>
3.document.domain
对于主域相同而子域不同的例子,可以通过设置document.domain的办法来解决。 具体的做法是可以在http://www.a.com/a.html和http://script.a.com/b.html两个文件中分别加上 document.domain = "a.com";然后通过a.html文件中创建一个iframe,去控制iframe的contentdocument,这样两个js文件之间就可以 “交互”了。当然这种办法只能解决主域相同而二级域名不同的情况
上的a.html
document.domain = 'a.com'; var ifr = document.createelement('iframe'); ifr.src = 'http://script.a.com/b.html'; ifr.style.display = 'none'; document.body.appendchild(ifr); ifr.onload = function(){ var doc = ifr.contentdocument || ifr.contentwindow.document; // 在这里操纵b.html alert(doc.getelementsbytagname("h1")[0].childnodes[0].nodevalue); };
script.a.com上的b.html
document.domain = 'a.com';
4.跨域资源共享(cors)
原理:跨源资源共享(cors)定义一种跨域访问的机制,可以让ajax实现跨域访问。cors允许一个域上的网络应用向另一个域提交跨域ajax请求。实现此功能非常简单,只需由服务器发送一个响应标头即可。它是通过客户端+服务端协作声明的方式来确保请求安全的。服务端会在http请求头中增加一系列http请求参数(例如access-control-allow-origin等),来限制哪些域的请求和哪些请求类型可以接受,而客户端在发起请求时必须声明自己的源(orgin),否则服务器将不予处理,如果客户端不作声明,请求甚至会被浏览器直接拦截都到不了服务端。服务端收到http请求后会进行域的比较,只有同域的请求才会处理。
pagea(www.jack.com/index.html)代码如下:
var xhr = new xmlhttprequest(); xhr.onreadystatechange = function(){ if(xhr.readystate === 4 && xhr.status === 200){ console.log(xhr.responsetext); } }; xhr.open("get","http://www.jesse.com/data.php"); xhr.send(null);
pageb(www.jesse.com/data.php)代码如下:
<?php header("access-control-allow-origin: http://www.jack.com");//与简单的请求相同 header("access-control-allow-methods: get, post");//允许请求的方法 header("access-control-max-age: 3628800"); //将这个请求缓存多长时间 $data = array('a','b','c');//要返回的数据 echo json_encode($data);//输出 ?>
5.window.postmesage 不常用
window.postmessage(message,targetorigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前ie8+、firefox、chrome、opera等浏览器都已经支持window.postmessage方法。
pagea(www.jack.com/index.html)代码如下:
<iframe id="proxy" src="http://www.jesse.com/index.html" onload="postmsg()" style="display: none"></iframe> <script type="text/javascript"> var obj = { msg: 'hello world' } function postmsg() { var iframe = document.getelementbyid('proxy'); var win = iframe.contentwindow; win.postmessage(obj, 'http://www.jesse.com'); } </script> pageb(www.jesse.com/data.php)代码如下: <script type="text/javascript"> window.onmessage = function(e) { console.log(e.data.msg + " from " + e.origin); } </script>
6. location.hash 不常用
pagea(www.jack.com/index.html)代码如下:
function startrequest() { var ifr = document.createelement('iframe'); ifr.style.display = 'none'; ifr.src = 'http://www.jesse.com/b.html#sayhi'; //传递的location.hash document.body.appendchild(ifr); } function checkhash() { try { var data = location.hash ? location.hash.substring(1) : ''; if (console.log) { console.log('now the data is ' + data); } } catch (e) {}; } setinterval(checkhash, 5000); window.onload = startrequest;
pagea(www.jack.com/proxy.html)代码如下:
parent.parent.location.hash = self.location.hash.substring(1);
pageb(www.jesse.com/b.html)代码如下:
function checkhash() { var data = ''; //模拟一个简单的参数处理操作 switch (location.hash) { case '#sayhello': data = 'helloworld'; break; case '#sayhi': data = 'hiworld'; break; default: break; } data && callback('#' + data); } function callback(hash) { // ie、chrome的安全机制无法修改parent.location.hash,所以要利用一个中间的www.a.com域下的代理iframe var proxy = document.createelement('iframe'); proxy.style.display = 'none'; proxy.src = 'http://www.jack/c.html' + hash; // 注意该文件在"www.jack.com"域下 document.body.appendchild(proxy); } window.onload = checkhash;
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。