说说JSON和JSONP,浅析JSONP解决AJAX跨域问题

说到AJAX就会不可避免的面临两个问题,第一个是AJAX以何种格式来交换数据?第二个是跨域的需求如何解决?这两个问题目前都有不同的解决方案,比如数据可以用自定义字符串或者用XML来描述,跨域可以通过服务器端代理来解决。

但到目前为止最被推崇或者说首选的方案还是用JSON来传数据,靠JSONP来跨域。而这就是本文将要讲述的内容。

JSON和JSONP虽然只有一个字母的差别,但其实他们根本不是一回事儿:JSON是一种数据交换格式,而JSONP是一种依靠开发人员的聪明才智创造出的一种非官方跨域数据交互协议。我们拿最近比较火的谍战片来打个比方,JSON是地下党们用来书写和交换情报的“暗号”,而JSONP则是把用暗号书写的情报传递给自己同志时使用的接头方式。看到没?一个是描述信息的格式,一个是信息传递双方约定的方法。

既然随便聊聊,那我们就不再采用教条的方式来讲述,而是把关注重心放在帮助开发人员理解是否应当选择使用以及如何使用上。

什么是JSON?

前面简单说了一下,JSON是一种基于文本的数据交换方式,或者叫做数据描述格式,你是否该选用他首先肯定要关注它所拥有的优点。

JSON的优点:

1、基于纯文本,跨平台传递极其简单;

2、Javascript原生支持,后台语言几乎全部支持;

3、轻量级数据格式,占用字符数量极少,特别适合互联网传递;

4、可读性较强,虽然比不上XML那么一目了然,但在合理的依次缩进之后还是很容易识别的;

5、容易编写和解析,当然前提是你要知道数据结构;

JSON的缺点当然也有,但在作者看来实在是无关紧要的东西,所以不再单独说明。

JSON的格式或者叫规则:

JSON能够以非常简单的方式来描述数据结构,XML能做的它都能做,因此在跨平台方面两者完全不分伯仲。

1、JSON只有两种数据类型描述符,大括号{}和方括号[],其余英文冒号:是映射符,英文逗号,是分隔符,英文双引号""是定义符。

2、大括号{}用来描述一组“不同类型的无序键值对集合”(每个键值对可以理解为OOP的属性描述),方括号[]用来描述一组“相同类型的有序数据集合”(可对应OOP的数组)。

3、上述两种集合中若有多个子项,则通过英文逗号,进行分隔。

4、键值对以英文冒号:进行分隔,并且建议键名都加上英文双引号"",以便于不同语言的解析。

5、JSON内部常用数据类型无非就是字符串、数字、布尔、日期、null 这么几个,字符串必须用双引号引起来,其余的都不用,日期类型比较特殊,这里就不展开讲述了,只是建议如果客户端没有按日期排序功能需求的话,那么把日期时间直接作为字符串传递就好,可以省去很多麻烦。

JSON实例:

 1 // 描述一个人
 2
 3 var person = {
 4     "Name": "Bob",
 5     "Age": 32,
 6     "Company": "IBM",
 7     "Engineer": true
 8 }
 9
10 // 获取这个人的信息
11
12 var personAge = person.Age;
13
14 // 描述几个人
15
16 var members = [
17     {
18         "Name": "Bob",
19         "Age": 32,
20         "Company": "IBM",
21         "Engineer": true
22     },
23     {
24         "Name": "John",
25         "Age": 20,
26         "Company": "Oracle",
27         "Engineer": false
28     },
29     {
30         "Name": "Henry",
31         "Age": 45,
32         "Company": "Microsoft",
33         "Engineer": false
34     }
35 ]
36
37 // 读取其中John的公司名称
38
39 var johnsCompany = members[1].Company;
40
41 // 描述一次会议
42
43 var conference = {
44     "Conference": "Future Marketing",
45     "Date": "2012-6-1",
46     "Address": "Beijing",
47     "Members":
48     [
49         {
50             "Name": "Bob",
51             "Age": 32,
52             "Company": "IBM",
53             "Engineer": true
54         },
55         {
56             "Name": "John",
57             "Age": 20,
58             "Company": "Oracle",
59             "Engineer": false
60         },
61         {
62             "Name": "Henry",
63             "Age": 45,
64             "Company": "Microsoft",
65             "Engineer": false
66         }
67     ]
68 }
69
70 // 读取参会者Henry是否工程师
71
72 var henryIsAnEngineer = conference.Members[2].Engineer;

