寫在前面
本篇主要內容是結合“XMLHttpRequest Level 1”規范和w3school網站中針對XML DOM - XMLHttpRequest 對象的介紹寫的,其中大量的代碼段都是來自于XMLHttpRequest Level 1規范。由于英語水平有限,如有翻譯錯誤或理解偏差之處,請告知。
簡介
XMLHttpRequest 對象是一組獲取資源的API。
XMLHttpRequest 對象提供了對 HTTP 協議的完全的訪問,包括做出 POST 和 HEAD 請求以及普通的 GET 請求的能力。XMLHttpRequest 可以同步或異步地返回 Web 服務器的響應,并且能夠以文本或者一個 DOM 文檔的形式返回內容。
這個對象的名稱“XMLHttpRequest”中的每個組件(XML,Http,Request)都非常具有誤導性,之所以取這個名稱更多的是為了適合Web。之所以說這個名稱具有誤導性主要有以下三點:
首先,這個對象支持任何基于文本(TEXT)的數據格式,包括XML;
其次、可以使用它創建基于HTTP和HTTPS協議(有些實現還支持其他協議,但這不屬于本規范)的請求(Request);
最后,它支持HTTP協議中所有的方法,并且可以處理Response響應。
瀏覽器兼容性
XMLHttpRequest 得到了所有現代瀏覽器較好的支持。唯一的瀏覽器依賴性涉及 XMLHttpRequest 對象的創建。在 IE 5 和 IE 6 中,必須使用特定于 IE 的 ActiveXObject() 構造函數。IE7及更高版本的IE對XMLHttpRequest對象的創建與其他瀏覽器(Chrome,Firefox等)一樣。
XMLHttpRequest 接口定義
下來的代碼來自W3C的“XMLHttpRequest Level 1”規范。
[NoInterfaceObject]
interface XMLHttpRequestEventTarget : EventTarget {
// 事件句柄
attribute EventHandler onloadstart;
attribute EventHandler onprogress;
attribute EventHandler onabort;
attribute EventHandler onerror;
attribute EventHandler onload;
attribute EventHandler ontimeout;
attribute EventHandler onloadend;
};
interface XMLHttpRequestUpload : XMLHttpRequestEventTarget {
};
enum XMLHttpRequestResponseType {
"",
"arraybuffer",
"blob",
"document",
"json",
"text"
};
[Constructor]
interface XMLHttpRequest : XMLHttpRequestEventTarget {
// event handler
attribute EventHandler onreadystatechange;
// states
const unsigned short UNSENT = 0;
const unsigned short OPENED = 1;
const unsigned short HEADERS_RECEIVED = 2;
const unsigned short LOADING = 3;
const unsigned short DONE = 4;
readonly attribute unsigned short readyState;
// request
void open(ByteString method, [EnsureUTF16] DOMString url);
void open(ByteString method, [EnsureUTF16] DOMString url, boolean async, optional [EnsureUTF16] DOMString? username = null, optional [EnsureUTF16] DOMString? password = null);
void setRequestHeader(ByteString header, ByteString value);
attribute unsigned long timeout;
attribute boolean withCredentials;
readonly attribute XMLHttpRequestUpload upload;
void send(optional (ArrayBufferView or Blob or Document or [EnsureUTF16] DOMString or FormData)? data = null);
void abort();
// response
readonly attribute unsigned short status;
readonly attribute ByteString statusText;
ByteString? getResponseHeader(ByteString header);
ByteString getAllResponseHeaders();
void overrideMimeType(DOMString mime);
attribute XMLHttpRequestResponseType responseType;
readonly attribute any response;
readonly attribute DOMString responseText;
readonly attribute Document? responseXML;
};
每一個XMLHttpRequest對象都有一個唯一XMLHttpRequestUpload對象與之相關聯。
如何創建XMLHttpRequest對象
在瀏覽器兼容性一節中已經介紹過,所有現代瀏覽器創建XMLHttpRequest的方法已經統一了,如下代碼所示。
client= newXMLHttpRequest();
// 返回一個XMLHttpRequest對象
XMLHttpRequest對象的屬性
關于XMLHttpRequest 對象的屬性,在W3School中的介紹對程序員來說更有參考價值,而“XMLHttpRequest Level 1規范”中的介紹更適合XMLHttpRequest對象的實現者研讀。下面的介紹多來自于W3School。
readyState
HTTP 請求的狀態.當一個 XMLHttpRequest 初次創建時,這個屬性的值從 0 開始,直到接收到完整的 HTTP 響應,這個值增加到 4。
5 個狀態中每一個都有一個相關聯的非正式的名稱,下表列出了狀態、名稱和含義:
| 狀態 | 名稱 | 描述 |
| -- | -- | --| -- |
| 0 | Uninitialized | 初始化狀態。XMLHttpRequest 對象已創建或已被 abort() 方法重置。|
| 1 | Open | open() 方法已調用,但是 send() 方法未調用。請求還沒有被發送。|
| 2 | Sent | Send() 方法已調用,HTTP 請求已發送到 Web 服務器。未接收到響應。|
| 3 | Receiving | 所有響應頭部都已經接收到。響應體開始接收但未完成。|
| 4 | Loaded | HTTP 響應已經完全接收。|
readyState 的值不會遞減,除非當一個請求在處理過程中的時候調用了 abort() 或 open() 方法。每次這個屬性的值增加的時候,都會觸發 onreadystatechange 事件句柄。
responseText
目前為止為XMLHttpRequest接收到的響應體(不包括頭部),或者如果還沒有接收到數據的話,就是空字符串。
如果 readyState 小于 3,這個屬性就是一個空字符串。當 readyState 為 3,這個屬性返回目前已經接收的響應部分。如果 readyState 為 4,這個屬性保存了完整的響應體。
如果響應包含了為響應體指定字符編碼的頭部,就使用該編碼。否則,假定使用 Unicode UTF-8。
responseXML
XMLHttpRequest接收到的響應,解析為 XML 并作為Document 對象返回。
status
由服務器返回的HTTP 狀態代碼,如 200 表示成功,而 404 表示 "Not Found" 錯誤。當 readyState 小于 3 的時候讀取這一屬性會導致一個異常。
statusText
這個屬性用名稱而不是數字指定了請求的 HTTP 的狀態代碼。也就是說,當狀態為 200 的時候它是 "OK",當狀態為 404 的時候它是 "Not Found"。和 status 屬性一樣,當 readyState 小于 3 的時候讀取這一屬性會導致一個異常。
timeout
以毫秒為單位的一個超時時間,這個時間是相對于請求開始時間計算的。
如果被設置為一個非0值,那么請求將在開始(調用send方法時)后計時,在執行時間達到timeout指定的時間如果還沒有完成請求將被強行中止;同時會有一個超時事件被觸發,或者拋出一個 TimeoutError 異常。
需要注意的是,如果執行的是同步請求,設置timeout為非0值會拋出 InvalidAccessError 異常。
另外,timeout屬性的值可以在send方法調用之后、請求完成之前的任何時間進行設置,并且無論何時設置,計時都是從調用send方法的那一刻開始的。
事件句柄
以下是XMLHttpRequest對象支持的事件句柄(及其相應的事件句柄所對應的事件類型),這些是繼承自XMLHttpRequestEventTarget的接口對象必須實現的屬性:
事件句柄 | 事件名 | 事件類型 | 何時觸發 |
---|---|---|---|
onloadstart | loadstart | ProgressEvent | 請求開始時 |
onprogress | progress | ProgressEvent | 數據傳輸的過程中 可能會觸發多次 |
onabort | abort | ProgressEvent | 請求被中止時,如調用 abort() 方法時 |
onerror | error | ProgressEvent | 請求出錯時 |
onload | load | ProgressEvent | 請求成功完成時(強調語法成功) |
ontimeout | timeout | ProgressEvent | 在請求完成之前,并已經達到指定的超時時間 |
onloadend | loadend | ProgressEvent | 請求完成時(無論成功或失敗) |
下面也是XMLHttpRequest對象支持的事件句柄(及其相應的事件句柄所對應的事件類型),這些只能通過XMLHttpRequest對象支持:
事件句柄 | 事件名 | 事件類型 | 何時觸發 |
---|---|---|---|
onreadystatechange | readystatechange | Event | readyState屬性變化時 |
每次 readyState 屬性改變的時候都會調用 onreadystatechange 事件句柄函數。當 readyState 為 3 時,它也可能調用多次。
方法
abort()
取消當前響應,關閉連接并且結束任何未完成的網絡活動。
這個方法把 XMLHttpRequest 對象重置為 readyState 為 0 的狀態,并且取消所有未完成的網絡活動。例如,如果請求用了太長時間,而且響應不再必要的時候,可以調用這個方法。
getResponseHeader()
返回指定的 HTTP 響應頭部的值。其參數是要返回的 HTTP 響應頭部的名稱。可以使用任何大小寫來制定這個頭部名字,和響應頭部的比較是不區分大小寫的。
該方法的返回值是指定的 HTTP 響應頭部的值,如果沒有接收到這個頭部或者 readyState 小于 3 則為空字符串。如果接收到多個有指定名稱的頭部,這個頭部的值被連接起來并返回,使用逗號和空格分隔開各個頭部的值。
getAllResponseHeaders()
把 HTTP 響應頭部作為未解析的字符串返回。
如果 readyState 小于 3,這個方法返回 null。否則,它返回服務器發送的所有 HTTP 響應的頭部。頭部作為單個的字符串返回,一行一個頭部。每行用換行符 "\r\n" 隔開。
open()
初始化 HTTP 請求參數,例如 URL 和 HTTP 方法,但是并不發送請求。
語法
xhr.open(method, url[, async[, username[, password]]])
method 參數是用于請求的 HTTP 方法。值包括 GET、POST 和 HEAD。
url 參數是請求的主體。大多數瀏覽器實施了一個同源安全策略,并且要求這個 URL 與包含腳本的文本具有相同的主機名和端口。
async 參數指示請求使用應該異步地執行。如果這個參數是 false,請求是同步的,后續對 send() 的調用將阻塞,直到響應完全接收。如果這個參數是 true 或省略,請求是異步的,且通常需要一個 onreadystatechange 事件句柄。
username 和 password 參數是可選的,為 url 所需的授權提供認證資格。如果指定了,它們會覆蓋 url 自己指定的任何資格。
說明
這個方法初始化請求參數以供 send() 方法稍后使用。它把 readyState 設置為 1,刪除之前指定的所有請求頭部,以及之前接收的所有響應頭部,并且把 responseText、responseXML、status 以及 statusText 參數設置為它們的默認值。當 readyState 為 0 的時候(當 XMLHttpRequest 對象剛創建或者 abort() 方法調用后)以及當 readyState 為 4 時(已經接收響應時),調用這個方法是安全的。當針對任何其他狀態調用的時候,open() 方法的行為是未指定的。
除了保存供 send() 方法使用的請求參數,以及重置 XMLHttpRequest 對象以便復用,open() 方法沒有其他的行為。要特別注意,當這個方法調用的時候,實現通常不會打開一個到 Web 服務器的網絡連接。
注意事項
如果method不是一個有效的HTTP方法或url不能被解析,將拋出TypeError異常。
setRequestHeader()
向一個打開但未發送的請求設置或添加一個 HTTP 請求頭。
語法
setRequestHeader(name, value)
name 參數是要設置的頭部的名稱。這個參數不應該包括空白、冒號或換行。
value 參數是頭部的值。這個參數不應該包括換行。
說明
setRequestHeader() 方法指定了一個 HTTP 請求的頭部,它應該包含在通過后續 send() 調用而發布的請求中。這個方法只有當 readyState 為 1 的時候才能調用,也就是說必須在調用了 open() 之后,且在調用 send() 之前。
如果帶有指定名稱的頭部已經被指定了,這個頭部的新值就是:之前指定的值,加上逗號、空白以及這個調用指定的值。
如果 open() 調用指定了認證資格(給定了name和password參數),XMLHttpRequest 自動發送一個適當的 Authorization 請求頭部。但是,你可以使用 setRequestHeader() 來添加這個頭部。如果 Web 服務器已經保存并傳遞給 open() 的 URL 相關聯的 cookie,適當的 Cookie 或 Cookie2 頭部也自動地包含到請求中。也可以通過調用 setRequestHeader() 來把這些 cookie 添加到頭部。XMLHttpRequest 也可以為 User-Agent 頭部提供一個默認值。如果這么做了,你為該頭部指定的任何值都會添加到這個默認值后面。
有些請求頭部由 XMLHttpRequest 自動設置而不是由這個方法設置,以符合 HTTP 協議。這包括如下和代理相關的頭部:
- Host
- Connection
- Keep-Alive
- Accept-charset
- Accept-Encoding
- If-Modified-Since
- If-None-Match
- If-Range
- Range
send()
發送 HTTP 請求,使用傳遞給 open() 方法的參數,以及傳遞給該方法的可選請求體。
語法
xhr.send(data)
如果通過調用 open() 指定的 HTTP 方法是 POST 或 PUT,data 參數指定了請求實體的主體,data 的類型將在下面詳細介紹。如果請求體不是必須的話,這個參數就為 null。對于任何其他方法,這個參數是不可用的,應該為 null(有些實現不允許省略該參數)。
說明
這個方法導致一個 HTTP 請求發送。如果之前沒有調用 open(),或者更具體地說,如果 readyState 不是 1,send() 拋出一個異常。否則,它發送一個 HTTP 請求,該請求由以下幾部分組成:
- 之前調用 open() 時指定的 HTTP 方法、URL 以及認證資格(如果有的話)。
- 之前調用 setRequestHeader() 時指定的請求頭部(如果有的話)。
- 傳遞給這個方法的body參數。
一旦請求發布了,send() 把 readyState 設置為 2,并觸發 onreadystatechange 事件句柄。
如果之前調用的 open() 參數 async 為 false,這個方法會阻塞并不會返回,直到 readyState 為 4 并且服務器的響應被完全接收。如果 async 參數為 true,或者這個參數省略了,send() 立即返回,并且正如后面所介紹的,服務器響應將在一個后臺線程中處理。
如果服務器響應帶有一個 HTTP 重定向,send() 方法或后臺線程自動遵從重定向。當所有的 HTTP 響應頭部已經接收,send() 或后臺線程把 readyState 設置為 3 并觸發 onreadystatechange 事件句柄。如果響應較長,send() 或后臺線程可能在狀態 3 中多次觸發 onreadystatechange 事件句柄:這可以作為一個下載進度指示器。最后,當響應完成,send() 或后臺線程把 readyState 設置為 4,并最后一次觸發 onreadystatechange 事件句柄。
data 參數的有效類型
ArrayBufferView
如果 data 的類型為ArrayBufferView,則設置請求實體的主體(Request Entiry Body)為 data 中的數據。
Blob 對象
如果data是Blob對象,則設置請求實體的主體(Request Entiry Body)為 data 中的數據。
如果 Blob 對象的屬性不是空字符串,則設置 MIME-Type 為Blob的類型。
document 對象
如果data是document對象,會進行如下設置:
- 設置Request請求的編碼為UTF-8;
- 如果 data 是html document對象,則設置 MIME-Type 為 “text/html” 或 “application/xml”,并在MIME-Type后追加字符“;charset=UTF-8”。
- 對請求實體的主體(Request Entiry Body)進行序列化,并編碼 UTF-8 格式。
注意:如果數據不能被序列化,將拋出一個“InvalidStateError”異常。
string
如果 data 是一個字符串,則把字符串的內容設置為請求實體的主體,并把請求的編碼設置為UTF-8;設置MIME-Type為“text/plain;charset=UTF-8”;然后對請求實體的主體進行UTF-8編碼。
FormData
data為FormData類型時會進行如下操作:
- 對data數據進行UTF-8編碼后使用“multipart/form-data”算法處理,將返回的結果設置為請求實體的主體。“multipart/form-data”算法會生成“boundary=”字符串,并使用 boundary 字符分隔開請求實體的主體中的各部分數據。
- 設置MIME-Type的值為“multipart/form-data”
FormData 接口
以下代碼來自W3C的“XMLHttpRequest Level 1”規范。
[Constructor(optional HTMLFormElement form )]
interfaceFormData {
void append([EnsureUTF16] DOMStringname, Blob value, optional EnsureUTF16] DOMStringfilename );
void append([EnsureUTF16] DOMStringname, [EnsureUTF16] DOMStringvalue);
};
FormData中的數據是一系列條目(entry)的有序列表;這些 entry 必須包含name和value兩個屬性。
如何得到 FormData對象
fd = new FormData([form]);
數據form是可選的,如果傳了form參數,它必須是一全form元素的DOM對象。
如何向FormData對象中添加數據
fd.append(name,value[,filename]);
參數 name 必須是一個字符串;value 可以是一個字符中,也可以是一個Blob對象;如果value是一個Blob對象時,filename參數是可選的,用來說明Blob對象的名字。
FormData 小結
之所以要在這里寫個小結,是因為這里介紹的FormData對象太過簡單了;其實FormData對象還有很多方法和屬性,并且組成FormData對象的條目(entry)對象也是需要花大段文字來說明的。由于篇幅和時間限制,這里就先簡單介紹一下,后續我會對FomData對象寫一篇專門的介紹。
剛發布不久就有朋友反應FomData部分的內容看不懂;不得以,只能加班把FomData介紹的文章趕出來。如果你也有同樣的困惑請移步Ajax之魂-WEB中的XMLHttpRequest規范:附FormData詳解。
總結
其實很不想寫總結,因為還有很多東西沒有介紹清楚。有些東西在W3C的“XMLHttpRequest Level 1”規范寫的很詳細,由于本人英文水平限制,有些內容無法解理,還有些理解了但無法準備的翻譯成中文。如果有想要完全了解XMLHttpRequest對象的同學,可以去W3C官網看一下XMLHttpRequest Level 1規范的英文原文。
在本篇中,有很多依賴的內容沒有做詳細的解決,如:Blob對象,ArrayBufferView 對象,DOM 4 規范等。其實這些內容都有對應的規范,有些我是看過了解一些,有些也只是知道,并沒有詳細看過規范文檔。由于作者是一個求知欲很強的人,所以那些沒有看過的文檔肯定會抽出時間來研讀,屆時一定也會整理出文章分享給大家。而那些已經看過有點了解的規范,會在我每有閑暇之時就整理出來分享給大家。有興趣的同學可以關注一下。