JavaScript-Ajax

Ajax通訊與數據格式無關,從服務器獲取的數據不必定是XML數據。javascript

Ajax的核心:XMLHttpRequest對象(簡稱XHR)php

在XHR對象以前,Ajax通訊一般使用hack手段,如使用隱藏的或內嵌的框架。java

XHR對象爲向服務器發送信息和解析服務器響應提供了流暢的接口。json

 

1.XMLHttpRequest對象跨域

IE5是第一款引進XHR對象的瀏覽器,經過MSXML庫中的ActiveX對象實現(有3個版本)。瀏覽器

兼容全部瀏覽器,建立XHR對象:緩存

function createXHR(){
    if (typeof XMLHttpRequest != "undefined"){
        return new XMLHttpRequest();
    } else if (typeof ActiveXObject != "undefined"){
        if (typeof arguments.callee.activeXString != "string"){
            var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
                            "MSXML2.XMLHttp"],
                i, len;

            for (i=0,len=versions.length; i < len; i++){
                try {
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeXString = versions[i];
                    break;
                } catch (ex){
                    //skip
                }
            }
        }

        return new ActiveXObject(arguments.callee.activeXString);
    } else {
        throw new Error("No XHR object available.");
    }
}

以後就能在全部瀏覽器建立XHR對象:var xhr = createrXHR();安全

 

2.原生XHR對象 (支持的瀏覽器: IE7+、FF、Chrome、Opera、Safari)服務器

經過XMLHttpRequest構建函數,建立XHR對象:cookie

xhr =  XMLHttpRequest();

 

3.XHR用法

3.1.open()

open() 3個參數: 發送的類型、請求的URL、表是否異步的布爾值

xhr.open("get","/index", false);

    ①URl爲相對於執行代碼的當前頁,或絕對地址;

    ②false爲同步,JavaScript代碼會在服務器響應後再繼續執行;

    ③調用open()只是啓動一個請求以備發送,還沒真正發送;

    ④只能在同個域中使用相同端口和協議的URL發送請求。

 

3.2.send()

send() 1個參數: 請求主體發送的數據,不須要經過請求主體發送數據則傳入null。

調用send()後,請求被分派到服務器。

xhr.open("get","example.php", false) ;

xhr.send();

 

3.3.收到響應後,響應數據會自動填充XHR對象的屬性:

responseText:做爲響應的主體被返回的文本;

responseXML:若響應的內容類型」text/xml」或」application/xml」,此屬性保存響應數據XML DOM文檔;

status:響應的HTTP狀態;

statusText:HTTP狀態的說明;

 

☆:不管什麼內容類型,響應主體的內容都會保存在responseText屬性中。對於非XML數據,responseXML屬性值爲null。

 

3.4.status屬性確認響應是否成功返回

HTTP狀態代:

    200:響應有效,responseText屬性已就緒,內容類型正確下的responseXML也可訪問。

    304:響應有效,只是請求的資源併爲修改,可直接使用瀏覽器中緩存的版本。

正確檢查上述2種狀態代碼:

if ((xhr.status >= 200 && xhr.status <=300) || xhr.status == 304) {
     alert(xhr.responseText);
} else {
     alert("Request was unsuccessful:" + xhr.status);
};

 

3.5.readystate屬性

該屬性存儲請求/響應過程的當前活動狀態。

    0 : 未初始化,未調用open();

    1 : 啓動,調用了open();

    2 : 發送,調用了send(),未接受響應;

    3 : 接受,已接受部分響應;

    4 : 完成,已接受所有響應,且可在客戶端使用。

 

3.6.readystatechange事件

該事件,在readystate屬性值改變時觸發。

var xhr = createXHR();
xhr.onreadystatechange = function(event){
    if (xhr.readyState == 4){
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
            alert(xhr.responseText);
        } else {
            alert("Request was unsuccessful: " + xhr.status);
        }
    }
};

xhr.open("get", "example.txt", true);

①必須在調用open()以前知道readystatechange事件的事件處理程序,確保兼容。