关于JSON,就说这么多,更多细节请在开发过程中查阅资料深入学习。

 什么是JSONP?

先说说JSONP是怎么产生的:

其实网上关于JSONP的讲解有很多,但却千篇一律,而且云里雾里,对于很多刚接触的人来讲理解起来有些困难,小可不才,试着用自己的方式来阐释一下这个问题,看看是否有帮助。

1、一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准;

2、不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有"src"这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>);

3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;

4、恰巧我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据;

5、这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。

6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。

7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

如果对于callback参数如何使用还有些模糊的话,我们后面会有具体的实例来讲解。

JSONP的客户端具体实现:

JSONP(JSON with Padding)是JSON的 一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的<script> 元素是一个例外。利用 <script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 资料,而这种使用模式就是所谓的 JSONP。用 JSONP 抓到的资料并不是 JSON,而是任意的JavaScript,用 JavaScript 直译器执行而不是用 JSON 解析器解析。

到这里,应该明白了,JSON是一种轻量级的数据交换格式,像xml一样,是用来描述数据间的。JSONP是一种使用JSON数据的方式,返回的不是JSON对象,是包含JSON对象的javaScript脚本。

那JSONP是如何工作的呢,我们知道,由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源。若要跨域请求出于安全性考虑是不行的,但是我们发现,Web页面上调用js文件时则不受是否跨域的影响,而且拥有”src”这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>,这时候,聪明的程序猿就想到了变通的方法,如果要进行跨域请求, 通过使用html的script标记来进行跨域请求,并在响应中返回要执行的script代码,其中可以直接使用JSON传递 javascript对象。即在跨域的服务端生成JSON数据,然后包装成script脚本回传,着不就突破同源策略的限制,解决了跨域访问的问题了么。不管jQuery也好,extjs也罢,又或者是其他支持jsonp的框架,他们幕后所做的工作都是一样的,下面我来循序渐进的说明一下jsonp在客户端的实现:

Html前端:

 1  <script src="scripts/jquery-3.1.1.min.js"></script>
 2     <script>
 3         CallWebServiceByJsonp();
 4         function CallWebServiceByJsonp() {
 5             var strCparent = {
 6                 run: "UrlReferrer",
 7                 referrer:document.referrer
 8             }
 9             $.ajax({
10                 type: "Get",
11                 cache: false,
12                 async:false,
13                 url: "http://localhost:18065/WebService.asmx/Getinfo",
14                 data: JSON.stringify(strCparent),
15                 contentType: "application/json; charset=utf-8",
16                 dataType: "jsonp",
17                 jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为:callback)
18                 jsonpCallback: "OnGetMemberSuccessByjsonp",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名
19                 //success: function (data) {
20                 //    alert(JSON.stringify(data));
21                 //},
22                 //error: function (XMLHttpRequest, textStatus, errorThrown) {
23                 //    myAlert("请求数据异常,状态码:" + XMLHttpRequest.status);
24                 //}
25             });
26         }
27         function OnGetMemberSuccessByjsonp(data) {
28             //处理data
29             alert(JSON.stringify(data));
30         }
31     </script>

