21.1 XMLHttpRequest 對象【JavaScript進階程式設計第三版】

來源:互聯網
上載者:User

標籤:3.2   xmlhttp   tar   另一個   XML   使用者   ring   法則   重用   

IE5 是第一款引入XHR 對象的瀏覽器。在IE5 中,XHR 對象是通過MSXML 庫中的一個ActiveX對象實現的。因此,在IE 中可能會遇到三種不同版本的XHR 對象,即MSXML2.XMLHttp、MSXML2.XMLHttp.3.0 和MXSML2.XMLHttp.6.0。要使用MSXML 庫中的XHR 對象,需要像第18章討論建立XML 文檔時一樣,編寫一個函數,例如:

//適用於IE7 之前的版本function createXHR() {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) {//跳過}}}return new ActiveXObject(arguments.callee.activeXString);}

這個函數會儘力根據IE 中可用的MSXML 庫的情況建立最新版本的XHR 對象。
IE7+、Firefox、Opera、Chrome 和Safari 都支援原生的XHR 對象,在這些瀏覽器中建立XHR 對象要像下面這樣使用XMLHttpRequest 建構函式。

var xhr = new XMLHttpRequest();

假如你只想支援IE7 及更高版本,那麼大可丟掉前面定義的那個函數,而只用原生的XHR 實現。但是,如果你必須還要支援IE 的早期版本,那麼則可以在這個createXHR()函數中加入對原生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) {//跳過}}}return new ActiveXObject(arguments.callee.activeXString);} else {throw new Error("No XHR object available.");}}

運行一下
這個函數中新增的代碼首先檢測原生XHR 對象是否存在,如果存在則返回它的新執行個體。如果原生對象不存在,則檢測ActiveX 對象。如果這兩種對象都不存在,就拋出一個錯誤。然後,就可以使用下面的代碼在所有瀏覽器中建立XHR 對象了。

var xhr = createXHR();

由於其他瀏覽器中對XHR 的實現與IE 最早的實現是相容的,因此就可以在所有瀏覽器中都以相同方式使用上面建立的xhr 對象。

21.1.1 XHR的用法

在使用XHR 對象時,要調用的第一個方法是open(),它接受3 個參數:要發送的請求的類型("get"、"post"等)、請求的URL 和表示是否非同步發送請求的布爾值。下面就是調用這個方法的例子。

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

這行代碼會啟動一個針對example.php 的GET 請求。有關這行代碼,需要說明兩點:一是URL相對於執行代碼的當前頁面(當然也可以使用絕對路徑);二是調用open()方法並不會真正發送請求,而只是啟動一個請求以備發送。

只能向同一個域中使用相同連接埠和協議的URL 發送請求。如果URL 與啟動請求的頁面有任何差別,都會引發安全錯誤。

要發送特定的請求,必須像下面這樣調用send()方法:

xhr.open("get", "example.txt", false);xhr.send(null);

運行一下
這裡的send()方法接收一個參數,即要作為請求主體發送的資料。如果不需要通過請求主體發送資料,則必須傳入null,因為這個參數對有些瀏覽器來說是必需的。調用send()之後,請求就會被指派到伺服器。
由於這次請求是同步的,JavaScript 代碼會等到伺服器響應之後再繼續執行。在收到響應後,響應的資料會自動填滿XHR 對象的屬性,相關的屬性簡介如下。

  • responseText:作為響應主體被返回的文本。
  • responseXML:如果響應的內容類型是"text/xml"或"application/xml",這個屬性中將儲存包含著響應資料的XML DOM 文檔。
  • status:響應的HTTP 狀態。
  • statusText:HTTP 狀態的說明。

在接收到響應後,第一步是檢查status 屬性,以確定響應已經成功返回。一般來說,可以將HTTP狀態碼為200 作為成功的標誌。此時,responseText 屬性的內容已經就緒,而且在內容類型正確的情況下,responseXML 也應該能夠訪問了。此外,狀態碼為304 表示請求的資源並沒有被修改,可以直接使用瀏覽器中緩衝的版本;當然,也意味著響應是有效。為確保接收到適當的響應,應該像下面這樣檢查上述這兩種狀態碼:

xhr.open("get", "example.txt", false);xhr.send(null);if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {alert(xhr.responseText);} else {alert("Request was unsuccessful: " + xhr.status);}

運行一下
根據返回的狀態碼,這個例子可能會顯示由伺服器返回的內容,也可能會顯示一條錯誤訊息。我們建議讀者要通過檢測status 來決定下一步的操作,不要依賴statusText,因為後者在跨瀏覽器使用時不太可靠。另外,無論內容類型是什麼,響應主體的內容都會儲存到responseText 屬性中;而對於非XML 資料而言,responseXML 屬性的值將為null。

有的瀏覽器會錯誤地報告204 狀態碼。IE 中XHR 的ActiveX 版本會將204 設定為1223,而IE 中原生的XHR 則會將204 正常化為200。Opera 會在取得204 時報告status 的值為0。

像前面這樣發送同步請求當然沒有問題,但多數情況下,我們還是要發送非同步請求,才能讓JavaScript 繼續執行而不必等待響應。此時,可以檢測XHR 對象的readyState 屬性,該屬性工作表示請求/響應過程的當前活動階段。這個屬性可取的值如下。

  • 0:未初始化。尚未調用open()方法。
  • 1:啟動。已經調用open()方法,但尚未調用send()方法。
  • 2:發送。已經調用send()方法,但尚未接收到響應。
  • 3:接收。已經接收到部分響應資料。
  • 4:完成。已經接收到全部響應資料,而且已經可以在用戶端使用了。

只要readyState 屬性的值由一個值變成另一個值,都會觸發一次readystatechange 事件。可以利用這個事件來檢測每次狀態變化後readyState 的值。通常,我們只對readyState 值為4 的階段感興趣,因為這時所有資料都已經就緒。不過,必須在調用open()之前指定onreadystatechange事件處理常式才能確保跨瀏覽器安全色性。下面來看一個例子。

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.txt", true);xhr.send(null);

運行一下
以上代碼利用DOM 0 級方法為XHR 對象添加了事件處理常式,原因是並非所有瀏覽器都支援DOM 2級方法。與其他事件處理常式不同,這裡沒有向onreadystatechange 事件處理常式中傳遞event 對象;必須通過XHR 對象本身來確定下一步該怎麼做。

這個例子在onreadystatechange 事件處理常式中使用了xhr 對象,沒有使用this 對象,原因是onreadystatechange 事件處理常式的範圍問題。如果使用this 對象,在有的瀏覽器中會導致函數執行失敗,或者導致錯誤發生。因此,使用實際的XHR 對象執行個體變數是較為可靠的一種方式。

另外,在接收到響應之前還可以調用abort()方法來取消非同步請求,如下所示:

xhr.abort();

調用這個方法後,XHR 對象會停止觸發事件,而且也不再允許訪問任何與響應有關的對象屬性。在終止請求之後,還應該對XHR 對象進行解引用操作。由於記憶體原因,不建議重用XHR 對象。

21.1.2 HTTP頭部資訊

每個HTTP 要求和響應都會帶有相應的頭部資訊,其中有的對開發人員有用,有的也沒有什麼用。XHR 對象也提供了操作這兩種頭部(即要求標頭部和回應標頭部)資訊的方法。預設情況下,在發送XHR 請求的同時,還會發送下列頭部資訊。

  • Accept:瀏覽器能夠處理的內容類型。
  • Accept-Charset:瀏覽器能夠顯示的字元集。
  • Accept-Encoding:瀏覽器能夠處理的壓縮編碼。
  • Accept-Language:瀏覽器當前設定的語言。
  • Connection:瀏覽器與伺服器之間串連的類型。
  • Cookie:當前版面設定的任何Cookie。
  • Host:發出請求的頁面所在的域 。
  • Referer:發出請求的頁面的URI。注意,HTTP 規範將這個頭部欄位拼字錯了,而為保證與規範一致,也只能將錯就錯了。(這個英文單詞的正確拼法應該是referrer。)
  • User-Agent:瀏覽器的使用者代理程式字串。

雖然不同瀏覽器實際發送的頭部資訊會有所不同,但以上列出的基本上是所有瀏覽器都會發送的。使用setRequestHeader()方法可以設定自訂的要求標頭部資訊。這個方法接受兩個參數:頭部欄位的名稱和頭部欄位的值。要成功發送要求標頭部資訊,必須在調用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);

