解决跨域问题的利器——JSONP

阅读:470 2019-03-19 14:42:55 来源:新网

asynchronousjavascriptandxml(ajax)是驱动新一代web站点(流行术语为web2.0站点)的关键技术。ajax允许在不干扰web应用程序的显示和行为的情况下在后台进行数据检索。使用xmlhttprequest函数获取数据,它是一种api,允许客户端javascript通过http连接到远程服务器。ajax也是许多mashup的驱动力,它可将来自多个地方的内容集成为单一web应用程序。

不过,由于受到浏览器的限制,该方法不允许跨域通信。如果尝试从不同的域请求数据,会出现安全错误。如果能控制数据驻留的远程服务器并且每个请求都前往同一域,就可以避免这些安全错误。但是,如果仅停留在自己的服务器上,web应用程序还有什么用处呢?如果需要从多个第三方服务器收集数据时,又该怎么办?

理解同源策略限制

同源策略阻止从一个域上加载的脚本获取或操作另一个域上的文档属性。也就是说,受到请求的url的域必须与当前web页面的域相同。这意味着浏览器隔离来自不同源的内容,以防止它们之间的操作。这个浏览器策略很旧,从netscapenavigator2.0版本开始就存在。

克服该限制的一个相对简单的方法是让web页面向它源自的web服务器请求数据,并且让web服务器像代理一样将请求转发给真正的第三方服务器。尽管该技术获得了普遍使用,但它是不可伸缩的。另一种方式是使用框架要素在当前web页面中创建新区域,并且使用get请求获取任何第三方资源。不过,获取资源后,框架中的内容会受到同源策略的限制。

克服该限制更理想方法是在web页面中插入动态脚本元素,该页面源指向其他域中的服务url并且在自身脚本中获取数据。脚本加载时它开始执行。该方法是可行的,因为同源策略不阻止动态脚本插入,并且将脚本看作是从提供web页面的域上加载的。但如果该脚本尝试从另一个域上加载文档,就不会成功。幸运的是,通过添加javascriptobjectnotation(json)可以改进该技术。

1、什么是jsonp?

要了解jsonp,不得不提一下json,那么什么是json?

jsonisasubsetoftheobjectliteralnotationofjavascript.sincejsonisasubsetofjavascript,itcanbeusedinthelanguagewithnomussorfuss.

jsonp(jsonwithpadding)是一个非官方的协议,它允许在服务器端集成scripttags返回至客户端,通过javascriptcallback的形式实现跨域访问(这仅仅是jsonp简单的实现形式)。

2、jsonp有什么用?

由于同源策略的限制,xmlhttprequest只允许请求当前源(域名、协议、端口)的资源,为了实现跨域请求,可以通过script标签实现跨域请求,然后在服务端输出json数据并执行回调函数,从而解决了跨域的数据请求。

3、如何使用jsonp?

下边这一demo实际上是jsonp的简单表现形式,在客户端声明回调函数之后,客户端通过script标签向服务器跨域请求数据,然后服务端返回相应的数据并动态执行回调函数。

html代码(任一):

html代码

