zoukankan      html  css  js  c++  java
  • (三)ajax请求不同源之jsonp跨域

    在页面上引入不同域上的JS脚本文件却是可以跨域的,JSONP正是利用这个特性来实现的,下面我们将逐步认识JSONP。

    一、初识JSONP

    JSON是一种数据交互格式,JSONP是一种非官方跨域数据交互协议。JSONP(json with padding)也叫填充式JSON,是被包含在函数调用中的JSON,可以让网页从别的网站获取资料,即跨域读取数据。

    二、JSONP的组成

    回调函数和数据。

    三、JSONP的格式

    callbackFunction({"name":"camille","sex":"male"})

    四、JSONP的优缺点

    1、优点

    它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;

    它的兼容性更好,在古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;

    在请求完毕后可以通过调用callback的方式回传结果。

    2、缺点

    它只支持GET请求而不支持POST等其它类型的HTTP请求;

    它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

    五、JSONP的原理

    1、直接引入js文件

    A、页面的代码

    <script type="text/javascript" src="https://xxx.com.cn/js/test.js"></script>

    B、test.js的代码

    alert("script方式引入js文件");

    页面弹出:script方式引入js文件

    2、跨域的js调用本地方法获取数据

    A、页面的代码

    <script type="text/javascript">
    var testCallbackFunc = function(result){
      alert("我是本地函数,可以被跨域的test.js文件调用,name: " + result.name);
    };
    </script>
    <script type="text/javascript" src="https://xxx.com.cn/js/test.js"></script>

    B、test.js的代码

    testCallbackFunc({
     "name": "camille",
     "sex": "male",
     "age": 27});

    页面弹出:我是本地函数,可以被跨域的test.js文件调用,name:camille

    3、怎么让远程js知道它应该调用的本地函数叫什么名字呢?

    可以传一个参数过去告诉服务端“我想要一段调用testCallbackFunc函数的js代码,请你返回给我”,于是服务器就可以按照客户端的需求来生成js脚本并响应了。

    //jsonp回调方法,一定要写在jsonp请求前面

    //script默认会把请求回来的内容当成js脚本进行解析运行。

    var testCallbackFunc= function (result) {
       alert("je suis " + result.name);
    };
    var scriptsrc ="https://xxx.com.cn/api/getUserInfo?sCallback=testCallbackFunc";  //访问后端接口,得到服务器端生成的被回调函数包裹的JSON数据,js脚本。
    var dscript = document.createElement("script");
    dscript.setAttribute("src", scriptsrc);
    document.getElementsByTagName('head')[0].appendChild(dscript);

    在network中查看请求,可以看到服务端返回的数据是这样的。

    testCallbackFunc({
      "name": "camille",
      "sex": "male",
      "age": 27
    });

    接下来,把代码封装一下,以便于与用户界面交互,从而实现多次和重复调用。

    4、jQuery如何实现jsonp调用

    $.ajax({
      url:"https://xxx.com.cn/api/getUserInfo",
      async: false,
      type: "get", // jsonp模式只有get是合法的
      data:{
        sUrl: "https://sss.com.cn/page/detail.html"
      },
      dataType: "jsonp",
      jsonp:"sCallback",// 指定回调函数名,与服务器端接收的一致,并回传回来
      jsonpCallback:"testCallbackFunc",//自定义jsonp回调函数名称,该方法不用自己写
      success:function(result){
        //dosomething
      },
      error:function(){
        //dosomething
      }
    })

    JSONP协议的特点就是允许用户给服务端传递一个jsonpCallback参数(如jQuery2030038573939353227615_1402643146875)

    服务端返回数据时,会将jsonpCallback的参数值作为函数名来包裹json数据,这样客户端就可以自定义函数处理返回数据了。

    A、如果不设置jsonpCallback参数,jQuery会自动帮我们随机生成一个回调函数,类似jQuery2030038573939353227615_1402643146875。每刷新一次页面,该值就会变。在network中查看请求,可以看到服务端返回的数据是这样的。

    jQuery2030038573939353227615_1402643146875({
      "name": "camille",
      "sex": "male",
      "age": 27
    });

    B、如果设置jsonpCallback参数,我们可以自定义一个回调函数testCallbackFunc。在network中查看请求,可以看到服务端返回的数据是这样的。

    testCallbackFunc({
      "name": "camille",
      "sex": "male",
      "age": 27
    });

    jquery内部会转化成

    https://xxx.com.cn/api/getUserInfo?sCallback=testCallbackFunc&sUrl=http://sss.com.cn/page/detail.html

    然后动态加载

    <script type="text/javascript" src="https://xxx.com.cn/api/getUserInfo?sCallback=testCallbackFunc&sUrl=http://sss.com.cn/page/detail.html
    "></script>

    获取到数据后,jQuery会自动销毁这个全局函数。

    5、封装后的简写方式

    $.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。

    $.getJSON("https://xxx.com.cn/api/getUserInfo?sUrl=https://sss.com.cn/page/detail.html&sCallback=testCallbackFunc",  
      function(result){  
        //dosomething
    });  

    六、如何处理跨域请求失败?

    如果服务器拒绝请求,或者请求地址不正确,参数错误,网络不通,跨域就取不到数据,怎么办?下面介绍3种方案。众所周知,在浏览器中,script标签遇到服务器错误时,会触发onerror事件

    1、如果自己实现jsonp,可以这么做。

    var ele = document.createElement('script');
    ele.type = "text/javascript";
    ele.src = '...';
    ele.onerror = function() {
      alert('error');
    };
    ele.onload = function() {
      alert('load');
    };
    document.body.appendChild(ele);

    2、如果想用jquery的jsonp

    JQuery不会处理此类错误,而是选择“静静地失败”,fail回调不会执行,你的代码也不会得到任何反馈,所以你没有处理这种错误的机会,也无法向用户报告错误。

    jQuery.ajaxTransport( "script", function(s) {
     if ( s.crossDomain ) {
      var script,
       head = document.head || jQuery("head")[0] || document.documentElement;
      return {
       send: function( _, callback ) {
        script = document.createElement("script");
        script.async = true;
        ...
        script.src = s.url;
        script.onload = script.onreadystatechange = ...;
        head.insertBefore( script, head.firstChild );
       },
       abort: function() {
        ...
       }
      };
     }
    });

    可以看到script是个局部变量,外部无法取到。但是我们可以根据script标签的位置,动态获取head第一个元素,就可以拿到script,然后添加onerror事件。

    function jsonp(url, data, callback) {
      var xhr = $.getJSON(url + '?jsoncallback=?', data, callback);
    
      // request failed
      xhr.fail(function(jqXHR, textStatus, ex) {
        /*
         * in ie 8, if service is down (or network occurs an error), the arguments will be:
         * 
         * testStatus: 'parsererror'
         * ex.description: 'xxxx was not called' (xxxx is the name of jsoncallback function)
         * ex.message: (same as ex.description)
         * ex.name: 'Error'
         */
        alert('failed');
      });
    
      // ie 8+, chrome and some other browsers
      var head = document.head || $('head')[0] || document.documentElement; // code from jquery
      var script = $(head).find('script')[0];
      script.onerror = function(evt) {
        alert('error');
    
        // do some clean
    
        // delete script node
        if (script.parentNode) {
          script.parentNode.removeChild(script);
        }
    // delete jsonCallback global function
        var src = script.src || '';
        var idx = src.indexOf('jsoncallback=');
        if (idx != -1) {
          var idx2 = src.indexOf('&');
          if (idx2 == -1) {
            idx2 = src.length;
          }
          var jsonCallback = src.substring(idx + 13, idx2);
          delete window[jsonCallback];
        }
      };
    }

    3、修改jquery源码,加上onerror事件。

  • 相关阅读:
    Bom入门
    Dom入门
    JavaScript对象
    Ultra-QuickSort——[归并排序、分治求逆序对]
    UVA 11212 Editing a Book [迭代加深搜索IDA*]
    Anagram——[枚举全排列]
    Black Box--[优先队列 、最大堆最小堆的应用]
    Argus--[优先队列]
    UVa1601
    UVa 10603 Fill [暴力枚举、路径搜索]
  • 原文地址:https://www.cnblogs.com/camille666/p/jsonp.html
Copyright © 2011-2022 走看看