運行一下
伺服器在接收到這種自訂的頭部資訊之後,可以執行相應的後續操作。我們建議讀者使用自訂的頭部欄位名稱,不要使用瀏覽器正常發送的欄位名稱,否則有可能會影響伺服器的響應。有的瀏覽器允許開發人員重寫預設的頭部資訊,但有的瀏覽器則不允許這樣做。
調用XHR 對象的getResponseHeader()方法並傳入頭部欄位名稱,可以取得相應的回應標頭部資訊。而調用getAllResponseHeaders()方法則可以取得一個包含所有頭部資訊的長字串。來看下面的例子。

var myHeader = xhr.getResponseHeader("MyHeader");var allHeaders = xhr.getAllResponseHeaders();

在伺服器端,也可以利用頭部資訊向瀏覽器發送額外的、結構化的資料。在沒有自訂資訊的情況下,getAllResponseHeaders()方法通常會返回如下所示的多行常值內容:

Date: Sun, 14 Nov 2004 18:04:03 GMTServer: Apache/1.3.29 (Unix)Vary: AcceptX-Powered-By: PHP/4.3.8Connection: closeContent-Type: text/html; charset=iso-8859-1

這種格式化的輸出可以方便我們檢查響應中所有頭部欄位的名稱,而不必一個一個地檢查某個欄位是否存在。

