跨域解决方案JSONP

来源:互联网 发布:淘宝账号管理在哪 编辑:程序博客网 时间:2024/06/05 20:05

什么是跨域

老生常谈的问题了。下面列出一个表格:

URL 说明 是否跨域 http://www.a.com/a.js http://www.a.com/b.js 同一个域名 否 http://www.a.com/lab/a.js http://www.a.com/script/b.js 同一个域名,不同文件夹 否 http://www.a.com:8080/a.js http://www.a.com/b.js 同一个域名,不同端口 是 http://www.a.com/a.js http://79.23.1.21/b.js 域名与域名对应IP 是 http://www.a.com/a.js http://script.a.com/b.js 主域和子域 是 http://www.a.com/a.js http://a.com/b.js 一级域名和二级域名 是 http://www.XXX.com/a.js http://www.a.com/b.js 不同域名 是 http://localhost/a.js http://127.0.0.1/b.js 本地和本地对应的IP 是

牢记:只要协议schema ,域名,子域名,端口有一个不同的都是跨域!

而且跨域问题上,域仅仅是通过URL的首部来识别的,而不会去尝试判断两个域是否在同一个IP上(这涉及到DNS解析)

下面介绍几种能实现跨域的方法

图像Ping

JS高程提到的一个方法,利用的是img的src可以跨域。

var img= new Image();img.onload=img.onerror=function(){    console.log('Done~!');};img.src='http://www.example.com/test?name=Nicolas';

这个方法唯一的能力就是可以知道是否接受到了响应,但是浏览器无法得到任何信息,但是可以请求服务器,比如可以请求服务器做一个删除的操作,或者创建的操作,因为querystring可以通过img.src提交给服务器。

仅仅是 浏览器 –> 服务器

1.只能发送get请求,并且无法获得任何服务器的响应。2. 仅仅是单向的数据传递,浏览器-->服务器

JSONP

JSON with Padding
JSONP 两部分组成: 回调函数+数据
JSONP中的回调函数
响应到来的时候,应该在页面中调用的函数。
JSON就是数据
数据是传入回调函数中的JSON数据。

JSONP的原理

动态添加一个<script>标签,而script标签的src属性是没有跨域的限制的。 和img的src一样可以不受限制从其他域加载资源。我们不能跨域请求数据,但是可以引入不同域的脚本文件。 所以我们JSONP中,src是我们请求服务器的url,一般是这样的形式:

http://crossdomain.com/jsonServerResponse?jsonp=jsonpCallback

用法

动态script元素来使用,用js动态生成script标签来进行跨域操作了。

