JSONP原理以及示例

同源策略

浏览器的同源策略限制从一个源加载的文档或脚本与来自另一个源的资源进行交互。如果要在js里发起跨域请求获取数据,有两种方式:

  • JSONP;
  • 采用后端代理的方式。

看一个示例:

1)准备两个不同端口的服务:(不同的端口,也是不同域的服务)

接口数据:

前端页面:

2)前端代码:

<%@ page pageEncoding="utf-8" contentType="text/html;charset=UTF-8"  language="java" %>
<html>
<head>
    <title>跨域测试</title>
    <script src="js/jquery-1.7.2.js"></script>
    <script>
        $(document).ready(function () {
            $("#btn").click(function () {
                $.ajax({
                    url: 'http://localhost:9090/student',
                    type: 'GET',
                    success: function (data) {
                        $(text).val(data);
                    }
                });
            });
        });
    </script>
</head>
<body>
    <input id="btn" type="button" value="跨域获取数据" />
    <textarea id="text" style="width: 400px; height: 100px;"></textarea>
</body>
</html>

3)后端接口代码:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setCharacterEncoding("UTF-8");
    response.setContentType("text/html;charset=UTF-8");
    //数据
    List<Student> studentList = getStudentList();
    JSONArray jsonArray = JSONArray.fromObject(studentList);
    String result = jsonArray.toString();
    response.getWriter().write(result);
}

 4)在前段界面上请求数据:

可以看到跨域的请求被浏览器拦截了。

JSONP原理

首先我们需要明白,在页面上直接发起一个跨域的ajax请求是不可以的,但是,在页面上引入不同域上的js脚本却是可以的,就像你可以在自己的页面上使用<img src=""> 标签来随意显示某个域上的图片一样。比如我在8080端口的页面上请求一个9090端口的图片:

可以得出结论:对于<img><script>这类标签中的src属性是可以跨域请求的(可以看作是浏览器给我们留下一个跨域访问的后门)。

1)原理:

JSONP就是利用了<script>标签的src属性发起的跨域请求,由于script标签的作用是用来执行src指定的js代码。那么我们可以跟后端协商一个函数名,后端将要返回的数据作为函数的参数,一起返回给前端,前端事先定义好该函数,这样就完成了跨域请求。

2)JSONP的缺点:

  • 首先,它没有关于JSONP调用的错误处理,一旦回调函数调用失败,浏览器会以静默失败的方式处理。
  • 其次,它只支持GET请求,这是由于该技术本身的特性所决定的。因此,对于一些需要对安全性有要求的跨域请求,JSONP的使用需要谨慎一点了。
  • JSONP不支持用async:false的方法设置同步。

3)JSONP的优点:

由于JSONP对于老浏览器兼容性方面比较良好,因此,对于那些对IE8以下仍然需要支持的网站来说,仍然被广泛应用。不过,针对高级浏览器,建议还是用CORS 方法。

示例:

1)前端代码:

<%@ page pageEncoding="utf-8" contentType="text/html;charset=UTF-8"  language="java" %>
<html>
<head>
    <title>跨域测试</title>
    <script src="js/jquery-1.7.2.js"></script>
    <script>
        //回调函数
        function showData (result) {
            var data = JSON.stringify(result); //json对象转成字符串
            $("#text").val(data);
        }
        $(document).ready(function () {
            $("#btn").click(function () {
                //向头部输入一个脚本,该脚本发起一个跨域请求
                $("head").append("<script src='http://localhost:9090/student?callback=showData'><\/script>");
            });

        });
    </script>
</head>
<body>
    <input id="btn" type="button" value="跨域获取数据" />
    <textarea id="text" style="width: 400px; height: 100px;"></textarea>
</body>
</html>

2)服务端:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setCharacterEncoding("UTF-8");
    response.setContentType("text/html;charset=UTF-8");
    //数据
    List<Student> studentList = getStudentList();
    JSONArray jsonArray = JSONArray.fromObject(studentList);
    String result = jsonArray.toString();
    //前端传过来的回调函数名称
    String callback = request.getParameter("callback");
    //用回调函数名称包裹返回数据,这样,返回数据就作为回调函数的参数传回去了
    result = callback + "(" + result + ")";
    response.getWriter().write(result);
}

说明:当点击"跨域获取数据"的按钮时,添加一个<script>标签,用于发起跨域请求;注意看请求地址后面带了一个callback=showData的参数;showData即是回调函数名称,传到后台,用于包裹数据。数据返回到前端后,就是showData(result)的形式,因为是script脚本,所以自动调用showData函数,而result就是showData的参数。

jquery的jsonp跨域示例:

1、配置dataType:

服务端代码同上,ajax请求时只需配置一个dataType:'jsonp',就可以发起一个跨域请求。

1)前端代码

<%@ page pageEncoding="utf-8" contentType="text/html;charset=UTF-8"  language="java" %>
<html>
<head>
    <title>跨域测试</title>
    <script src="js/jquery-1.7.2.js"></script>
    <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>
</head>
<body>
    <input id="btn" type="button" value="跨域获取数据" />
    <textarea id="text" style="width: 400px; height: 100px;"></textarea>
</body>
</html>

2)效果:

3)说明:

jsonp指定服务器返回的数据类型为jsonp格式。从请求的url上可以看到,自动带了一个callback=xxx,xxx是jquery随机生成的一个回调函数。

jquery配置jsonp后会随机生成回调函数,当返回jsonp的数据后,执行该回调函数,只不过默认jquery会将这个执行动作映射到success中。

2、指定回调函数:

可以通过jsonpCallback属性指定函数的名称,然后显示的将指定的jsonp回到函数写到<script>下(默认属于window对象),或window对象里。

我们都知道,ajax执行成功后会调用success函数,那上面那种指定了jsonp后,会不会调用success呢?答案是会,而且是先执行jsonp的回调函数,再执行ajax的success函数,例如:

<%@ page pageEncoding="utf-8" contentType="text/html;charset=UTF-8"  language="java" %>
<html>
<head>
    <title>跨域测试</title>
    <script src="js/jquery-1.7.2.js"></script>
    <script>
        function showData (data) {
            console.info("调用showData");

            var result = JSON.stringify(data);
            $("#text").val(result);
        }
        $(document).ready(function () {
//            window.showData = function  (data) {
//                console.info("调用showData");
//                var result = JSON.stringify(data);
//                $("#text").val(result);
//            }
            $("#btn").click(function () {
                $.ajax({
                    url: "http://localhost:9090/student",
                    type: "GET",
                    dataType: "jsonp",  //指定服务器返回的数据类型
                    jsonpCallback: "showData",  //指定回调函数名称
                    success: function (data) {
                        console.info("调用success");
                    }
                });
            });
        });
    </script>
</head>
<body>
    <input id="btn" type="button" value="跨域获取数据" />
    <textarea id="text" style="width: 400px; height: 100px;"></textarea>
</body>
</html>

效果:

3、指定回调函数的参数名:

我们可以通过jsonp属性,来指定回调函数的参数名称,默认是callback。

1)前端代码:

<%@ page pageEncoding="utf-8" contentType="text/html;charset=UTF-8"  language="java" %>
<html>
<head>
    <title>跨域测试</title>
    <script src="js/jquery-1.7.2.js"></script>
    <script>
        function showData (data) {
            console.info("调用showData");
            var result = JSON.stringify(data);
            $("#text").val(result);
        }
        $(document).ready(function () {
            $("#btn").click(function () {
                $.ajax({
                    url: "http://localhost:9090/student",
                    type: "GET",
                    dataType: "jsonp",  //指定服务器返回的数据类型
                    jsonp: "theFunction",   //指定参数名称
                    jsonpCallback: "showData",  //指定回调函数名称
                    success: function (data) {
                        console.info("调用success");
                    }
                });
            });
        });
    </script>
</head>
<body>
    <input id="btn" type="button" value="跨域获取数据" />
    <textarea id="text" style="width: 400px; height: 100px;"></textarea>
</body>
</html>

2)后端代码:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setCharacterEncoding("UTF-8");
    response.setContentType("text/html;charset=UTF-8");
    //数据
    List<Student> studentList = getStudentList();
    JSONArray jsonArray = JSONArray.fromObject(studentList);
    String result = jsonArray.toString();
    //前端传过来的回调函数名称
    String callback = request.getParameter("theFunction");
    //用回调函数名称包裹返回数据,这样,返回数据就作为回调函数的参数传回去了
    result = callback + "(" + result + ")";
    response.getWriter().write(result);
}

效果:

4、看看jquery的jsonp是否支持POST方式:

jsonp方式不支持POST方式跨域请求,就算这里指定成POST方式,也会自动转为GET方式;而后端如果设置成POST方式了,那就请求不了了。

jsonp的实现方式其实就是<script>脚本请求地址的方式一样,只是ajax的jsonp对其做了封装,所以可想而知,jsonp是不支持POST方式的。

参考:

https://www.cnblogs.com/chiangchou/p/jsonp.html

猜你喜欢

转载自blog.csdn.net/liuxiao723846/article/details/112649466