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

作者: 错码匠 | 来源:发表于2017-01-07 20:22 被阅读219次

在开篇之前,我们也许知道跨域问题的存在,知道通过服务端开放跨域请求来使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将会以文本形式返回一个带有入参的函数调用getUserInfo(data),把用户信息填充到入参中,于是最终API将会返回如下文本:

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

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

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

然后在 B站 页面上调用这个API:

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

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

进阶

发现了没?上面的实现方式和平时在本地写JS几乎一样呀!只不过调用getUserInfo()函数的代码是通过后端书写并在客户端发送请求后返回的。那么问题来了,B站 开发人员希望借助这个API调取除用户信息之外更多的数据,比如天气、在 A站 的收藏……显然一个getUserInfo()是不够的,比较灵活的做法是 A站 后端依据 B站 客户端发出的需求来决定返回什么样的数据,我们给调用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仍有存在的必要。

相关文章

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

    在开篇之前,我们也许知道跨域问题的存在,知道通过服务端开放跨域请求来使API实现跨域访问,甚至也知道JSON-P这...

  • vue 异步请求

    vue-router 异步请求 在实际的应用开发中,与后端交互,进行异步请求是很常见的需求 axios 请求 跨域...

  • axios发送俩次请求的原因

    其实跨域分为简单跨域请求和复杂跨域请求 简单跨域请求是不会发送options请求的 复杂跨域请求会发送一个预检请求...

  • AJAX出现两次请求 options和get|post

    跨域请求 允许跨域请求 preflighted request预请求(options) 跨域请求 XMLHttpR...

  • 2017-04-12 工作总结

    工作:jQuery 跨域请求 jQuery请求示例 异步响应 替换内容的Img标签的Src路径 过滤HTML 标签

  • 异步请求,跨域请求ajax

    调用

  • JavaScript的异步线程

    异步网络请求 在JavaScript中如何请求服务器,并且异步的回调呢?那就是用AJAX。 跨域安全限制 默认情况...

  • 用express实现CORS跨域

    跨域请求头 cors express 跨域请求

  • Rails 跨域异步请求

    我们知道因为浏览器的『同源策略』策略,不同域名下的资源是不能共享的,尤其是我们在web开发中最常用的ajax请求,...

  • jsonp

    参考:轻松搞定JSONP跨域请求参考:JavaScript 跨域总结与解决办法要理解跨域,先要了解一下“同源策略”...

网友评论

    本文标题:理解跨域异步请求的 JSON-P

    本文链接:https://www.haomeiwen.com/subject/idsfbttx.html