②該事件處理程序中沒有傳遞event對象,必須經過XHR對象本地來肯定下一步怎麼作;

③使用xhr對象而不使用this對象,是由於onreadystatechange事件處理程序的做用域問題。使用this對象在一些瀏覽器會致使函數執行失敗或發生錯誤。

 

3.7.abort()

調用此方法可取消異步請求:xhr.abort();

調用後,xhr對象中止觸發事件,不容許訪問如何與響應相關的屬性;

終止請求後,應對XHR對象進行解引用操做,不建議重用XHR對象。

 

4.HTTP頭部信息

發送請求時的頭部信息:

    Accept:瀏覽器可以處理的內容類型

    Accept-Charset:瀏覽器可以顯示的字符集

    Accept-Encoding:瀏覽器可以處理的壓縮編碼

    Accept-Language:瀏覽器當前設置的語言

    Connection:瀏覽器與服務器之間鏈接的類型

    Cookie:當前頁面設置的如何Cookie

    Host:發送請求耳洞頁面所在域

    Referer:發出請求的頁面的URI

    User-Agent:瀏覽器的用戶代理字符串

 

setRequestHeader() : 設置自定義頭部信息。2個參數:頭部字段名稱、頭部信息值。

需在open()方法以後調用send()以前調用setRequestHeader(),才能成功發送請求頭部信息。

var xhr = createXHR();       
xhr.onreadystatechange = function(){
    if (xhr.readyState == 4){
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
            alert(xhr.responseText);
        } else {
            alert("Request was unsuccessful: " + xhr.status);
        }
    }
};
xhr.open("get", "example.php", true);
xhr.setRequestHeader("MyHeader", "MyValue");
xhr.send(null);

getRequestHeader() : 獲取指定的相應頭部信息

xhr.getRequestHeader(「MyHeader」);

getAllRequestHeader() : 獲取一個包含全部頭部信息的長字符串

xhr.getAllRequestHeader();

 

5.GET請求

對於XHR對象,位於open()的URL末尾的查詢字符串 需通過編碼,使用encodeURIComponent()編碼。

名-值對需用和號(&)分隔。

自定義函數,添加URL查詢字符串參數:

function submitData(){
    var xhr = createXHR();        
    xhr.onreadystatechange = function(event){
        if (xhr.readyState == 4){
            if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
                alert(xhr.responseText);
            } else {
                alert("Request was unsuccessful: " + xhr.status);

            }

        }

    };

 

6.POST請求

因爲XHR其初的設計是爲了處理XML,故在send()中可傳入XHR DOM文檔。

 

6.1.服務端讀取POST數據

①默認狀況下,服務器對POST請求和提交Web表單不會一視同仁,故服務端須要程序來讀取發送的原始數據,並解析出有用部分。

②XHR模擬表單提交:

    1.將Content-Type頭部信息設置爲application/x-www-form-urlencoded (即表單提交時的內容問題);

    2.以適當格式建立一個字符串。(經過serialize()函數建立該字符串,序列化表單數據)

function submitData(){
    var xhr = createXHR();
    xhr.onreadystatechange = function(event){
        if (xhr.readyState == 4){
            if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
                alert(xhr.responseText);
            } else {
                alert("Request was unsuccessful: " + xhr.status);
            }
        }
    };
    xhr.open("post", "postexample.php", true);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    var form = document.getElementById("user-info");
    xhr.send(serialize(form));
}

 

7.CORS 跨源資源共享

跨域安全策略限制了Ajax的異步通訊,CORS則是定義了跨域時,客戶端和服務器的溝通。

CORS思想:使用自定義HTTP頭部讓瀏覽器與服務器進行溝通,從而決定請求/響應的成功與否。

 

7.1.給一個請求附加Origin頭部,包含請求頁面的源信息(協議、域名 和 端口)

Origin: http://www.domain.com

服務器根據Origin判斷是否接收請求,接收則在Access-Control-Allow-Origin頭部會發相同信息。

Access-Control-Allow-Origin: http://www.domain.com

