跨域之法(二)—— JSONP

jsonp可能是很多人第一次跨域使用的一种方法,使用起来比较简单,而且也不绕。

jsonp全称 json padding,为什么?接下来你就知道了

先说说JSONP是怎么产生的

  1. 当我们请求不在当前域下的文件的时候,无论是静态页面、动态网页、web服务、WCF,只要是不同源的,就会被认定为跨域,全都是失败的

  2. 不知道你有没有发现过,Web页面上调用有src属性的文件时则,不受是否跨域的影响

  3. 于是可以判断,我们可以在远程的服务器上面把数据写在js文件中,让我们页面去调用那个js文件

  4. 然后我们就结合了json这个天然的数据装载,让客户端通过与调用脚本的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀)

  5. 服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。

  6. 客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,也就是json+padding的时候了

  7. 为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作jsonp,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住json数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

如何跨域

简单调用函数

我们知道,哪怕跨域js文件中的代码(当然指符合web脚本安全策略的),web页面也是可以无条件执行的。(下面的案例是在本地搭建不同端口的服务器上测试的)

本地服务器 http://localhost:8026/cross 下有个jsonp.html页面代码如下:

1
2
3
4
5
6
7
8
9
<!DOCTYPE>
<html>
<head>
<title>JSONP Page</title>
<script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
</head>
<body>
</body>
</html>

远程服务器 http://localhost:8027/cross 根目录下有个 remote.js 文件代码如下:

1
alert('我是远程文件');

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

利用调用函数获得返回

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

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE>
<html>
<head>
<title>JSONP Page</title>
<script type="text/javascript">
var localHandler =function(data){
alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:'+ data.result);
};
</script>

<script type="text/javascript" src="http://localhost:8027/cross/remote.js"></script>
</head>
<body></body>
</html>

remote.js文件代码如下:

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

运行之后查看结果,页面成功弹出提示窗口,显示本地函数被跨域的远程js调用成功,并且还接收到了远程js带来的数据。
很欣喜,跨域远程获取数据的目的基本实现了。

但是又一个问题出现了,怎么让远程js知道它应该调用的本地函数叫什么名字?毕竟是jsonp的服务者都要面对很多服务对象,而这些服务对象各自的本地函数都不相同啊?我们接着往下看。

调用动态生成的js

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

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE>
<html>
<head>
<title>JSONP Page</title>
<script type="text/javascript">
function localHandler(data) {
alert('我是本地函数,通过后台回调回来的数据是:\n'+ data.result);
};
</script>

<script type="text/javascript" src="http://localhost:8027/cross/test.php?callback=localHandler"></script>
</head>
<body></body>
</html>

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

callback参数则告诉服务器,我的本地回调函数叫做localHandler,所以请把查询结果传入这个函数中进行调用

服务器会把这个叫做 http://localhost:8027/cross/test.php 的页面生成了一段这样的代码提供给jsonp.html(服务端的数据是用php写的, 其实就是字符串拼接):

1
2
3
4
5
6
<?php
header("Content-Type:text/javascript");
$callback = $_GET["callback"];
$a = $callback."({result:\"我是远程js带来的数据\"});";
echo $a;
?>

我们看到,传递给localHandler函数的是一个json,运行一下页面,成功弹出提示窗口,jsonp的执行全过程顺利完成!

jQuery的jsonp

jquery的jsonp与js略有不同,可以在键值对里面表明callback和回调函数的名字,如果回调函数名字不确定,可以用“?”代替

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE>
<html>
<head>
<title>JSONP Page</title>
<script type="text/javascript" src="jquery.min.js">
</script><script type="text/javascript">
jQuery(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');
}
});
});
</script>

</head>
<body></body>
</html>

jquery在处理jsonp类型的ajax时自动帮你生成回调函数并把数据取出来供success属性方法来调用.

但是jsonp和ajax并不是一回事。

文章目录
  1. 1. 先说说JSONP是怎么产生的
  2. 2. 如何跨域
    1. 2.1. 简单调用函数
    2. 2.2. 利用调用函数获得返回
    3. 2.3. 调用动态生成的js
  3. 3. jQuery的jsonp