Heim  >  Artikel  >  Web-Frontend  >  Eingehende Analyse des JSONP-Protokolls „principle_json“.

Eingehende Analyse des JSONP-Protokolls „principle_json“.

WBOY
WBOYOriginal
2016-05-16 15:38:021916Durchsuche

Heute müssen wir bei der Entwicklung des gemeinsamen Debuggens Daten über Domänen hinweg abrufen. Da jquery verwendet wird, kann das Problem natürlich leicht durch die Verwendung von dataType:'jsonp' gelöst werden.
Da das Backend zu diesem Zeitpunkt jedoch keinen JSONP-Zugriff unterstützte, fragte er mich später bei der Implementierung dieser Funktion: Welches Format wird im JSONP-Format zurückgegeben? Ich wusste nur, wie man es benutzt, aber ich konnte die Antwort nicht herausfinden. . .

Obwohl es später gelöst wurde, machte ich mir, der ich gerne Probleme löse, immer Sorgen darüber. Ich musste dies gründlich recherchieren, also begann ich, die Informationen zu lesen, und nachdem ich sie gesehen hatte, verspürte ich eine plötzliche Erleuchtung Ich hatte vor, Notizen zu machen, die ich mit allen teilen kann.

Der Unterschied zwischen JSON und JSONP

Obwohl es nur einen Buchstabenunterschied zwischen JSON und JSONP gibt, sind sie eigentlich überhaupt nicht dasselbe: JSON ist ein Datenaustauschformat und JSONP ist ein domänenübergreifendes Dateninteraktionsprotokoll. Was Sie mit der JSONP-Methode erhalten ist immer noch Es handelt sich um Daten im JSON-Format.

Um es ganz klar auszudrücken: 用JSON来传数据,靠JSONP来跨域.

JSONP im Detail erklärt

Wir alle wissen: 一个页面的ajax只能获取和此页面同域的数据。 Wenn wir also Daten über Domänen hinweg abrufen müssen, müssen wir die JSONP-Methode verwenden, um sie abzurufen.

Wie in der Abbildung unten gezeigt, handelt es sich um die Fehlermeldung, die bei Verwendung des JSON-Formats zum Abrufen domänenübergreifender Daten zurückgegeben wird:

Wie lässt sich das Problem lösen? Front-End-Kinderschuhe, die Frameworks verwenden, verfügen möglicherweise über eigene entsprechende Methoden. Jquery kann das Problem beispielsweise durch einfaches Hinzufügen von dataType设为jsonp lösen. Haben wir jedoch jemals darüber nachgedacht, warum es auf diese Weise gelöst werden kann? Was ist die zentrale Idee?

Das Folgende ist eine detaillierte Erklärung für Sie. Die erste Idee besteht darin, scipt-Tags zu verwenden, um domänenübergreifende Daten einzuführen. Lassen Sie uns langsam von Anfang an in den JSONP-Prozess einsteigen.

Leitfaden Schritt 1

Schreibenb.com/b.js Inhalt:

Code kopieren Der Code lautet wie folgt:
alert('hello');

Dann schreiben Sie a.com/a.htmlInhalt:

Code kopieren Der Code lautet wie folgt:
58cdce07cddaf1c57c79da1e351a8c6b

Führen Sie a.html aus. Das Ergebnis ist offensichtlich. Hallo wird auf jeden Fall angezeigt.

Anleitung Schritt 2

Ändern b.com/b.js Dateiinhalt:

Code kopieren Der Code lautet wie folgt:
myFunction('hello');

Ändern Sie dann den a.com/a.html Inhalt:

<script type='text/javascript' src='http://b.com/b.js'>
<script>
function myFunction(str)
{ //定义处理数据的函数
alert(str + ' world');
}
</script>

Das Ausführen von a.html führt dazu, dass „Hallo Welt“ angezeigt wird. Daran sollte kein Zweifel bestehen.

Anleitung Schritt 3

Sehen wir uns Schritt 2 oben noch einmal an. Das „Hallo“ in b.js sind die Daten unter dem b.com-Domainnamen, die in a.com/a.html ausgeführt und angezeigt werden können Dies wurde bereits 跨域请求数据了 implementiert?

Da der src im Skript-Tag nicht unbedingt auf die js-Datei verweist, kann er außerdem auf eine beliebige Adresse verweisen.