若無此頭部或頭部信息不匹配,瀏覽器將駁回請求。

☆請求和響應不會包含cookie信息。

 

7.2.IE8+對CORS的實現

IE8引入的XDR(XDomainRequest)類型,類型XHR,可實現安全可靠的跨域通訊。

 

7.2.1.XDR與XHR的不一樣之處:

①cookie不會隨請求發送,也不會隨響應返回;

②只能設置請求頭部信息中的Content-Type字段;

③不能訪問響應頭部信息;

④只支持GET和POST請求

被請求的資源可判斷用戶代理、來源頁面等如何數據 來決定是否設置Access-Control-Allow-Origin頭部

 

7.2.2.XDR使用方法相似XHR,建立一個XDomainRequest實例,調用open(),再調用send()

XDR只能執行異步請求,因此open()方法只有兩個參數,請求的類型和URL。

在收到響應後,只能訪問響應的原始文本,沒法肯定響應的狀態代碼。

只要響應有效就會觸發load事件,響應的數據會保存在responseText屬性中。

若是失敗(如,響應中缺乏Access-Control-Allow-Origin頭部)就會觸發error事件,但該事件無有用信息,須要自定義一個onerror事件句柄。

var xdr = new XDomainRequest();
xdr.onload = function(){
    alert(xdr.responseText);
};
xdr.onerror = function(){
    alert("Error!");
};

xdr.open("get", "http://www.somewhere-else.com/xdr.php");
xdr.send(null);

在請求返回前調用abort()可終止請求。

 

7.2.3.XDR也支持timeout屬性及ontiomout事件處理程序,在運行超過timeout設定的秒數後,調用ontimeout事件句柄

爲支持POST請求,XDR提供了contentType屬性,用於表示發送數據的格式。

contentType屬性是XDR對象影響頭部信息的惟一方式。

xdr.contentType = "application/x-www-form-urlencoded";

 

7.3.其餘瀏覽器對CORS的實現

FF等瀏覽器都經過XMLHttpRequest對象實現了對CORS的原生支持。要請求另外一個域中的資源時,使用標準XHR對象並在open()中傳入絕對URL便可。

var xhr = createXHR();
xhr.onreadystatechange = function(){
    if (xhr.readyState == 4){
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
            alert(xhr.responseText);
        } else {
            alert("Request was unsuccessful: " + xhr.status);
        }
    }
};
xhr.open("get", "http://www.abc.com/page/", true);
xhr.send(null);

與IE不一樣,經過跨域XHR對象能夠訪問status屬性和statusText屬性,也可同步請求。

 

7.3.1.跨域XHR的限制:

①不能使用setRequestHeader()設置自定義頭部;

②不能發送和接收cookie;

③調用getAllResponseHeader()方法總會返回空字符串。

 

7.3.2.不管同源請求仍是跨域請求都是使用相同的接口,故對於本地資源,最好用相對URL,對遠程資源再用絕對URL。

這樣能消除歧義,避免出現限制訪問頭部或本地cookie信息等問題。

 

7.4.跨瀏覽器的CORS(IE8+、FF等)

檢測XHR是否支持CORS的方法:檢查是否存在withCredentials屬性,在結合檢測XDomainRequest對象是否存在。

function createCORSRequest(method, url){
    var xhr = new XMLHttpRequest();
    if ("withCredentials" in xhr){
        xhr.open(method, url, true);
    } else if (typeof XDomainRequest != "undefined"){
        xhr = new XDomainRequest();
        xhr.open(method, url);
    } else {
        xhr = null;
    }
    return xhr;
}

var request = createCORSRequest("get", "http://www.somewhere-else.com/xdr.php");
if (request){
    request.onload = function(){
        //do something with request.responseText
    };
    request.send();
}

上述createCORSRequest()函數返回的對象的屬性(XHR和XDR的共同屬性):

①abort():中止正在進行的請求;

②onerror:用於替代onreadystatechange檢測錯誤;

③onload:用於代替onreadystatechange檢測成功;

④responseText:用於取得響應內容;