WebsService:

  1 using Newtonsoft.Json;
  2 using System;
  3 using System.Collections.Generic;
  4 using System.Linq;
  5 using System.Web;
  6 using System.Web.Script.Serialization;
  7 using System.Web.Script.Services;
  8 using System.Web.Services;
  9
 10 namespace API
 11 {
 12     /// <summary>
 13     /// WebService 的摘要说明
 14     /// </summary>
 15     [WebService(Namespace = "http://tempuri.org/")]
 16     [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
 17     [System.ComponentModel.ToolboxItem(false)]
 18     // 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行。
 19     // [System.Web.Script.Services.ScriptService]
 20     public class WebService : System.Web.Services.WebService
 21     {
 22
 23         [WebMethod]
 24        [ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = true)]
 25         public void Getinfo()
 26         {
 27             HttpContext.Current.Response.ContentType = "application/json;charset=utf-8";
 28             string jsonCallBackFunName = HttpContext.Current.Request.Params["callback"].ToString();
 29             string strCparent = HttpUtility.UrlDecode(HttpContext.Current.Request.Params.ToString());
 30             //string jsonCallBackFunName1 = HttpContext.Current.Request.QueryString["callback"].Trim();
 31             //#region -把多个string拼接成json对象-
 32             //Dictionary<string, string> lt = new Dictionary<string, string>();
 33             //lt.Add("run", run);
 34             //lt.Add("referrer", referrer);
 35             //string strCparent = JsonConvert.SerializeObject(lt).ToString();
 36             //#endregion
 37             if (strCparent != "")
 38             {
 39                 if (strCparent.IndexOf("}") > 0)
 40                 {
 41                     strCparent = strCparent.Substring(strCparent.IndexOf("{"), strCparent.IndexOf("}") - strCparent.IndexOf("{") + 1);
 42                     //获取接口首字段Run值,识别命令执行方法
 43                     Run run = new Run();
 44                     JavaScriptSerializer serializer = new JavaScriptSerializer();
 45                     string x = HttpUtility.UrlDecode(strCparent);
 46                     run = serializer.Deserialize<Run>(HttpUtility.UrlDecode(strCparent));
 47
 48                     //访问来源过滤
 49                     if (run.run == "UrlReferrer")
 50                     {
 51
 52                         UrlReferrer tmp = new UrlReferrer();
 53
 54                         tmp = serializer.Deserialize<UrlReferrer>(HttpUtility.UrlDecode(strCparent));
 55
 56                         string referrer = tmp.referrer;
 57
 58                         bool isfree = true;
 59                         string source = null;
 60                         string keyword = null;
 61
 62                         if (referrer.Contains("baidu.com/baidu.php?"))
 63                         {
 64                             //百度广告
 65                             //wd=上海长城宽带
 66                             keyword = referrer.Substring(referrer.IndexOf("&wd=") + 4).Substring(0, referrer.Substring(referrer.IndexOf("&wd=") + 4).IndexOf("&"));
 67                             isfree = false;
 68                             source = "百度推广";
 69
 70                         }
 71
 72                         if (referrer.Contains("baidu.com/link?url"))
 73                         {
 74                             //百度自然流量,无法获取关键词
 75                             source = "百度自然";
 76                         }
 77
 78                         if (referrer.Contains("so.com/link?url"))
 79                         {
 80                             //360广告
 81                             //q=长城宽带
 82
 83                             keyword = (referrer.Substring(referrer.IndexOf("q=") + 2).IndexOf("&") >= 0) ? referrer.Substring(referrer.IndexOf("q=") + 2).Substring(0, referrer.Substring(referrer.IndexOf("q=") + 2).IndexOf("&")) : referrer.Substring(referrer.IndexOf("q=") + 2);
 84                             isfree = false;
 85                             source = "360推广";
 86                         }
 87
 88                         if (referrer.Contains("so.com/link?url"))
 89                         {
 90                             //360自然流量
 91                             //q=长城宽带
 92
 93                             keyword = (referrer.Substring(referrer.IndexOf("q=") + 2).IndexOf("&") >= 0) ? referrer.Substring(referrer.IndexOf("q=") + 2).Substring(0, referrer.Substring(referrer.IndexOf("q=") + 2).IndexOf("&")) : referrer.Substring(referrer.IndexOf("q=") + 2);
 94                             source = "360自然";
 95
 96                         }
 97
 98                         if (referrer.Contains("sogou.com/link?url="))
 99                         {
100                             //搜狗自然流量
101                             //query=上海长城宽带
102                             keyword = (referrer.Substring(referrer.IndexOf("query=") + 6).IndexOf("&") >= 0) ? referrer.Substring(referrer.IndexOf("query=") + 6).Substring(0, referrer.Substring(referrer.IndexOf("query=") + 6).IndexOf("&")) : referrer.Substring(referrer.IndexOf("query=") + 6);
103                             source = "搜狗自然";
104                         }
105
106                         UrlReferrer_Return urlreferrer_return = new UrlReferrer_Return { isfree = isfree, keyword = keyword, source = source };
107                         //JSON序列化
108                         string Data = serializer.Serialize(urlreferrer_return);
109
110                         //下面代码必须
111                         HttpContext.Current.Response.Write(string.Format("{0}({1})", jsonCallBackFunName, Data));
112                         HttpContext.Current.Response.End();
113                     }
114                 }
115             }
116         }
117         [WebMethod]
118         public string HelloWorld()
119         {
120             return "Hello World";
121         }
122         public class Run
123         {
124             public string run { get; set; }
125
126         }
127
128         public class UrlReferrer
129         {
130             public string referrer { get; set; }
131         }
132
133         public class UrlReferrer_Return
134         {
135             public bool isfree { get; set; }
136
137             public string keyword { get; set; }
138
139             public string source { get; set; }
140         }
141     }
142 }

Config加入如下配置:

1  <webServices>
2       <protocols>
3         <add name= "HttpPost"/>
4         <add name= "HttpGet"/>
5       </protocols>
6     </webServices>

如上所示,前端的CallWebServiceByJsonp方法采用jQuery的ajax方法调用后端的Web服务GetInfo方法,后台的GetInfo方法,使用前端的回调方法OnGetMemberSuccessByjsonp包装后台的业务操作的JSON对象,返回给前端一段javascript片段执行。巧妙的解决了跨域访问问题。

JSONP的缺点:

JSONP不提供错误处理。如果动态插入的代码正常运行,你可以得到返回,但是如果失败了,那么什么都不会发生。

时间: 2024-12-26 08:15:43

说说JSON和JSONP,浅析JSONP解决AJAX跨域问题的相关文章

深入浅出JSONP:解决AJAX跨域问题

本文主要讲解了如何去解决AJAX跨域的问题,从跨域的简单原理到JONP实现模式详细的讲解整个解决方案,最后利用jQuery可以很方便的实现JSONP来进行跨域访问. 上周客户新买了服务器,原本在旧的服务器上放着客户的Web主页信息和一个后台程序(asp.net),在客户的主页中有一个动态显示最新消息的处理,这个处理就是通过ajax异步从那个后台程序中取得的.由于又购买了新的服务器,客户想把web主页和那个后台程序分开来,后台程序被部署到了新的服务器上.不过这个项目是我的同事小福同志开发的,也就由

jQuery中利用JSONP解决AJAX跨域问题

写在前面 跨域的解决方案有多种,其中最常见的是使用同一服务器下的代理来获取远端数据,再通过ajax进行读取,而在这期间经过了两次请求过程,使得获取数据的效率大大降低,这篇文章蓝飞就为大家介绍一下解决跨域问题的一种比较通用的方案——JSONP. 什么是跨域? 简单的来说,出于安全方面的考虑,页面中的JavaScript无法访问其他服务器上的数据,即“同源策略”.而跨域就是通过某些手段来绕过同源策略限制,实现不同服务器之间通信的效果. 具体策略限制情况可看下表: URL 说明 允许通信 http:/

解决Ajax 跨域问题 - JSONP原理解析