Also ändern wir den Inhalt von a.html in Schritt 2 oben: 76213f686a5840fdc8272e38cbc99a7d, und wir können b.js in b.html oder b.json usw. ändern, und die Ausführung wird normal fortgesetzt.

Anleitung Schritt 4

Die oben genannten Daten sind alle statisch und in der Datei fest codiert, sodass sie unsere Anforderungen nicht erfüllen können. . . Da sich unsere Ajax-Anforderungsdaten in Echtzeit ändern, müssen wir die Daten dynamisch gestalten.

Wir können die Skripttabelle eine dynamische Seite (Schnittstelle) aufrufen lassen, um dynamische Daten zu erhalten. Hier denken wir an 回调函数.

Seiteninhalt a.com/a.html bearbeiten:

<script type='text/javascript' src='http://b.com/b.aspx&#63;callback=myFunction'>
 <script>
   function myFunction(str){ //定义处理数据的函数
    alert(str + ' world');
   }
</script>

Wir haben ?callback=myFunction zur src-Referenzadresse hinzugefügt, was bedeutet, dass die Funktion zum Anzeigen von Daten auch dynamisch übergeben wird.

Die Verwendung der JSONP-Methode zum Abrufen von Daten ist 后端接口也要支持jsonp才行. Der folgende Code ändert beispielsweise die zurückgegebenen Daten in das JSONP-Format. Bitte lesen Sie weiter: (Hier wird die Sprache .net verwendet )

protected void page_load(object sender, EventArgs e){
  if(this.IsPostBack == false){
    string callback = '';
    if(Request["callback"] != null){
      callback = request["callback"];
      string data = "hello";
      Response.Write(callback+"("+ data + ")"); //接口页面返回的数据格式“函数(参数)”的格式。
    }
  }
}

代码的意思很简单,就是获取调用函数的参数。如果这里调用b.aspx?callback=myFunction的话,则会返回myFunction('hello'),如果后端代码给data赋值一个变量,这里的‘hello'则变成了动态的数据了。

引导步骤5

再看上面的步骤,虽然获取的数据是动态的了,但在页面上引入一个script标签,却只能执行一次,获取一次,显然还是不能满足需求的。所以我们在需要的时候,就得动态的添加一次这样的script标签。

所以我们在这里需要封装一个函数:

function addScript(src){
   var script = document.createElement('script');
   script.setAttribute('type','text/javascript');
   script.src= src;
   document.body.appendChild(script);
 }

需要调用的时候,就去执行:

addScript('b.com/b.aspx&#63;callback=myFunction');
 
   function myFunction(data){//定义处理数据的函数
     alert(data);
   }

ok,上面的过程就是jsonp的原理,我们不必去记住那些令人纠结不清的定义,只要看一遍这个过程,我相信就能明白其中的精髓了吧。

jquery实现跨域

jquery跨域方法

$.ajax({
  url: 'b.com/b.json', //不同的域
  type: 'GET', // jsonp模式只有GET是合法的
  dataType: 'jsonp', // 数据类型
  jsonp: 'callback', // 指定回调函数名,与服务器端接收的一致,并回传回来
  success: function(data) {
    console.log(data);
  }
})

使用jquery非常方便,那么它是怎么实现这个转化的呢?下面我们来看看这部分的jquery源码。

jq实现jsonp源码分析

我贴出网上给的jquery实现jsonp部分的源码分析:

if (s.dataType == "jsonp") {   // 构建jsonp请求字符集串。jsonp是跨域请求,要加上callback=?后面将会加函数名             
  if (type == "GET") { //使get的url包含 callback=?后面将 会进行加函数名
    if (!s.url.match(jsre))      s.url += (s.url.match(/&#63;/) &#63; "&" : "&#63;") + (s.jsonp || "callback") + "=&#63;";     
  } // 构建新的s.data,使其包含 callback=function name
  else if (!s.data || !s.data.match(jsre))    s.data = (s.data &#63; s.data + "&" : "") + (s.jsonp || "callback") + "=&#63;";
  s.dataType = "json";
}
//判断是否为jsonp,如果是 ,进行处理。
if (s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre))) {  
  jsonp = "jsonp" + jsc ++; //为请 求字符集串的callback=加上生成回调函数名
  if (s.data) s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");  
  s.url = s.url.replace(jsre, "=" + jsonp + "$1"); // 我们需要保证jsonp 类 型响应能正确地执行
     //jsonp的类型必须为script。这样才能执行服 务器返回的
     //代码。这里就是调用这个回调函数。
  s.dataType = "script";
  //window下注册一个jsonp回调函数 有,让ajax请求返回的代码调用执行它,
  window[jsonp] = function(tmp) {   
    data = tmp;
    success();
    complete();   // 垃圾回收,释放联变量,删除jsonp的对象,除去head中加的script元素  
    window[jsonp] = undefined;   
    try { 
      delete window[jsonp];     
    } catch (e) {}   
    if (head)  head.removeChild(script);   
  };  
}
if (s.data && type == "GET") {    // data有效,追加到get类型的url上去
  s.url += (s.url.match(/&#63;/) &#63; "&" : "&#63;") + s.data;    // 防止IE会重复发送get和post data
  s.data = null;
}
if (s.dataType == "script"  && type == "GET" && parts && (parts[1] && parts[1] != location.protocol || parts[2] != location.host)) {    // 在head中加上<script src=""></script>
  var head = document.getElementsByTagName("head")[0];   
  var script = document.createElement("script");   
  script.src = s.url;   
  if (s.scriptCharset) script.charset = s.scriptCharset;
  if (!jsonp) {  //如果datatype不是jsonp,但是url却是跨域 的。采用scriptr的onload或onreadystatechange事件来触发回 调函数。
    var done = false; // 对所有浏览器都加上处理器
    script.onload = script.onreadystatechange = function() {     
      if (!done && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {         
        done = true; 
        success();        
        complete();
        head.removeChild(script);       
      }   
    };  
  }  
  head.appendChild(script); // 已经使用 了script 元素注射来处理所有的事情
  return undefined;
}

上面的代码稍显复杂,但是我们挑拣重要的看就好了。

我们来分析一下这个过程,其实这个过程也就是上面我提出问题的答案了:

这里执行代码之后,其实就是判断是否配置了dataType: 'jsonp',如果是jsonp协议,则要在url上加callback=jQueryxxx(函数名),jquery会把url转化为:http://b.com/b.json?callback=jQueryxxx,然后再在html中插入,加载完b.json这个文件后,就会执行jQueryxxx这个回调函数,而且此时这个函数里面已经存在了动态数据(json格式数据),所以在页面上执行的时候就能够随心所欲的处理数据了,但是也别忘了后端也要支持jsonp格式才行。所以这样就达到了跨域获取数据的功能。

原生js封装jsonp

function jsonp(config) {
    var options = config || {};  // 需要配置url, success, time, fail四个属性
    var callbackName = ('jsonp_' + Math.random()).replace(".", "");
    var oHead = document.getElementsByTagName('head')[0];
    var oScript = document.createElement('script');
    oHead.appendChild(oScript);
    window[callbackName] = function(json) { //创建jsonp回调函数
      oHead.removeChild(oScript);
      clearTimeout(oScript.timer);
      window[callbackName] = null;
      options.success && options.success(json);  //先删除script标签,实际上执行的是success函数
    };
    oScript.src = options.url + '&#63;' + callbackName;  //发送请求
    if (options.time) { //设置超时处理
      oScript.timer = setTimeout(function () {
        window[callbackName] = null;
        oHead.removeChild(oScript);
        options.fail && options.fail({ message: "超时" });
      }, options.time);
    }
  };

这是我自己写的一个原生js实现jsonp获取跨域数据的方法。

我们只需要调用jsonp函数就能够跨域获取数据了。比如:

jsonp({
    url: '/b.com/b.json',
    success: function(d){
      //数据处理
    },
    time: 5000,
    fail: function(){
      //错误处理
    }    
  })
小结

再说几点注意的地方:

使用jsonp方法时,在控制台的network-JS中才能找到调用的接口,不再是XHR类了。由于页面渲染的时候script只执行一次,而且动态数据需要多次调用,所以在插入使用之后需要删除,并且要初始化回调函数。原生js实现时,最好加一个请求超时的功能,方便调试。

总之jsonp就是一种获取跨域json数据的方法。

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn