一直觉得jsonp很好用,却不知道原理,跨越技术有点多,在这探索解释一番,望有所获
同源策略
在JavaScript中,有一个很重要的安全性限制,被称为“Same-Origin Policy”(同源策略)。这一策略对于JavaScript代码能够访问的页面内容做了很重要的限制,即JavaScript只能访问与包含它的文档在同一域下的内容。(图片来自MDN)
JSONP
在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但是,在页面上用<script>
标签引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的。
-
jsonp的客户端实现
tocat服务器上有个remote.js(这里用tocat,web项目演示)
alert("跨域成功");
本地index.html文件
<!DOCTYPE> <html> <head> <title>跨域资源</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> /* 完成跨域 */ <script src="http://localhost:8080/web/js/remote.js"></script> </head> <body> </body> </html>
很明显,显示跨域成功 - 接下来我们在本地index.html定义callback函数 ,然后远程tomcat.js传入回调数据,
index.html
<!DOCTYPE>
<html>
<head>
<title>跨域资源</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
/* 完成跨域 */
<script type="text/javascript">
window.onload = function() {
var callback = function(data) {
alert("跨域调用远程tomcat.js成功: " + data.name);
}
}
</script>
<script src="http://localhost:8080/web/js/tomcat.js"></script>
</head>
<body>
</body>
</html>
远程服务器:
tomcat.js
callback({"name":"kk"});
到此,跨域基本实现,但是这里有个问题,<font color=red>远程站点怎么知道要调用哪个callback呢?</font>
所以就有了jsonp的核心:服务端提供的callback是动态生成的, 本地只要动态传一个callback给服务端,服务端就知道该调用哪个了
像这样:
客户端
<!DOCTYPE>
<html>
<head>
<title>跨域资源</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<script type="text/javascript">
window.onload = function() {
var callback = function(data) {
alert("跨域调用远程成功: " + data.name);
}
var url = "http://localhost:8080/web/js/helloServlet?userid=1&callback=callback";
// 创建script标签,设置其属性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把script标签加入head,此时调用开始
document.getElementsByTagName('head')[0].appendChild(script);
}
</script>
</head>
<body>
</body>
</html>
服务端:(这里选用java解释)
// 从客户端获取参数userid和callback
PrintWriter out =response.getWriter();
// 根据userid查找到的数据存储为json,
out.println(callback+"("+json+")");
// 返回给客户端的代码,callback(json)
jsonp的执行全过程就是这么回事了(个人理解)
- 现在再来看JQuery中的实现,我们最经常用的
$.ajax
,似乎通透了不少,也就是这么回事
$(function() {
$.ajax({
async: false,
url: http, //跨域url地址
type: "GET",
dataType: 'jsonp',
jsonp: 'jsoncallback',
data: {
"userid": "1"
},
timeout: 5000,
beforeSend: function() {
//jsonp 方式此方法不被触发.原因可能是dataType如果指定为jsonp的话,就已经不是ajax事件了
},
success: function(json) { //客户端jquery预先定义好的callback函数,成功获取跨域服务器上的json数据后,会动态执行这个callback函数
}
error: function(xhr) {
//jsonp 方式此方法不被触发.原因是:dataType如果指定为jsonp的话,就已经不是ajax事件了
//请求出错处理
alert("请求出错(请检查相关度网络状况.)");
}
});
})
这里发现,$.ajax
和ajax
并不是一回事啊,<font color=red>ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。</font>
window.name
注意,window.name的值只能是字符串的形式,这个字符串的大小最大能允许2M左右甚至更大的一个容量,具体取决于不同的浏览器,但一般是够用了。
实例:
假设有三个页面:
http://a.com/a.html //渴望获得数据的页面
http://a.com/b.html //a的兄弟页面,充当中间人角色
http://b.com/data.html //不同源的数据页
-
http://a.com/a.html
,监听iframe的onload事件,在此事件中设置这个iframe的src指向本地域的代理页面http://a.com/b.html
(代理文件和应用页面在同一域下,所以可以相互通信)
动态创建iframe<script type="text/javascript"> var state = 0, iframe = document.createElement('iframe'); // 通过iframe的src与data.html通信 iframe.src = 'http://b.com/data.html'; //监听onload if (iframe.attachEvent) { iframe.attachEvent('onload', loadfn); } else { iframe.onload = loadfn; var loadfn = function() { if (state === 0) { state = 1; iframe.contentWindow.location = "http://a.com/b.html"; // 设置的代理页面 } else if (state === 1) { var data = iframe.contentWindow.name; // 读取数据 alert(data); //弹出'data.html!' } }; </script>
http://b.com/data.html
<script type="text/javascript"> window.name = 'data.html'; </script>
这样就可以成功解决了,理一下思路,应该是这样:
<font color=red>充当中间人的iframe想要获取到data.html的通过window.name设置的数据,只需要把这个iframe的src设为www.cnblogs.com/data.html就行了。然后a.html想要得到iframe所获取到的数据,也就是想要得到iframe的window.name的值,还必须把这个iframe的src设成跟a.html页面同一个域才行</font>
图示:
HTML5中新引进的window.postMessage方法
在HTML5中新增了postMessage方法,postMessage可以实现跨文档消息传输(Cross Document Messaging),Internet Explorer 8, Firefox 3, Opera 9, Chrome 3和 Safari 4都支持postMessage。
实现:
发送信息的页面http://a.com/a.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>客户端</title>
<script type="text/javascript">
window.onload = function() {
window.frames[0].postMessage('data', 'http://b.com/data.html');
//postMessage接收两个参数,第一个数据,第二个字符串参数,指明目标窗口的源,具体看MDN
}
</script>
</head>
<body>
<iframe id="child" src="http://b.com/data.html"></iframe>
</body>
</html>
接收信息的页面http://b.com/data.html
// 通过监听message事件获取数据
window.addEventListener('message',function(e){
var e =e||event;
alert(e.data);
},false);
图示:
跨域资源共享(CORS)
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。具体原理参见MDN
- 当然:还有document.domain......
- 以上十个人理解,ps: 欢迎指正
- CSDN发布地址