解决Ajax 跨域问题 - JSONP原理解析 为什么会有跨域问题? - 因为有同源策略 同源策略是浏览器的一种安全策略,所谓同源指的是 请求URL地址中的 协议, 域名 和 端口 都相同,只要其中之一不相同就是跨域 同源策略主要为了保证浏览器的安全性 在同源策略下,浏览器**不允许**Ajax跨域获取服务器数据 http://www.example.com/detail.html 跨域请求: http://api.example.com/detail.html 域名不同 http://www.

浅析JSONP-解决Ajax跨域访问问题

浅析JSONP-解决Ajax跨域访问问题 很久没有写随笔了,总是感觉没时间,其实时间就是...废话少说,前几天,工作上有一新需求,需要前端web页面异步调用后台的Webservice方法返回信息.实现方法有多种,本例采用jQuery+Ajax,完成后,在本地调试了一切ok,但是部署到服务器上以后就出现问题了,后台服务调用没有响应,怎么回事?代码没怎么改动,唯一修改的地方就是jQuery的ajax方法中的url地址.难道是这里的问题,经过检查和调试,发现原来是同源策略在作怪,我们知道,JavaSc

如何解决ajax跨域问题(转)

最近项目中用到json作为系统间交互的手段,自然就伴随着众多ajax请求,随之而来的就是要解决 ajax的跨域问题.本篇将讲述一个小白从遇到跨域不知道是跨域问题,到知道是跨域问题不知道如何解决,再到解决跨域问题,最后找到两种方法解决ajax 跨域问题的全过程. 不知是跨域问题 起 因是这样的,为了复用,减少重复开发,单独开发了一个用户权限管理系统,共其他系统获取认证与授权信息,暂且称之为A系统:调用A系统以B为例.在B系统 中用ajax调用A系统系统的接口(数据格式为json),当时特别困惑,在

如何解决ajax跨域问题

如何解决ajax跨域问题(转) 由 于此前很少写前端的代码(哈哈,不合格的程序员啊),最近项目中用到json作为系统间交互的手段,自然就伴随着众多ajax请求,随之而来的就是要解决 ajax的跨域问题.本篇将讲述一个小白从遇到跨域不知道是跨域问题,到知道是跨域问题不知道如何解决,再到解决跨域问题,最后找到两种方法解决ajax 跨域问题的全过程. 不知是跨域问题 起 因是这样的,为了复用,减少重复开发,单独开发了一个用户权限管理系统,共其他系统获取认证与授权信息,暂且称之为A系统:调用A系统以B为

解决Ajax跨域问题:Origin xx is not allowed by Access-Control-Allow-Origin.

解决Ajax跨域问题:Origin xx is not allowed by Access-Control-Allow-Origin. 今天一个Ajax跨域问题,纠结我半天,记录之. <html> <head> <title>title</title> <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script> <script>

如何解决ajax跨域请求?

1.什么是跨域? a.域名不同 b.域名相同,端口不同 注:只有域名相同,端口相同时,才可以访问数据 可以使用jsonp解决ajax跨域请求的问题. 2.什么是jsonp? Jsonp其实就是一个跨域解决方案.Js跨域请求是不可以的,但是js跨域请求js脚本是可以的.可以把数据封装成一个js语句,做一个方法的调用.跨域请求js脚本可以得到此脚本.得到js脚本之后会立即执行.可以把数据作为参数传递到方法中.就可以获得数据.从而解决跨域请求的问题. 原文地址:https://www.cnblogs.

用iframe设置代理解决ajax跨域请求问题

面对ajax跨域请求的问题,想用代理的方式来解决这个跨域问题.在服务器端创建一个静态的代理页面,在客户端用iframe调用这个代理 今天在项目中需要做远程数据加载并渲染页面,直到开发阶段才意识到ajax跨域请求的问题.于是想用代理的方式来解决这个跨域问题. 什么是跨域?简单的来说,出于安全方面的考虑,页面中的JavaScript无法访问其他服务器上的数据,即"同源策略".而跨域就是通过某些手段来绕过同源策略限制,实现不同服务器之间通信的效果. 方案:在服务器端创建一个静态的代理页面,在