理解跨域异步请求的 JSON-P

在开篇之前,我们也许知道跨域问题的存在,知道通过服务端开放跨域请求来使API实现跨域访问,甚至也知道JSON-P这种处理方式,然而可能只是基于阅读的基础上,未必有去做过实践,于是JSON-P只是纸上谈兵,的确很多文章都太不“直接了当”。现在,我尝试着用自己的方式来阐述一下,力求做到简明扼要,同时也是对自己知识储备的整理。

跨域

跨域就客户端而言是这样理解的:客户端(浏览器)为安全起见,特意设置了资源请求的门槛——“同源策略”,即不允许在当前域下访问其他来源的资源,这个“域”或“源”在浏览器里表现为实际请求的URL。也就是说,浏览器不允许客户端在访问A站的同时请求B站的资源。域名不同,IP不同,甚至端口号不同都被视为跨域,除非服务端通过HTTP头标识告知客户端放开这一限制。我们的目的是要越过这道坎。

JSON-P

JSON-P是绕过客户端同源策略的方法之一,实现跨域请求是需要前后端技术协作的,是对跨域异步请求的其他方法的补充。它并非一门技术,而是一种技巧。上文说到浏览器设置了同源请求的门槛,然而这种限制通常只针对cookie、本地存储、Ajax、跨域DOM获取等,对静态资源的请求并没有限制,比如在某个网站插入一段来自CDN提供的Bootstrap库文件:

<script src="//cdn.bootcss.com/bootstrap/4.0.0-alpha.5/js/bootstrap.min.js"></script>

src指向的脚本可以被顺利的下载并执行。
  JSON-P将通过这一特性来绕过浏览器的同源限制,配合后端返回加工过的内容以实现跨域请求。

实现

“JSON-P”全名JSON with Padding,很形象的诠释了JSON的获取方式(JSON数据以填充的方式来获取),我们用一个经典的例子来说明:
  假设我们为 A站 后台设计一个API,允许第三方开发人员在 B站 通过这个API得到 A站 返回的形如JSON的数据{"name":"Warren","age":"28"}
  这个API其实是一个纯 js 的文本资源,包含一个带有形参的函数调用语句 getUserInfo(data);,把参数替换为用户信息数据,于是最终API将会返回如下文本:

getUserInfo({"name":"Warren","age":"28"});

同时确保该函数在 B站 客户端存在:

<script>
    function getUserInfo(data){
        alert('Your name is '+data.name);
        alert('You\'re '+data.age+' years old.');
    }
</script>

然后在 B站 页面上加载该资源:

<script src="http://a.com/api.js"></script>

浏览器“出于本能”的立即执行了该资源内的js语句,

当访问 B站 时就会依次弹出两个对话框。就这样,一个JSON-P请求完成了,它的确绕过了同源限制,实现在不同域间数据的交互。值得一提的是,客户端getUserInfo函数里得到的data实际上是JS的对象字面量(理论上可以是任何对象,这取决于服务端传递的内容),因为API返回的文本在浏览器上被当作JS代码解析并立即执行了,于是也就省去了JSON.parse()的步骤。

进阶

发现了没?上面的实现方式和平时在本地写JS几乎一样呀!只不过调用getUserInfo()函数的代码是通过后端书写并在客户端发送请求后返回的。那么问题来了,B站 开发人员希望借助这个API调取除用户信息之外更多的数据,比如天气、在 A站 的收藏……显然一个getUserInfo()是不够的,比较灵活的做法是 A站 后端依据 B站 客户端发出的需求来决定返回什么样的数据(可执行js文本),我们给调用API的链接添加查询参数,并把希望得到的数据类型告诉API:

<script src="http://a.com/api.js?getJSONP=getUserInfo"></script>

或者

<script src="http://a.com/api.js?getJSONP=getWeather"></script>

……

API根据查询参数getJSONP的值判断客户端需要什么样的数据,填充进对应的函数并返回给客户端执行。这就能让一个API满足多种跨域请求。相应地,客户端还是免不了要设计对应的数据处理函数的。

异步

原理已经掌握,我们可以来点更有趣的,比如异步请求,聪明的读者一定猜到了——不就是动态创建script标签或者改变已有标签的src属性值么?没错!封装一个用原生JS创建script标签的函数:

    function getData(src){
        var script = document.createElement('script');
        script.src = src;
        document.body.appendChild(script);
    }

在任何时候调用它:

getData('http://a.com/api.js?getJSONP=getUserInfo');

建议在数据成功获取后保存数据到一个新的变量,并移除空闲的script标签,最大限度的防治污染。

总结

JSON-P作为一种非标准的跨域请求实现有着先天的缺陷,它虽然巧妙,但实现起来繁琐怪异。一个现成的库来实现它再好不过了,比如JQuery的jsonp。然而仍难弥补只能接受GET请求和存在的安全隐患的缺点。未来JSON-P也许会被正式的跨域技术标准取代(比如CORS),但因为IE及其它老旧的浏览器的存在,新标准的超前使得JSON-P仍有存在的必要。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容