21.1 XMLHttpRequest 對象【JavaScript高級程序設計第三版】

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

//適用於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 構造函數。html

var xhr = new XMLHttpRequest();

假如你只想支持IE7 及更高版本,那麼大可丟掉前面定義的那個函數,而只用原生的XHR 實現。可是,若是你必須還要支持IE 的早期版本,那麼則能夠在這個createXHR()函數中加入對原生XHR對象的支持。api

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()方法:app

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 GMT
Server: Apache/1.3.29 (Unix)
Vary: Accept
X-Powered-By: PHP/4.3.8
Connection: close
Content-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 取得提交的數據了:

<?php
header("Content-Type: text/plain");
echo <<<EOF
Name: {$_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

相關文章
相關標籤/搜索