AJAX 的出現使得網頁能夠經過在後臺與服務器進行少許數據交換,實現網頁的局部刷新。可是出於安全的考慮,ajax不容許跨域通訊。若是嘗試從不一樣的域請求數據,就會出現錯誤。若是能控制數據駐留的遠程服務器而且每一個請求都前往同一域,就能夠避免這些安全錯誤。可是,若是僅停留在本身的服務器上,Web 應用程序還有什麼用處呢?若是須要從多個第三方服務器收集數據時,又該怎麼辦?javascript
一、Ajax爲何不能跨域?究竟是卡在哪一個環節了?(下面項目中具體說,這裏先說下結論)。 Ajax其實就是向服務器發送一個GET或POST請求,而後取得服務器響應結果,返回客戶端。理論上這是沒有任何問題的,然而普通ajax跨域請求,在服務器端不會有任何問題,只是服務端響應數據返回給瀏覽器的時候,瀏覽器根據響應頭的Access-Control-Allow-Origin字段的值來判斷是否有權限獲取數據,通常狀況下,服務器端若是沒有在這個字段作特殊處理的話,跨域是沒有權限訪問的,因此響應數據被瀏覽器給攔截了,因此在ajax回調函數裏是獲取不到數據的(來自園友補充)。因此如今ajax跨域的問題能夠轉化爲數據怎麼拿回客戶端的問題。css
二、既然不能直接訪問第三方站點,咱們能夠在服務器上面作代理,經過ajax向代理髮送請求,代理得到數據後在返回給客戶端,固然這是一種解決辦法,可是一次請求要從客戶端通過代理到第三方站點,而後再原路返回,響應速度是個問題。html
三、咱們發現咱們能夠將一些js、css等文件放在第三方的服務器上面,如CDN等來加快網頁的打開速度,這樣是沒有任何問題的,也就是說web頁面能夠加載放在任意站點的js、css、圖片等資源,不會受到"跨域"的影響。這個時候,咱們會想到:既然咱們能夠調用第三方站點的js,那麼若是咱們將數據放到第三方站點的js中不就能夠將數據帶到客戶端了嗎?java
下面咱們來作一個實驗,來驗證一下咱們的猜測成不成立:jquery
打開Visual Studio,新建一個Web項目,這裏用WebForm,而後咱們在項目中添加一個名爲remoteJs的js文件,寫入以下代碼:web
function GetRemoteData() { return "remote data"; }
很簡單,就一個方法,返回一個字符串,下面咱們來寫一個客戶端調用,既然是跨域,那就寫個html靜態頁面來測試吧,新建local.html,輸入如下代碼:ajax
<!DOCTYPE html> <html> <head> <title>本地站點</title> <meta charset="UTF-8"> <script type="text/javascript" src="http://localhost:4071/remoteJs.js"></script> </head> <script type="text/javascript"> var data = GetRemoteData(); alert(data); </script> <body> </body> </html>
讓咱們的Web項目跑起來,而後打開local.html,能夠看到彈出一個窗口,顯示信息remote data。這裏證實咱們的想法是正確的。接下來的問題是,咱們如何根據須要發送請求和獲取請求的結果呢?下面咱們來認識一下JSONP。json
一、什麼是JSONP跨域
JSONP(JSON with Padding)是JSON的一種「使用模式」,可用於解決主流瀏覽器的跨域數據訪問的問題。其核心思想是利用JS標籤裏面的跨域特性進行跨域數據訪問,在JS標籤裏面存在的是一個跨域的URL,實際執行的時候經過這個URL得到一段字符串,這段返回的字符串必須是一個合法的JS調用,經過EVAL這個字符串來完成對得到的數據的處理。瀏覽器
JSONP是一個非官方的協議,它容許在服務器端集成Script tags返回至客戶端,經過javascript callback的形式實現跨域訪問(這僅僅是JSONP簡單的實現形式)。
二、JSONP的實現
下面咱們經過一個例子來講明一下JSONP是如何實現ajax跨域請求的。這裏咱們模擬圖書館圖書的查詢,在剛剛咱們創建的web項目裏面添加一個名爲SearchBook的通常處理程序,寫入以下代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace BookLibrary { public class SearchBook : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; string callback = context.Request["callback"]; context.Response.Write(callback + "({'BookName':'English','Pages':562})"); } public bool IsReusable { get { return false; } } } }
暫時先不解釋,咱們寫完客戶端看到效果後在詳細說明,而後修改剛剛的local.html,代碼以下:
<!DOCTYPE html> <html> <head> <title>跨域請求</title> <meta charset="UTF-8"> </head> <body> <input type="button" value="發送請求" onclick="GetAjaxData();" /> </body> <script type="text/javascript"> var GetData = function (data) { alert(data.BookName + " " + data.Pages); }; function GetAjaxData(){ var url = "http://localhost:4071/SearchBook.ashx?callback=GetData"; var script = document.createElement('script'); script.setAttribute('src', url); document.getElementsByTagName('head')[0].appendChild(script); } </script> </html>
這個html頁面有一個按鈕,綁定方法GetAjaxData,當咱們單擊發送請求時,就會向Web站點發送請求,獲取查詢的數據,咱們讓Web站點跑起來,而後打開local.html,單擊按鈕,看到彈出以下信息:
咱們成功的取得了web站點的數據,實現了跨域請求。下面咱們來講一下他的實現原理:
var url = "http://localhost:4071/SearchBook.ashx?callback=GetData";
這一行代碼咱們定義了請求的url,問號前面的是web站點通常處理程序SearchBook的地址,問號後面咱們傳入了一個參數callback,值爲GetData,也就是咱們上面定義的方法名,及回調函數名稱。固然咱們能夠傳入更多的參數。
var script = document.createElement('script'); script.setAttribute('src', url); document.getElementsByTagName('head')[0].appendChild(script);
這三行代碼就是添加script節點,url指向第三方站點,執行結果如圖:
那麼咱們剛剛寫的通常處理程序返回的結果就不用說了吧,他就是返回一個字符串,內容爲:
看到這裏清楚了吧,就是第三方站點生成一個對回調函數的調用,傳入查詢結果,而後經過<script>加載到客戶端執行。看到這裏是否是想到了什麼?是否是和C#裏面的委託有些共同點?總體流程如圖:
能夠看到,整個過程,本地站點一直處於主動的地位,主動的發送請求,主動的加載遠程js.而第三方站點則處於被動的響應。
三、普通Ajax請求在哪一個環節出錯了
下面,咱們用JQuery的ajax來講明一下ajax請求究竟是卡在哪一個環節了,修改GetAjaxData方法以下:
$.ajax({ type: "get", async: false, url: "http://localhost:4071/SearchBook.ashx", dataType: "text", success: function (data) { alert(data.BookName + " " + data.Pages); }, error: function () { alert('fail'); } });
在SearchBook裏面context.Response.Write(callback + "({'BookName':'English','Pages':562})");這行下端點,而後運行,會發現能夠走到斷點,而後就出錯了。
四、用JQuery實現ajax跨域
其實JQuery裏面也封裝了跨域的ajax方法,咱們來看一下上面的方法用JQuery怎麼寫:
<script type="text/javascript"> function GetAjaxData() { $.ajax({ type: "get", async: false, url: "http://localhost:4071/SearchBook.ashx", dataType: "jsonp", jsonp: "callback",//傳遞給請求處理程序或頁面的,標識jsonp回調函數名(通常爲:callback) jsonpCallback: "GetData",//callback的function名稱 success: function (data) { alert(data.BookName + " " + data.Pages); }, error: function () { alert('fail'); } }); } </script>
注意,JQuery寫法裏面Url後面就不用再寫?來傳遞參數了,jsonp的值至關於?後面的值及參數名稱,jsonpCallback的值就是參數的value.success就是執行成功後調用的方法。
哎,不對啊,怎麼沒有GetData方法了?JQuery究竟是怎麼實現的呢?下面咱們來調試一下JQuery,來看一下里面是怎麼實現的,調試js,固然仍是要用Chrome,看圖:
這張圖中,咱們看到有個對象s,在作url拼接操做,看到選中那行了吧,?後面拼的是s.jsonp,最後拼接的是callbackName.繼續向下走:
咱們看到s.url的值,爲拼接後的值,是否是和咱們本身寫的js代碼裏面的url如出一轍,繼續向下走:
咱們看到JQuery又在剛剛的url後面添加下劃線等號,而後又跟了一串數字,至於什麼用,我也說不上來,繼續向下走:
看到了什麼,success方法,哈哈,這是JQuery在變量參數,繼續走:
看到什麼了?沒錯,這就是JQuery最終調用的方法,最後一行代碼,添加了script節點,和我本身寫js實現的原理同樣。繼續向下走,看看還有什麼:
看到JQuery執行完後,又刪除了剛剛添加的script節點,仍是JQuery想的周到啊~~
下面咱們來看一下,咱們本身寫的js執行後的DOM結構:
看到了吧,script節點會隨着請求的次數一路飆升,不過並不會引發錯誤,刷新後就消失了。而JQuery執行後,DOM結構是不變的。
一、ajax和jsonp這兩種技術在調用方式上「看起來」很像,目的也同樣,都是請求一個url,而後把服務器返回的數據進行處理,所以jquery和ext等框架都把jsonp做爲ajax的一種形式進行了封裝;
二、ajax和jsonp其實本質上是不一樣的東西。ajax的核心是經過XmlHttpRequest獲取非本頁內容,而jsonp的核心則是經過HTTP來動態添加<script>標籤來調用服務器提供的js腳本。
三、其實ajax與jsonp的區別不在因而否跨域,ajax經過服務端代理同樣能夠實現跨域,jsonp自己也不排斥同域的數據的獲取。
四、jsonp是一種方式或者說非強制性協議,如同ajax同樣,它也不必定非要用json格式來傳遞數據,若是你願意,字符串都行,只不過這樣不利於用jsonp提供公開服務。
五、jsonp整個過程當中,本地站點一直處於主動的地位,主動的發送請求,主動的加載遠程js.而第三方站點則處於被動的響應。
做者:雲霏霏
QQ交流羣:243633526
博客地址:http://www.cnblogs.com/yunfeifei/
聲明:本博客原創文字只表明本人工做中在某一時間內總結的觀點或結論,與本人所在單位沒有直接利益關係。非商業,未受權,貼子請以現狀保留,轉載時必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
若是你們感受個人博文對你們有幫助,請推薦支持一把,給我寫做的動力。