jsonp协议原理深度解析

前端之家收集整理的这篇文章主要介绍了jsonp协议原理深度解析前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

前言

今天在开发联调的过程中,需要跨域的获取数据,因为使用的jquery,当然使用dataType:'jsonp'就能够很easy的解决了。
但是因为当时后端没有支持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内容

1  alert(‘hello’);

然后编写a.com/a.html内容

1 <script type='text/javascript' src='http://b.com/b.js'>

运行a.html,结果很明显,肯定会弹出hello。

引导步骤2

修改b.com/b.js文件内容

1 myFunction('hello');

然后修改a.com/a.html内容

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

运行a.html 结果是弹出‘hello world’。这个应该也毫无疑问。

引导步骤3

让我们再看一下上面的步骤2,b.js中的‘hello’就是b.com域名下的数据了,而能够在a.com/a.html中执行显示出来,这不就已经实现了跨域请求数据了吗?

另外,因为script标签中的src 不一定要指向js文件,而可以指向任何地址。

所以,我们把上面步骤2中a.html的内容<script type='text/javascript' src='http://b.com/b.js'>,我们把其中的b.js改成b.html或者b.json等等都是可以的,执行都能正常返回。

引导步骤4

上面的数据都是静态的,是在文件内写死的,所以并不能满足我们的需求了吧。。。因为我们ajax请求数据是实时变化的,所以我们要把数据变成动态的了。

我们可以让script表器去调用一个动态的页面(接口),去实现获取动态数据,这里就想到了回调函数.

编辑a.com/a.html页面内容

1 <script type='text/javascript' src='http://b.com/b.aspx?callback=myFunction'> 我们在src引用地址中加了?callback=myFunction,意思是把显示数据的函数也动态的传入了。

使用jsonp方法获取数据,还有一个要点就是后端接口也要支持jsonp才行,比如下面一段代码就是让返回的数据变成jsonp的格式,请继续看:(此处使用.net语言作为例子)

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

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

引导步骤5

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

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

1 function addScript(src){ 2 var script = document.createElement('script'); 3 script.setAttribute('type','text/javascript'); 4 script.src= src; 5 document.body.appendChild(script); 6 }

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

1 addScript('b.com/b.aspx?callback=myFunction'); 2 function myFunction(data){4 alert(data); 5 }

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

jquery实现跨域

jquery跨域方法

1 $.ajax({ 2 url: 'b.com/b.json',不同的域 3 type: 'GET',0)"> jsonp模式只有GET是合法的 4 dataType: 'jsonp',0)"> 数据类型 5 jsonp: 'callback',0)"> 指定回调函数名,与服务器端接收的一致,并回传回来 6 success: function(data) { 7 console.log(data); 8 } 9 })
使用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(/?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";      4 } 构建新的s.data,使其包含 callback=function name 5 else if (!s.data || !s.data.match(jsre))    s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?"; 6 s.dataType = "json"; 7 } 8 判断是否为jsonp,如果是 ,进行处理。 9 if (s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre))) {   10 jsonp = "jsonp" + jsc ++; 为请 求字符集串的callback=加上生成回调函数 11 if (s.data) s.data = (s.data + "").replace(jsre,"=" + jsonp + "$1");   12 s.url = s.url.replace(jsre,"=" + jsonp + "$1"); 我们需要保证jsonp 类 型响应能正确地执行 13    jsonp的类型必须为script。这样才能执行服 务器返回的 14    代码。这里就是调用这个回调函数 15 s.dataType = "script"; 16 window下注册一个jsonp回调函数 有,让ajax请求返回的代码调用执行它, 17 window[jsonp] = function(tmp) {    18 data = tmp; 19 success(); 20 complete();   垃圾回收,释放联变量,删除jsonp的对象,除去head中加的script元素   21 window[jsonp] = undefined;    22 try {  23 delete window[jsonp];      24 } catch (e) {}    25 if (head)  head.removeChild(script);    26 };   27 } 28 if (s.data && type == "GET") {    data有效,追加到get类型的url上去 29 s.url += (s.url.match(/?/) ? "&" : "?") + s.data;    防止IE会重复发送get和post data 30 s.data = null; 31 } 32 if (s.dataType == "script"  && type == "GET" && parts && (parts[1] && parts[1] != location.protocol || parts[2] != location.host)) {    在head中加上<script src=""></script> 33 var head = document.getElementsByTagName("head")[0];    34 var script = document.createElement("script");    35 script.src = s.url;    36 if (s.scriptCharset) script.charset = s.scriptCharset; 37 if (!jsonp) {  如果datatype不是jsonp,但是url却是跨域 的。采用scriptr的onload或onreadystatechange事件来触发回 调函数 38 var done = false;  对所有浏览器都加上处理器 39 script.onload = script.onreadystatechange = function() {      40 if (!done && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {          41 done = true;  42 success();         43 complete(); 44 head.removeChild(script);        45 }    46 };   47 }   48 head.appendChild(script); 已经使用 了script 元素注射来处理所有的事情 49 return undefined; 50 }
上面的代码稍显复杂,但是我们挑拣重要的看就好了。

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

这里执行代码之后,其实就是判断是否配置了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) { 2 var options = config || {}; 需要配置url,success,time,fail四个属性 var callbackName = ('jsonp_' + Math.random()).replace(".",""); var oHead = document.getElementsByTagName('head')[0]; 5 var oScript = document.createElement('script'); 6 oHead.appendChild(oScript); 7 window[callbackName] = function(json) { 创建jsonp回调函数 8 oHead.removeChild(oScript); 9 clearTimeout(oScript.timer); 10 window[callbackName] = 11 options.success && options.success(json); 删除script标签,实际上执行的是success函数 12 }; 13 oScript.src = options.url + '?' + callbackName; 发送请求 14 if (options.time) { 设置超时处理 15 oScript.timer = setTimeout(function () { 16 window[callbackName] = 17 oHead.removeChild(oScript); 18 options.fail && options.fail({ message: "超时" }); 19 },options.time); 20 } 21 };
这是我自己写的一个原生js实现jsonp获取跨域数据的方法

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

1 jsonp({ 2 url: '/b.com/b.json', 3 success: function(d){ 4 数据处理 5 },128)"> 6 time: 5000,128)"> 7 fail: function(){ 8 错误处理 9 } 10 })

小结

再说几点注意的地方:

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

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

原文链接:https://www.f2er.com/json/289616.html

猜你在找的Json相关文章