jsonp跨域请求原理剖析

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

什么是跨域
在讲jsonp跨域访问之前有必要先讲一下什么是跨域,所谓的跨域就是跨域名,跨端口,跨协议,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。如下:

http://www.123.com/index.html 调用 http://www.456.com/server.php (主域名不同:123/456,跨域)

http://abc.123.com/index.html 调用 http://def.123.com/server.php (子域名不同:abc/def,跨域)

http://www.123.com:8080/index.html 调用 http://www.123.com:8081/server.php (端口不同:8080/8081,跨域)

http://www.123.com/index.html 调用 https://www.123.com/server.php (协议不同:http/https,跨域)

请注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域。

JSONP原理

jsonp是服务器与客户端跨源通信的常用方法之一,具有简单易用,浏览器兼容性好等特点。ajax请求受同源策略影响,不允许进行跨域请求,而script标签src属性中的链接却可以访问跨域的js脚本,利用这个特性,服务端不再返回JSON格式的数据,而是返回一段调用某个函数的js代码,在src中进行了调用,这样实现了跨域。
  1. 客户端利用script标签可以跨域请求资源的性质,向网页中动态插入script标签,来向服务端请求数据。
  2. 服务端会解析请求的url,至少拿到一个回调函数(比如callback=myCallback)参数,之后将数据放入其中返回给客户端。
  3. 当然jsonp不同于平常的ajax请求,它仅仅支持get类型的方式

JSONP具体实现

  1. 普通方法
<!DOCTYPE html>
<html>
<head>
    <title>JSONP跨域</title>
</head>
<body>
<script type="text/javascript">
    function jsonMethod(data){
        alert("age:" + data.age + "name:" + data.name);
    }
</script>
<script type="text/javascript" src="jquery-1.8.3.min.js">
</script>
<script type="text/javascript" src="http://www.practice-zhao.com/remote.js"></script>
</body>
</html>

上面代码在域www.practice.com下,script标签的src指向了不同域的脚本代码。remote.js代码如下:

jsonMethod({
    "age" : 15,
    "name": "John",
})

也就是这段远程的js代码执行了上面定义的函数,弹出了提示框 

jsonp跨域请求原理剖析

下面将前端代码进行修改

<script type="text/javascript">
    function jsonMethod(data){
        alert("age:" + data.age + "name:" + data.name);
    }
</script>
<script type="text/javascript" src="jquery-1.8.3.min.js">
</script>
<script type="text/javascript">
    $(document).ready(function(){
        var url = "http://www.practice-zhao.com/student.php?id=1&callback=jsonMethod";
        var obj = $('<script><\/script>');
        obj.attr("src",url);
        $("#div1").append(obj);
    });
</script>
上面代码添加了一个script标签,src指向跨域的一个php脚本,并且将上面的js函数名作为callback参数传入,那么我们看下后端代码怎么写的:


<?php
$data = array(
    'age' => 20,
    'name' => '张三',
);

$callback = $_GET['callback'];

echo $callback."(".json_encode($data).")";
return;
PHP代码返回了一段JS语句,即
jsonMethod({
    "age" : 15,
    "name": "张三",
})
此时访问页面时,动态添加了一个script标签,src指向PHP脚本,执行返回的JS代码,成功弹出提示框。 
所以JSONP将访问跨域请求变成了执行远程JS代码,服务端不再返回JSON格式的数据,而是返回了一段将JSON数据作为传入参数的函数执行代码。

jquery方法

 <script>
       $(document).ready(function () {

             $("#btn").click(function () {

                $.ajax({
                    url: "http://localhost:9090/student",
                    type: "GET",
                    dataType: "jsonp", //指定服务器返回的数据类型
                     success: function (data) {
                         var result = JSON.stringify(data); //json对象转成字符串
                         $("#text").val(result);
                     }
                });
 
             });
 
         });
     </script>

调用结果如下,注意看下图划线的url

jsonp跨域请求原理剖析

再看看如何指定特定的回调函数回调函数你可以写到<script>下(默认属于window对象),或者指明写到window对象里,看jquery源码,可以看到jsonp调用回调函数时,是调用的window.callback。
  然后看调用结果,发现,请求时带的参数是:callback=showData;调用回调函数的时候,先调用了指定的showData,然后再调用了success。所以,success是返回成功后必定会调用的函数,就看你怎么写了。

 <script>
 7 
 8         function showData (data) {
 9             console.info("调用showData");
10 
11             var result = JSON.stringify(data);
12             $("#text").val(result);
13         }
14 
15         $(document).ready(function () {
16 
17 //            window.showData = function  (data) {
18 //                console.info("调用showData");
19 //
20 //                var result = JSON.stringify(data);
21 //                $("#text").val(result);
22 //            }
23 
24             $("#btn").click(function () {
25 
26                 $.ajax({
27                     url: "http://localhost:9090/student",
28                     type: "GET",
29                     dataType: "jsonp",  //指定服务器返回的数据类型
30                     jsonpCallback: "showData",  //指定回调函数名称
31                     success: function (data) {
32                         console.info("调用success");
33                     }
34                 });
35             });
36 
37         });
38     </script>

另外zepto.js也有提供jsonp实现的api,具体可自行去zepto官网查看。