>  기사  >  웹 프론트엔드  >  jsonp 프로토콜의 심층 분석principle_json

jsonp 프로토콜의 심층 분석principle_json

WBOY
WBOY원래의
2016-05-16 15:38:021916검색

현재 공동 디버깅을 개발하는 과정에서 jquery를 사용하므로 당연히 dataType:'jsonp'를 사용하면 문제를 쉽게 해결할 수 있습니다.
그런데 당시 백엔드가 jsonp 액세스를 지원하지 않았기 때문에 나중에 이 기능을 구현할 때 jsonp 형식으로 반환되는 형식이 무엇인지 물었습니다. 사용법만 알았을 뿐, 답은 알 수 없었습니다. . .

나중에 해결됐지만 문제해결을 좋아하는 저는 항상 고민을 많이 하게 되어서 정보를 읽기 시작했고, 보고 나서 갑자기 깨달음을 느꼈습니다. 할 계획입니다. 모든 사람과 공유할 메모입니다.

JSON과 JSONP의 차이점

JSON과 JSONP 사이에는 문자가 하나만 다르지만 실제로는 전혀 동일하지 않습니다. 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 내용을 작성하세요.

코드 복사 코드는 다음과 같습니다.
a5a72787fc39f4790c4a6f6c2370e660

a.html을 실행하면 결과가 뻔하고 hello가 확실히 팝업됩니다.

가이드 2단계

b.com/b.js 파일 콘텐츠 수정:

코드 복사 코드는 다음과 같습니다.
myFunction('hello');

그런 다음 a.com/a.html 콘텐츠를 수정합니다.

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

a.html을 실행하면 'hello world' 팝업이 나타납니다. 이것에 대해서는 의심의 여지가 없습니다.

가이드 3단계

위의 2단계를 다시 살펴보겠습니다. b.js의 'hello'는 b.com 도메인 이름 아래의 데이터이며, a.com/a.html에서 실행되어 표시될 수 있습니다. 이것은 이미 跨域请求数据了 구현되었나요?

또한 스크립트 태그의 src가 반드시 js 파일을 가리키는 것은 아니기 때문에 어떤 주소든 가리킬 수 있습니다.

따라서 위의 2단계에서 a.html의 내용을 76213f686a5840fdc8272e38cbc99a7d로 변경하고, b.jsb.html 또는 b.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 메소드를 사용하여 데이터를 얻는 또 다른 중요한 점은 后端接口也要支持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으로 문의하세요.