ホームページ  >  記事  >  ウェブフロントエンド  >  jsonpプロトコルprinciple_jsonの詳細な分析

jsonpプロトコルprinciple_jsonの詳細な分析

WBOY
WBOYオリジナル
2016-05-16 15:38:021915ブラウズ

今日、共同デバッグの開発過程でドメインを越えてデータを取得する必要があります。もちろん、dataType:'jsonp' を使用すると、問題は簡単に解決できます。
しかし、当時バックエンドは jsonp アクセスをサポートしていなかったので、後でこの関数を実装するときに、jsonp 形式で返される形式は何ですか? と尋ねました。使い方だけは知っていましたが、答えはわかりませんでした。 。 。

後から解決したのですが、問題を解くのが好きな私としては、よく調べないといけないとずっと気になっていたので、情報を読み始めたのですが、それを見て突然悟ったような気がしたので、調べてみました。やる予定だったのでみんなで共有するメモ。

JSON と JSONP の違い

JSON と JSONP の間には 1 文字の違いがありますが、実際にはまったく同じではありません。JSON はデータ交換形式であり、JSONP は JSONP メソッドを使用して得られるクロスドメイン データ対話プロトコルです。はまだ json形式のデータです。

ハッキリ言って、用JSON来传数据,靠JSONP来跨域

JSONP で詳しく説明しました

誰もが知っている 一个页面的ajax只能获取和此页面同域的数据。 ため、ドメイン間でデータを取得する必要がある場合は、JSONP メソッドを使用してデータを取得する必要があります。

下の図に示すように、これは、json 形式を使用してクロスドメイン データを取得することによって返されるエラー メッセージです。

それではどうやって解決すればいいのでしょうか?フレームワークを使用するフロントエンドの子供靴には、独自の対応するメソッドがある場合があります。たとえば、jquery では dataType设为jsonp を追加するだけで解決できますが、それを使用するときに、なぜこの方法で解決できるのかを考えたことはありますか。中心的な考え方は何ですか?

以下は詳細な説明です。最初のアイデアは、scirpt タグを使用してクロスドメイン データを導入することです。 jsonp のプロセスを最初からゆっくりと見ていきましょう。

ガイドステップ 1

書くb.com/b.js 内容:

コードをコピー コードは次のとおりです:
alert('hello');

次に、a.com/a.html の内容を書きます:

コードをコピー コードは次のとおりです:
0320273fa6d297f212588f595e244432、b.jsb.htmlb.json などに変更すると、実行が正常に戻ります。

ガイダンス ステップ 4

上記のデータはすべて静的で、ファイル内にハードコーディングされているため、ニーズを満たすことができません。 。 。 Ajax リクエスト データはリアルタイムで変化するため、データを動的にする必要があります。

スクリプト テーブルに動的ページ (インターフェイス) を呼び出して動的データを取得させることができます。ここでは 回调函数.

について考えます。

a.com/a.html ページのコンテンツを編集:

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

src 参照アドレスに ?callback=myFunction を追加しました。これは、データを表示する関数も動的に渡されることを意味します。

jsonp メソッドを使用してデータを取得するもう 1 つの重要なポイントは 后端接口也要支持jsonp才行 です。たとえば、次のコードは返されたデータを jsonp 形式に変更します。このまま読み続けてください: (ここでは例として .net 言語が使用されています。 )

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数据的方法。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。