zoukankan      html  css  js  c++  java
  • 使用JSONP实现跨域

    跨域的实现方式有多种,除了 上篇文章 提到的CORS外,常见的还有JSONP、HTML5、Flash、iframe、xhr2等。

    这篇文章对JSONP的跨域原理进行了探索,并将我的心得记录在这里和大家分享。

    JSONP跨域原理探秘

    我们知道,使用 XMLHTTPRequest 对象发送HTTP请求时,会遇到 同源策略 问题,域不同请求会被浏览器拦截。

    那么是否有方法能绕过 XMLHTTPRequest 对象进行HTTP跨域请求呢?

    换句话说,不使用 XMLHTTPRequest 对象是否可以发送跨域HTTP请求呢?

    细心的你可能会发现,像诸如:

    <script type="text/javascript" src="http://www.a.com/scripts/1.js"></script>

    <img src="http://www.b.com/images/1.jpg" />

    <link rel="stylesheet" href="http://www.c.com/assets/css/1.css" />

    这种标签是不会遇到"跨域"问题的,严格意义上讲,这不是跨域,跨域是指在脚本代码中向非同源域发送HTTP请求,这只是跨站资源请求。

    那么,我们是否可以利用跨站资源请求这一方式来实现跨域HTTP请求呢?

    以<script></script>标签为例进行探索,先看一段代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <!DOCTYPE html>
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>jsonp demo</title>
        <!-- JavaScript片断1 -->
        <script type="text/javascript">
            function handler(data) {
                alert(data);
                // our code here...
            }
        </script>
     
        <!-- JavaScript片断2 -->
        <script type="text/javascript">
            handler('success');
        </script>
    </head>
    <body>
        A JSONP demo.
    </body>
    </html>

    这段代码中,有2个JavaScript片断,第1个片断中定义了一个处理函数handler(),这个处理函数比较简单,没有对数据做任何处理,只是把它alert出来;第2个片断调用了它,运行这个页面浏览器会弹出"success"。

    我们假设第2个JavaScript片断存储在别的地方,然后我们使用<script src="" />的方式把它引入进来,像这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <!DOCTYPE html>
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>jsonp demo</title>
        <!-- JavaScript片断1 -->
        <script type="text/javascript">
            function handler(data) {
                alert(data);
                // our code here...
            }
        </script>
     
        <!-- JavaScript片断2 -->
        <script type="text/javascript" src="http://service.a.com/script/1.js"></script>
    </head>
    <body>
        A JSONP demo.
    </body>
    </html>

    service.a.com/script/1.js:

    1
    handler('success');

    这种方法和把JavaScript代码直接写在页面是等效的,但是,我们由此可以联想到什么?

    我们是否可以事先在本页面定义处理程序,服务端返回JS脚本,脚本的内容就是对处理程序的回调,服务返回的数据通过参数的形式传回:

    handler('服务返回的数据');

    然后通过动态向当前页面head节点添加<script src="服务地址"></script>节点的方式来“伪造”HTTP请求?

    于是,可以编写这样一个简单的测试用例:

    先写服务端,非常简单的一个服务,返回字符串"Hello World",一般处理程序Service.ashx:

    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
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
     
    namespace JSONPDemo.Service
    {
        /// <summary>
        /// Service2 的摘要说明
        /// </summary>
        public class Service2 : IHttpHandler
        {
     
            public void ProcessRequest(HttpContext context)
            {
                context.Response.ContentType = "text/plain";
                context.Response.Write("handler('Hello World');");
            }
     
            public bool IsReusable
            {
                get
                {
                    return false;
                }
            }
        }
    }

    再写客户端,一个简单的静态Web页,index.html:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title></title>
        <script type="text/javascript">
            // 跨域发送HTTP请求,从服务端获取字符串"Hello World"
            function getHello() {
                var script = document.createElement('script');
                script.setAttribute('src', 'http://localhost:8546/Service.ashx');
                document.querySelector("head").appendChild(script);
            }
            // 处理函数
            function handler(data) {
                alert(data);
                // our code here...
            }
        </script>
    </head>
    <body>
        <input type="button" value="发送跨域HTTP请求,获取Hello World" onclick="getHello()" />
    </body>
    </html>

    测试成功!

    在这个测试例子中,我们使用一般处理程序编写了一个简单的返回Hello World的服务,然后使用动态创建<script></script>节点的方式实现了跨域HTTP请求。

    由于<script>、<img>标签资源请求是异步的,所以我们就实现了一个跨域的异步HTTP请求。

    但是这么做是不够的,一个页面可能会有多个HTTP请求,而上面这个示例的处理程序只有一个——handler。

    不同的请求应该由不同的处理程序来处理,对上面的代码稍做修改,只需要给<script>标签的src属性中的URL添加一个参数来指定回调函数的名称就可以了:

    服务端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/plain";
     
        // 前端指定的回调函数名称
        var callbackFuncName = context.Request.QueryString["callback"];
        var responseData = "Hello World";
        // 回调脚本,形如:handler('responseData');
        var scriptContent = string.Format("{0}('{1}');", callbackFuncName, responseData);
        context.Response.Write(scriptContent);
    }

    Web客户端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title>jsonp demo</title>
        <script type="text/javascript">
            // 跨域发送HTTP请求,从服务端获取字符串"Hello World"
            function getHello() {
                var script = document.createElement('script');
                script.setAttribute('src', 'http://localhost:8546/Service.ashx?callback=handler');//callback指定回调函数名称
                document.querySelector("head").appendChild(script);
            }
            // 处理函数
            function handler(data) {
                alert(data);
                // our code here...
            }
        </script>
     
    </head>
    <body>
        <input type="button" value="发送跨域HTTP请求,获取Hello World" onclick="getHello()" />
    </body>
    </html>

    使用jQuery的JSONP跨域

    在上面的章节我们探及了JSONP跨域的原理。幸运的是,我们并不需要每次都写这么多代码来完成一次跨域请求。

    jQuery的ajax方法对JSONP式跨域请求进行了封装,如果使用jQuery进行JSONP式跨域请求,代码将会变得非常简单:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title>jQuery jsonp demo</title>
        <script src="jquery-1.11.0.min.js"></script>
        <script type="text/javascript">
            $.ajax({
                type: "get",
                url: "http://localhost:8546/Service.ashx",
                dataType: "jsonp",//一定要指明为jsonp
                success: function (data) {
                    alert(data);
                },
                error: function () {
                    alert('fail');
                }
            });
        </script>
    </head>
    <body>
        使用jQuery一切将会变得非常简单。
    </body>
    </html>

    只需要将dataType设置为"jsonp"就可以进行跨域请求了,一切就像发送非跨域请求那样简单。

    jQuery为我们封装好了回调函数,一般情况下不需要我们单独去写,如果你不想在success中处理,想单独写处理函数,那么可以通过设置这2个参数来实现:

    • jsonp: "callback",//传递给服务端的回调函数参数名,如果不设置此项,则默认是"callback"
    • jsonpCallback: "handler",//传递给服务端的回调函数名称,如果不设置此项,则默认是形如"jQuery111007837897759742043_1460657212499"的由jQuery自动生成的函数名称,会自动在$.ajax方法的success中完成调用。

     

    必须要强调的是:

    1.JSONP虽然看起来很像一般的ajax请求,但其原理不同,JSONP是对文章第一小节原理的封装,是通过<script>标签的动态加载来实现的跨域请求,而一般的ajax请求是通过XMLHttpRequest对象进行;

    2.JSONP不是一种标准协议,其安全性和稳定性都不如 W3C 推荐的 CORS

    3.JSONP不支持POST请求,即使把请求类型设置为post,其本质上仍然是一个get请求。

     demo下载(1507)

    JavaScript中的跨域解决方案汇总:

    跨域解决方案一:使用CORS实现跨域

    跨域解决方案二:使用JSONP实现跨域

    出处:https://www.cnblogs.com/choon/p/5393682.html

  • 相关阅读:
    How to install VXDIAG Honda, Toyota and JLR SDD software
    16% off MPPS V16 ECU tuning tool for EDC15 EDC16 EDC17
    Cummins INSITE locked and ask for verification code
    How to use BMW Multi Tool 7.3 to replace lost key for BMW X1
    Bleed Brake Master Cylinder with Intelligent Tester IT2
    Porsche Piwis Tester II “No VCI has been detected”,how to do?
    Creader VIII VS. Creader VII+
    How to solve GM MDI cannot complete the installation
    汽车OBD2诊断程序开发 (原文转载,思路很清晰!)
    汽车节温器单片机开发思路
  • 原文地址:https://www.cnblogs.com/mq0036/p/12767030.html
Copyright © 2011-2022 走看看