前言

今天在开发联调的过程中,需要跨域的获取数据,因为使用的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'>
  2. <script>
  3. function myFunction(str){ //定义处理数据的函数
  4. alert(str + ' world');
  5. }
  6. </script>

我们在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["callback"];
  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.  
  3. function myFunction(data){//定义处理数据的函数
  4. alert(data);
  5. }

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

jquery实现跨域

jquery跨域方法

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

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

jq实现jsonp源码分析

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

  1. if (s.dataType == "jsonp") {   // 构建jsonp请求字符集串。jsonp是跨域请求,要加上callback=?后面将会加函数名             
  2. if (type == "GET") { //使get的url包含 callback=?后面将 会进行加函数名
  3. 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

  1. function jsonp(config) {
  2. var options = config || {}; // 需要配置url, success, time, fail四个属性
  3. var callbackName = ('jsonp_' + Math.random()).replace(".", "");
  4. 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] = null;
  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] = null;
  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. },
  6. time: 5000,
  7. fail: function(){
  8. //错误处理
  9. }
  10. })

小结

再说几点注意的地方:

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

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

jsonp协议原理深度解析的更多相关文章

  1. java8Stream原理深度解析

    Java8 Stream原理深度解析 Author:Dorae Date:2017年11月2日19:10:39 转载请注明出处 上一篇文章中简要介绍了Java8的函数式编程,而在Java8中另外一个比 ...

  2. mysql索引原理深度解析

    mysql索引原理深度解析 一.总结 一句话总结: mysql索引是b+树,因为b+树在范围查找.节点查找等方面优化 hash索引,完全平衡二叉树,b树等 1.数据库中最常见的慢查询优化方式是什么? ...

  3. SQL注入原理深度解析

    本文转自:http://www.iii-soft.com/forum.php?mod=viewthread&tid=1613&extra=page%3D1 对于Web应用来说,注射式攻 ...

  4. 第1课:SQL注入原理深度解析

    对于Web应用来说,注射式攻击由来已久,攻击方式也五花八门,常见的攻击方式有SQL注射.命令注射以及新近才出现的XPath注射等等.本文将以SQL注射为例,在源码级对其攻击原理进行深入的讲解. 一.注 ...

  5. react渲染原理深度解析

    https://mp.weixin.qq.com/s/aM-SkTsQrgruuf5wy3xVmQ   原文件地址 [第1392期]React从渲染原理到性能优化(二)-- 更新渲染 黄琼 前端早读课 ...

  6. Vue双向数据绑定原理深度解析

    首先,什么是双向数据绑定?Vue是三大MVVM框架之一,数据绑定简单来说,就是当数据发生变化时,相应的视图会进行更新,当视图更新时,数据也会跟着变化. 在分析其原理和代码的时候,大家首先了解如下几个j ...

  7. 跨站资源共享CORS原理深度解析

    我相信如果你写过前后端分离的web应用程序,或者写过一些ajax请求调用,你可能会遇到过CORS错误. CORS是什么? 它与安全性有关吗? 为什么要有CORS?它解决了什么目的? CORS是怎样运行 ...

  8. 轻量级跨平台消息传递协议XML-RPC深度解析

    一.引言 实现跨平台通信的协议主要有简单对象訪问协议(Simple Object Access Protocol,SOAP).代表性状态传输(Representational State Transf ...

  9. 关于laravel5.5控制器方法参数依赖注入原理深度解析及问题修复

    在laravel5.5中,可以根据控制器方法的参数类型,自动注入一个实例化对象,极大提升了编程的效率,但是相比较与Java的SpringMVC框架,功能还是有所欠缺,使用起来还是不太方便,主要体现在方 ...

随机推荐

  1. JAVA笔试题集(一)--JAVASE部分

    红色答案为参考答案 1.从下列选项中选择正确的Java表达式(多选) A.  int k=new String("aa");       B.  String str=String ...

  2. 详解Maple中的基础工具栏

    鉴于Maple 强大的符号计算功能,越来越多的人选择使用Maple 2015计算复杂的数学问题,初学者刚开始时需要对Maple有所熟悉才能很好地进行运用,下面就从基础开始,介绍Maple工作环境. M ...

  3. 一步一步学ROP之linux_x86篇

    一步一步学ROP之linux_x86篇 作者:蒸米@阿里聚安全 ​ 一.序 ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过 ...

  4. 飞流直下的精彩 -- 淘宝UWP中瀑布流列表的实现

    在淘宝UWP中,搜索结果列表是用户了解宝贝的重要一环,其中的图片效果对吸引用户点击搜索结果,查看宝贝详情有比较大的影响.为此手机淘宝特意在搜索结果列表上采用了2种表现方式:一种就是普通的列表模式,而另 ...

  5. 探索c#之跳跃表(SkipList)

    阅读目录: 基本介绍 算法思想 演化步骤 实现细节 总结 基本介绍 SkipList是William Pugh在1990年提出的,它是一种可替代平衡树的数据结构. SkipList在实现上相对比较简单 ...

  6. 收集最好的Mac软件和使用方法

    MacBook 初体验 作者是刚从Windows下转到mac时写的,这篇文章对也主要介绍了Mac下开发环境的部署.软件的安装和卸载.常用快捷键.文件系统的介绍. http://liujiacai.ne ...

  7. MySQL 主主复制

    200 ? "200px" : this.width)!important;} --> 介绍 环境 OS:CentOS 6.7,MySQL 5.6 Master:192.16 ...

  8. SQL Azure (15) SQL Azure 新的规格

    <Windows Azure Platform 系列文章目录> 在以前的文章中,笔者给大家介绍了Microsoft Azure SQL Database (以前被称为SQL Azure)的 ...

  9. 【VC++技术杂谈004】使用微软TTS语音引擎实现文本朗读

    本文主要介绍如何使用微软TTS语音引擎实现文本朗读,以及生成wav格式的声音文件. 1.语音引擎及语音库的安装 TTS(Text-To-Speech)是指文本语音的简称,即通过TTS引擎把文本转化为语 ...

  10. jsp登入oracle

    <body> <% Class.forName("oracle.jdbc.driver.OracleDriver"); Connection conn=null; ...