⑤send():用於發送請求。

 

8.其餘跨域技術

在CORS出現前,常利用DOM中可以執行跨域請求的功能,在不依賴XHR對象時,也能發送某種請求。

與COSR不一樣的是,不用修改服務器代碼。

 

8.1.圖像Ping

使用<img>標籤,因爲能夠從任何網頁加載圖像,故常是在線廣告跟蹤瀏覽量的主要方式。

可動態建立圖像,使用它們的onload和onerror事件句柄,肯定是否接受到了響應。

var img = new Image();
img.onload = img.onerror = function(){
    alert("Done!");
};

img.src = "http://www.example.com/test?name=Nicholas";

圖像Ping是與服務器進行簡單、單向的跨域通訊的一種方式。請求的數據經過查詢字符串形式發送,響應能夠是任何內容,一般是像素圖或204響應。雖然經過圖像Ping,瀏覽器得不到任何具體數據,但經過偵聽load和error事件,能找到響應收到的時間。

圖像Ping經常使用於跟蹤用戶點擊頁面 或動態廣告曝光次數。

缺點:①只能發送GET請求;②沒法訪問服務器響應文本。

 

8.2.JSONP

JSONP(JSON width Padding)填充式JSON或參數式JSON,相似JSON,是包含在函數調用中的JSON:

callback( {"name" : "value"} );

 

8.2.1.JSONP有兩個部分:回調函數和數據

回調函數:當響應到來時應該在頁面中調用的函數。回調函數的名稱在請求中指定。

數據:傳入回調函數的JSON數據。

 

8.2.2.JSONP經過動態<script>元素,爲其src屬性指定一個跨域的URL。相似<img>元素,即都能不受限制地跨域加載資源。

JSONP爲有效的JavaScript代碼,在請求完成即JSONP響應 加載到頁面後就會當即執行。

function handleResponse(response){
    alert("You're at IP address " + response.ip + ", which is in " + response.city + ", " + response.region_name);
}

var script = document.createElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script, document.body.firstChild);

headleResponse()爲回調函數,將在響應到來後執行。

 

8.2.3.JSONP之因此流行,是由於

①可以直接訪問響應文本;

②支持瀏覽器與服務器之間的雙向通訊。

①JSONP從其餘域加載代碼執行,所以該域必須安全可靠;

②很難確保JSONP請求是否失敗。

 

8.3.Comet 」服務器推送" 

Ajax從頁面想服務器請求數據,Comet則是服務器向頁面推送數據,Comet能近乎實時地向頁面推送信息。

 

8.3.1.實現Comet的2種方式:長輪詢和流

①長輪詢:與短輪詢相反,頁面發送一個請求,服務器一直保持鏈接打開,直到有數據可發送時就向頁面發送數據。接收完數據後瀏覽器關閉鏈接,隨機又發送一個新請求,在頁面打開期間如此循環。【短輪詢是服務器當即發送數據,即便數據無效,長輪詢是等待發送響應。】輪詢的優勢是,全部瀏覽器都支持。經過XHR對象和setTimeout()實現。

②HTTP流:它在網頁的整個生命週期內只使用一個HTTP鏈接。瀏覽器發送一個請求,服務器保持鏈接打開,再週期性向瀏覽器發送數據.

實現HTTP流的關鍵:全部服務器端語言都支持打印到輸出緩存而後刷新的功能。(將輸出緩存中的內容一次性所有發送給客戶端)

var xhr = new XMLHttpRequest(),
    received = 0;
xhr.open("get", url, true);
xhr.onreadystatechange = function(){
    var result;
    if (xhr.readyState == 3){
        //get only the new data and adjust counter
        result = xhr.responseText.substring(received);
        received += result.length;
        //call the progress callback
        progress(result);
    } else if (xhr.readyState == 4){
        finished(xhr.responseText);
    }
};
xhr.send(null);
return xhr;
}
var client = createStreamingClient("streaming.php", function(data){
    alert("Received: " + data);
}, function(data){
    alert("Done!");
});
相關文章
相關標籤/搜索