<script type="text/javascript">    function jsonpCallback(result) {        alert(result.a);        alert(result.b);        alert(result.c);        for(var i in result) {            alert(i+":"+result[i]);//循环输出a:1,b:2,etc.        }    }</script><script type="text/javascript" src="http://crossdomain.com/services.php?callback=jsonpCallback"></script>

我们看到,我们在客户端做的事情,就是定义一个回调函数,在请求完毕后执行它,然后在这个函数里面处理接收到的响应数据。
看到URL请求参数,也可以发现,我们需要将函数名传递给callback这个请求参数。

分析

其实这里叫callback还是jsonp是由服务端决定的,服务端会根据接收到的这个请求,生成所需的json数据,当然你还可以附加一大堆请求参数到url上。然后服务端根据这些参数生成json数据传入到回调函数中。

返回的格式如:
jsonpCallback({msg:”xxx”})

执行流程

客户端注册一个callback,这里是jsonpCallback,然后把callback的名字传给服务器,服务端得到这个函数名之后,要用这样的方法

jsonpCallback(...)

包裹要输出的json内容。 此时服务器生成的json数据才能被客户端正确接收。
然后以JS语法的方式生成一个function,function的名字就是传递上来的参数callback的值,这里的值”jsonCallback”
最后将json数据直接入参的方式,放置到function中,这样就生成了一段js语法的文档,并返回给客户端。
返回一个script文档

<script>...</script>

客户端解析script标签,并执行返回的js文档,此时js文档数据,作为参数传入到了客户端预先定义好的callback函数里。

可以说jsonp的方式原理上和<script src=”http://跨域/…xx.js”&ht;</script>是一致的(qq空间就是大量采用这种方式来实现跨域数据交换的)。JSONP是一种脚本注入(Script Injection)行为,所以有一定的安全隐患。

服务端代码可能是这样的

<?php//服务端返回JSON数据$arr=array('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5);$result=json_encode($arr);//echo $_GET['callback'].'("Hello,World!")';//echo $_GET['callback']."($result)";//动态执行回调函数$callback=$_GET['callback'];//得到回调函数echo $callback."($result)";//输出,result作为参数传递

服务端做的事
我们其实是要querystring,获取url中传递的参数,这里是callback,然后把callback的值’jsonpCallback’包裹要传递的json字符串。也就像这样:
jsonpCallback({“name”:”xxx”,”id”:23});

返回的就是用这个callback的值包裹的json数据。
然后这个回调函数就会被执行。因为你创建了script标签然后函数在里面被调用执行了。

jQuery的实现

原理是一样的,只不过我们不需要手动的插入script标签以及定义回掉函数。
jquery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用
$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。

jQuery源码部分:

//Build temporary JSONP functionif( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {    jsonp = s.jsonpCallback || ("jsonp" + jsc++);    // Replace the =? sequence both in the query string and the data    if ( s.data ) {    s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");    } /*...*/ if ( s.cache === false && type === "GET" ) {var ts = now();// try replacing _= if it is therevar ret = s.url.replace(rts, "$1_=" + ts + "$2");// if nothing was replaced, add timestamp to the ends.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");}// If data is available, append data to url for get requestsif ( s.data && type === "GET" ) {s.url += (rquery.test(s.url) ? "&" : "?") + s.data;}// Watch for a new set of requestsif ( s.global && ! jQuery.active++ ) {jQuery.event.trigger( "ajaxStart" );}// Matches an absolute URL, and saves the domainvar parts = rurl.exec( s.url ),remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);// If we're requesting a remote document// and trying to load JSON or Script with a GETif ( s.dataType === "script" && type === "GET" && remote ) {var head = document.getElementsByTagName("head")[0] || document.documentElement;var script = document.createElement("script");script.src = s.url;// Handle Script loadingif ( !jsonp ) {var done = false;// Attach handlers for all browsersscript.onload = script.onreadystatechange = function() { if ( !done && (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") ) { done = true; success(); complete(); // Handle memory leak in IE script.onload = script.onreadystatechange = null; if ( head && script.parentNode ) { head.removeChild( script ); } }};}// Use insertBefore instead of appendChild to circumvent an IE6 bug.// This arises when a base node is used (#2709 and #4378).head.insertBefore( script, head.firstChild );// We handle everything using the script element injectionreturn undefined; }

jQuery一开始先判断JSON类型的调用,然后为本次调用创建了临时的JSONP方法,并且添加了一个随机的数字(源于日期值)
然后创建了script标签,构造这个script片段,并且追加到了原文档的head标签后面。
还有就是判断浏览器的脚本onreadystatechange事件,判断readyState是loaded或complete两个都要判断。
NOTE
因为无论是哪个属性都表示资源已经可用了。有时候readyState会停在“loaded”有时候会跳过”loaded”直接完成)

JSONP的优点

  1. JSON可读性好,在JS中容易处理
  2. 比XML轻了很多
  3. PHP对JSON的支持也不错

JSONP的弊端

JSONP很好用,但是它有如下的缺点:

  1. JSONP是从其他域中加载代码执行的,如果其他域不安全(服务器端不安全),很有可能在响应中夹带一些恶意代码,此时除了完全放弃JSONP调用外没有办法。
  2. 其次,确定JSONP是否请求成功不容易。因为如果动态脚本插入有效那么就执行调用,如果无效就静默失败,失败没有任何提示~
  3. 用eval()解析也是容易出现安全问题
0 0
原创粉丝点击