21.1.3 GET請求

GET 是最常見的請求類型,最常用於向伺服器查詢某些資訊。必要時,可以將查詢字串參數追加到URL 的末尾,以便將資訊發送給伺服器。對XHR 而言,位於傳入open()方法的URL 末尾的查詢字串必須經過正確的編碼才行。
使用GET 請求經常會發生的一個錯誤,就是查詢字串的格式有問題。查詢字串中每個參數的名稱和值都必須使用encodeURIComponent()進行編碼,然後才能放到URL 的末尾;而且所有名-值對兒都必須由和號(&)分隔,如下面的例子所示。

xhr.open("get", "example.php?name1=value1&name2=value2", true);

下面這個函數可以輔助向現有URL 的末尾添加查詢字串參數:

function addURLParam(url, name, value) {url += (url.indexOf("?") == -1 ? "?": "&");url += encodeURIComponent(name) + "=" + encodeURIComponent(value);return url;}

這個addURLParam()函數接受三個參數:要添加參數的URL、參數的名稱和參數的值。這個函數首先檢查URL 是否包含問號(以確定是否已經有參數存在)。如果沒有,就添加一個問號;否則,就添加一個和號。然後,將參數名稱和值進行編碼,再添加到URL 的末尾。最後返回添加參數之後的URL。
下面是使用這個函數來構建請求URL 的樣本。

var url = "example.php";//添加參數url = addURLParam(url, "name", "Nicholas");url = addURLParam(url, "book", "Professional JavaScript");//初始化請求xhr.open("get", url, false);

在這裡使用addURLParam()函數可以確保查詢字串的格式良好,並可靠地用於XHR 對象。

21.1.4 POST請求

使用頻率僅次於GET 的是POST 請求,通常用於向伺服器發送應該被儲存的資料。POST 請求應該把資料作為請求的主體提交,而GET 請求傳統上不是這樣。POST 請求的主體可以包含非常多的資料,而且格式不限。在open()方法第一個參數的位置傳入"post",就可以初始化一個POST 請求,如下面的例子所示。

xhr.open("post", "example.php", true);

發送POST 請求的第二步就是向send()方法中傳入某些資料。由於XHR 最初的設計主要是為了處理XML,因此可以在此傳入XML DOM 文檔,傳入的文檔經序列化之後將作為請求主體被提交到伺服器。當然,也可以在此傳入任何想發送到伺服器的字串。
預設情況下,伺服器對POST 請求和提交Web 表單的請求並不會一視同仁。因此,伺服器端必須有程式來讀取發送過來的未經處理資料,並從中解析出有用的部分。不過,我們可以使用XHR 來模仿表單提交:首先將Content-Type 頭部資訊設定為application/x-www-form-urlencoded,也就是表單提交時的內容類型,其次是以適當的格式建立一個字串。第14 章曾經討論過,POST 資料的格式與查詢字串格式相同。如果需要將頁面中表單的資料進行序列化,然後再通過XHR 發送到伺服器,那麼就可以使用第14 章介紹的serialize()函數來建立這個字串:

function submitData() {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("post", "postexample.php", true);xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");var form = document.getElementById("user-info");xhr.send(serialize(form));}

運行一下
這個函數可以將ID 為"user-info"的表單中的資料序列化之後發送給伺服器。而下面的樣本PHP檔案postexample.php 就可以通過$_POST 取得提交的資料了:

<?phpheader("Content-Type: text/plain");echo <<<EOFName: {$_POST[‘user-name’]}Email: {$_POST[‘user-email’]}EOF;?>

如果不設定Content-Type 頭部資訊,那麼發送給伺服器的資料就不會出現在$_POST 超級全域變數中。這時候,要訪問同樣的資料,就必須藉助$HTTP_RAW_POST_DATA。

與GET 請求相比,POST 請求消耗的資源會更多一些。從效能角度來看,以發送相同的資料計,GET 請求的速度最多可達到POST 請求的兩倍。

下載離線版教程:http://www.shouce.ren/api/view/a/15218

21.1 XMLHttpRequest 對象【JavaScript進階程式設計第三版】

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.