functionjsonpcallback(result){//alert(result);for(variinresult){alert(i+":"+result[i]);//循环输出a:1,b:2,etc.}}varjsonp=document.createelement("script");jsonp.type="text/javascript";jsonp.src="http://crossdomain.com/services.php?callback=jsonpcallback";document.getelementsbytagname("head")[0].appendchild(jsonp);

或者

html代码

functionjsonpcallback(result){alert(result.a);alert(result.b);alert(result.c);for(variinresult){alert(i+":"+result[i]);//循环输出a:1,b:2,etc.}}

javascript的链接,必须在function的下面。

服务端php代码(services.php):

php代码

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)";

如果将上述js客户端代码用jquery的方法来实现,也非常简单。

$.getjson$.ajax$.get

客户端js代码在jquery中的实现方式1:

js代码

$.getjson("http://crossdomain.com/services.php?callback=?",function(result){for(variinresult){alert(i+":"+result[i]);//循环输出a:1,b:2,etc.}});

客户端js代码在jquery中的实现方式2:

js代码

$.ajax({url:"http://crossdomain.com/services.php",datatype:'jsonp',data:'',jsonp:'callback',success:function(result){for(variinresult){alert(i+":"+result[i]);//循环输出a:1,b:2,etc.}},timeout:3000});

客户端js代码在jquery中的实现方式3:

js代码

$.get('http://crossdomain.com/services.php?callback=?',{name:encodeuricomponent('tester')},function(json){for(variinjson)alert(i+":"+json[i]);},'jsonp');

其中jsoncallback是客户端注册的,获取跨域服务器上的json数据后,回调的函数。http://crossdomain.com/services.php?callback=jsonpcallback这个url是跨域服务器取json数据的接口,参数为回调函数的名字,返回的格式为

js代码

jsonpcallback({msg:'thisisjsondata'})

jsonp原理:首先在客户端注册一个callback,然后把callback的名字传给服务器。此时,服务器先生成json数据。然后以javascript语法的方式,生成一个function,function名字就是传递上来的参数jsonp.最后将json数据直接以入参的方式,放置到function中,这样就生成了一段js语法的文档,返回给客户端。客户端浏览器,解析script标签,并执行返回的javascript文档,此时数据作为参数,传入到了客户端预先定义好的callback函数里.(动态执行回调函数)

使用json的优点在于:

json也有一些劣势:

尽管如此,json的优点还是很明显的。他是ajax数据交互的很理想的数据格式。

主要提示:

jsonp是构建mashup的强大技术,但不幸的是,它并不是所有跨域通信需求的万灵药。它有一些缺陷,在提交开发资源之前必须认真考虑它们。

第一,也是最重要的一点,没有关于jsonp调用的错误处理。如果动态脚本插入有效,就执行调用;如果无效,就静默失败。失败是没有任何提示的。例如,不能从服务器捕捉到404错误,也不能取消或重新开始请求。不过,等待一段时间还没有响应的话,就不用理它了。(未来的jquery版本可能有终止jsonp请求的特性)。

jsonp的另一个主要缺陷是被不信任的服务使用时会很危险。因为jsonp服务返回打包在函数调用中的json响应,而函数调用是由浏览器执行的,这使宿主web应用程序更容易受到各类攻击。如果打算使用jsonp服务,了解它能造成的威胁非常重要。

关联:

以上内容,偏概念一些。下面我们直接show出代码。

参考地址http://kb.cnblogs.com/page/139725/

其实网上关于jsonp的讲解有很多,但却千篇一律,而且云里雾里,对于很多刚接触的人来讲理解起来有些困难,小可不才,试着用自己的方式来阐释一下这个问题,看看是否有帮助。

1、一个众所周知的问题,ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、wcf,只要是跨域请求,一律不准;

2、不过我们又发现,web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如

毫无疑问,页面将会弹出一个提示窗体,显示跨域调用成功。

2、现在我们在jsonp.html页面定义一个函数,然后在远程remote.js中传入数据进行调用。

jsonp.html页面代码如下:

varlocalhandler=function(data){alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:'+data.result);};

remote.js文件代码如下:

localhandler({"result":"我是远程js带来的数据"});

运行之后查看结果,页面成功弹出提示窗口,显示本地函数被跨域的远程js调用成功,并且还接收到了远程js带来的数据。很欣喜,跨域远程获取数据的目的基本实现了,但是又一个问题出现了,我怎么让远程js知道它应该调用的本地函数叫什么名字呢?毕竟是jsonp的服务者都要面对很多服务对象,而这些服务对象各自的本地函数都不相同啊?我们接着往下看。

3、聪明的开发者很容易想到,只要服务端提供的js脚本是动态生成的就行了呗,这样调用者可以传一个参数过去告诉服务端“我想要一段调用xxx函数的js代码,请你返回给我”,于是服务器就可以按照客户端的需求来生成js脚本并响应了。

看jsonp.html页面的代码:

//得到航班信息查询结果后的回调函数varflighthandler=function(data){alert('你查询的航班结果是:票价'+data.price+'元,'+'余票'+data.tickets+'张。');};//提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)varurl="http://flightquery.com/jsonp/flightresult.aspx?code=ca1998&callback=flighthandler";//创建script标签,设置其属性varscript=document.createelement('script');script.setattribute('src',url);//把script标签加入head,此时调用开始document.getelementsbytagname('head')[0].appendchild(script);

这次的代码变化比较大,不再直接把远程js文件写死,而是编码实现动态查询,而这也正是jsonp客户端实现的核心部分,本例中的重点也就在于如何完成jsonp调用的全过程。

我们看到调用的url中传递了一个code参数,告诉服务器我要查的是ca1998次航班的信息,而callback参数则告诉服务器,我的本地回调函数叫做flighthandler,所以请把查询结果传入这个函数中进行调用。

ok,服务器很聪明,这个叫做flightresult.aspx的页面生成了一段这样的代码提供给jsonp.html(服务端的实现这里就不演示了,与你选用的语言无关,说到底就是拼接字符串):

flighthandler({"code":"ca1998","price":1780,"tickets":5});

我们看到,传递给flighthandler函数的是一个json,它描述了航班的基本信息。运行一下页面,成功弹出提示窗口,jsonp的执行全过程顺利完成!

4、到这里为止的话,相信你已经能够理解jsonp的客户端实现原理了吧?剩下的就是如何把代码封装一下,以便于与用户界面交互,从而实现多次和重复调用。

什么?你用的是jquery,想知道jquery如何实现jsonp调用?好吧,那我就好人做到底,再给你一段jquery使用jsonp的代码(我们依然沿用上面那个航班信息查询的例子,假定返回jsonp结果不变):

untitledpagejquery(document).ready(function(){$.ajax({type:"get",async:false,url:"http://flightquery.com/jsonp/flightresult.aspx?code=ca1998",datatype:"jsonp",jsonp:"callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)jsonpcallback:"flighthandler",//自定义的jsonp回调函数名称,默认为jquery自动生成的随机函数名,也可以写"?",jquery会自动为你处理数据success:function(json){alert('您查询到航班信息:票价:'+json.price+'元,余票:'+json.tickets+'张。');},error:function(){alert('fail');}});});

是不是有点奇怪?为什么我这次没有写flighthandler这个函数呢?而且竟然也运行成功了!哈哈,这就是jquery的功劳了,jquery在处理jsonp类型的ajax时(还是忍不住吐槽,虽然jquery也把jsonp归入了ajax,但其实它们真的不是一回事儿),自动帮你生成回调函数并把数据取出来供success属性方法来调用,是不是很爽呀?

好啦,写到这里,我已经无力再写下去,又困又累,得赶紧睡觉。朋友们要是看这不错,觉得有启发,给点个“推荐”呗!由于实在比较简单,所以就不再提供demo源码下载了。

没想到上了博客园的头条推荐。看到大家对这篇文章的认可和评论,还是很开心的,这里针对ajax与jsonp的异同再做一些补充说明:

补充

1、ajax和jsonp这两种技术在调用方式上“看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装;

2、但ajax和jsonp其实本质上是不同的东西。ajax的核心是通过xmlhttprequest获取非本页内容,而jsonp的核心则是动态添加