原生js的JSONP跨域请求

时间:2022-11-21 21:53:55

但到目前为止最被推崇或者说首选的方案还是用JSON来传数据,靠JSONP来跨域。

JSONP跨域GET请求是常用的解决方案。
在进行一些比较深入的前端操作时,不可避免的要进行跨域操作,但是
基于安全的原因,浏览器是存在“同源策略“这个机制,同源策略阻止从一个源加载的文档或脚本获取或设置另一个源加载的文档的属性。即对js能够访问的页面的内容加以限制,只能访问与包含它的文档在同一域中的内容。

为了能够访问其他域中的内容,所以就出现了jsonp。

首先我们想一下,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>);在js的<script>标签中似乎不会涉及到跨域的问题,所以我们就利用<script>标签将向不同域提交HTTP请求。这种技术就叫做jsonp。

既然它叫jsonp,很明显目的还是json,而且是跨域获取。实现策略就是利用javascript构造一个script标签,把json的url赋给script的scr属性,把这个script插入到dom里,让浏览器去获取。实践:

比如远程的服务器aaa.com的根目录下有一个名叫a.js的文件。
内容为:

alert("Hello World !");

本地服务器localServer.com 下有一个名为index.html的文件。要访问aaa.com的根目录的a.js的文件:

只需要在index.html文件的head标签中加入

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

就可以进行跨域访问。

但接下来,我们在本地的index.html文件中定义一个处理函数,然后在a.js中传入数据进行调用。

var localHandler = function(data){
alert('我是本地函数,可以被跨域的a.js文件调用,远程js带来的数据是:' + data.result);
};
<script src="http://aaa.com/a.js"></script>

而在a.js中代码如下:

localHandler({“result”:”我是远程js带来的数据”});

这样很开心,我们也能得到理想的结果。

但是怎么让远程js知道它应该调用的本地函数叫什么名字呢?毕竟是jsonp的服务者都要面对很多服务对象,而这些服务对象各自的本地函数都不相同。

所以我们只要服务端提供的js脚本是动态生成的就行了呗,这样调用者可以传一个参数过去告诉服务端“我想要一段调用XXX函数的js代码,请你返回给我”,于是服务器就可以按照客户端的需求来生成js脚本并响应了。
  
 看index.html页面中script标签的代码:
 复制的代码:

// 得到航班信息查询结果后的回调函数
var flightHandler = function(data){
alert(‘你查询的航班结果是:票价 ’ + data.price + ’ 元,’ + ‘余票 ’ + data.tickets + ’ 张。’);
};

// 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)

var url = “http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler“;

// 创建script标签,设置其属性
var script = 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的页面生成了一段这样的代码提供给index.html(服务端的实现这里就不演示了,与你选用的语言无关,说到底就是拼接字符串):
  
flightHandler({
“code”: “CA1998”,
“price”: 1780,
“tickets”: 5
});
 
  我们看到,传递给flightHandler函数的是一个json,它描述了航班的基本信息。运行一下页面,成功弹出提示窗口,jsonp的执行全过程顺利完成!

最后再来简单的梳理一下:

比如远程的服务器aaa.com的根目录下有一个名叫a.js的文件。
内容为:
{“Name”:”张三”, “Age”:23, “Sex”: “男”}

本地的服务器localServer.com的index.html的<script>标签中去请求a.js中的内容:

// 处理函数
var localHandler = function(data){
alert(‘姓名:’+data.Name);
};

// 提供jsonp服务的url地址

var url = “http://aaa.com/a.js?callback=localHandler“;

// 创建script标签,设置其属性
var script = document.createElement(‘script’);
script.setAttribute(‘src’, url);

// 把script标签加入head,此时调用开始
document.getElementsByTagName(‘head’)[0].appendChild(script);

首先,第一个浏览器,http://aaa.com.a.js这个Url的确是存在一个json的,而且在本地的网页上用script标签来请求这个Url也是200OK的,但是最下面报js语法错误了。原来用script标签加载完后,会立即 把响应当js去执行,很明显{“Name”:”张三”, “Age”:23, “Sex”: “男”}不是合法的js语句。

所以如果存在localHandler这个方法,那么localHandler({“Name”:”张三”, “Age”:23, “Sex”: “男”})就是合法的js语句。

由于服务器不知道客户端的回调是什么,不可能hard code成jsonpcallback,所以就带一个QueryString让客户端告诉服务端,回调方法是什么,当然,QueryString的key要遵从服务端的约定,上面的是”callback“。

所以一句话就是利用script标签绕过同源策略,获得一个类似这样的数据,localHandler是页面存在的回调方法,参数就是想得到的json。