JSON和JSONP,淺析JSONP解決AJAX跨域問題

說到AJAX就會不可避免的面臨兩個問題,第一個是AJAX以何種格式來交換數據?第二個是跨域的需求如何解決?這兩個問題目前都有不一樣的解決方案,好比數據能夠用自定義字符串或者用XML來描述,跨域能夠經過服務器端代理來解決。javascript

但到目前爲止最被推崇或者說首選的方案仍是用JSON來傳數據,靠JSONP來跨域。而這就是本文將要講述的內容。php

JSON和JSONP雖然只有一個字母的差異,但其實他們根本不是一回事兒:JSON是一種數據交換格式,而JSONP是一種依靠開發人員的聰明才智創造出的一種非官方跨域數據交互協議。咱們拿最近比較火的諜戰片來打個比方,JSON是地下黨們用來書寫和交換情報的「暗號」,而JSONP則是把用暗號書寫的情報傳遞給本身同志時使用的接頭方式。看到沒?一個是描述信息的格式,一個是信息傳遞雙方約定的方法。html

既然隨便聊聊,那咱們就再也不採用教條的方式來說述,而是把關注重心放在幫助開發人員理解是否應當選擇使用以及如何使用上。前端

什麼是JSON?

 前面簡單說了一下,JSON是一種基於文本的數據交換方式,或者叫作數據描述格式,你是否該選用他首先確定要關注它所擁有的優勢。java

JSON的優勢:jquery

一、基於純文本,跨平臺傳遞極其簡單;web

二、Javascript原生支持,後臺語言幾乎所有支持;ajax

三、輕量級數據格式,佔用字符數量極少,特別適合互聯網傳遞;json

四、可讀性較強,雖然比不上XML那麼一目瞭然,但在合理的依次縮進以後仍是很容易識別的;後端

五、容易編寫和解析,固然前提是你要知道數據結構;

JSON的缺點固然也有,但在做者看來實在是可有可無的東西,因此再也不單獨說明。

JSON的格式或者叫規則:

JSON可以以很是簡單的方式來描述數據結構,XML能作的它都能作,所以在跨平臺方面二者徹底不分伯仲。

一、JSON只有兩種數據類型描述符,大括號{}和方括號[],其他英文冒號:是映射符,英文逗號,是分隔符,英文雙引號""是定義符。

二、大括號{}用來描述一組「不一樣類型的無序鍵值對集合」(每一個鍵值對能夠理解爲OOP的屬性描述),方括號[]用來描述一組「相同類型的有序數據集合」(可對應OOP的數組)。

三、上述兩種集合中如有多個子項,則經過英文逗號,進行分隔。

四、鍵值對以英文冒號:進行分隔,而且建議鍵名都加上英文雙引號"",以便於不一樣語言的解析。

五、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的講解有不少,但卻千篇一概,並且雲裏霧裏,對於不少剛接觸的人來說理解起來有些困難,小可不才,試着用本身的方式來闡釋一下這個問題,看看是否有幫助。

一、一個衆所周知的問題,Ajax直接請求普通文件存在跨域無權限訪問的問題,甭管你是靜態頁面、動態網頁、web服務、WCF,只要是跨域請求,一概不許;

二、不過咱們又發現,Web頁面上調用js文件時則不受是否跨域的影響(不只如此,咱們還發現凡是擁有"src"這個屬性的標籤都擁有跨域的能力,好比<script>、<img>、<iframe>);

三、因而能夠判斷,當前階段若是想經過純web端(ActiveX控件、服務端代理、屬於將來的HTML5之Websocket等方式不算)跨域訪問數據就只有一種可能,那就是在遠程服務器上設法把數據裝進js格式的文件裏,供客戶端調用和進一步處理;

四、恰巧咱們已經知道有一種叫作JSON的純字符數據格式能夠簡潔的描述複雜數據,更妙的是JSON還被js原生支持,因此在客戶端幾乎能夠爲所欲爲的處理這種格式的數據;

五、這樣子解決方案就呼之欲出了,web客戶端經過與調用腳本如出一轍的方式,來調用跨域服務器上動態生成的js格式文件(通常以JSON爲後綴),顯而易見,服務器之因此要動態生成JSON文件,目的就在於把客戶端須要的數據裝入進去。

六、客戶端在對JSON文件調用成功以後,也就得到了本身所需的數據,剩下的就是按照本身需求進行處理和展示了,這種獲取遠程數據的方式看起來很是像AJAX,但其實並不同。

七、爲了便於客戶端使用數據,逐漸造成了一種非正式傳輸協議,人們把它稱做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不提供錯誤處理。若是動態插入的代碼正常運行,你能夠獲得返回,可是若是失敗了,那麼什麼都不會發生。

相關文章
相關標籤/搜索