淺析Ajax跨域原理及JQuery中的實現分析

  AJAX 的出現使得網頁能夠經過在後臺與服務器進行少許數據交換,實現網頁的局部刷新。可是出於安全的考慮,ajax不容許跨域通訊。若是嘗試從不一樣的域請求數據,就會出現錯誤。若是能控制數據駐留的遠程服務器而且每一個請求都前往同一域,就能夠避免這些安全錯誤。可是,若是僅停留在本身的服務器上,Web 應用程序還有什麼用處呢?若是須要從多個第三方服務器收集數據時,又該怎麼辦?javascript

 

 1、關於ajax跨域的思考

  一、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

 

 2、JSONP

 一、什麼是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結構是不變的。

 

 3、總結

    一、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/

 聲明:本博客原創文字只表明本人工做中在某一時間內總結的觀點或結論,與本人所在單位沒有直接利益關係。非商業,未受權,貼子請以現狀保留,轉載時必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。

若是你們感受個人博文對你們有幫助,請推薦支持一把,給我寫做的動力。

相關文章
相關標籤/搜索