Ajax之魂-WEB中的XMLHttpRequest規范

寫在前面

本篇主要內容是結合“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 請求,該請求由以下幾部分組成:

  1. 之前調用 open() 時指定的 HTTP 方法、URL 以及認證資格(如果有的話)。
  2. 之前調用 setRequestHeader() 時指定的請求頭部(如果有的話)。
  3. 傳遞給這個方法的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對象,會進行如下設置:

  1. 設置Request請求的編碼為UTF-8;
  2. 如果 data 是html document對象,則設置 MIME-Type 為 “text/html” 或 “application/xml”,并在MIME-Type后追加字符“;charset=UTF-8”。
  3. 對請求實體的主體(Request Entiry Body)進行序列化,并編碼 UTF-8 格式。

注意:如果數據不能被序列化,將拋出一個“InvalidStateError”異常。

string

如果 data 是一個字符串,則把字符串的內容設置為請求實體的主體,并把請求的編碼設置為UTF-8;設置MIME-Type為“text/plain;charset=UTF-8”;然后對請求實體的主體進行UTF-8編碼。

FormData

data為FormData類型時會進行如下操作:

  1. 對data數據進行UTF-8編碼后使用“multipart/form-data”算法處理,將返回的結果設置為請求實體的主體。“multipart/form-data”算法會生成“boundary=”字符串,并使用 boundary 字符分隔開請求實體的主體中的各部分數據。
  2. 設置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 規范等。其實這些內容都有對應的規范,有些我是看過了解一些,有些也只是知道,并沒有詳細看過規范文檔。由于作者是一個求知欲很強的人,所以那些沒有看過的文檔肯定會抽出時間來研讀,屆時一定也會整理出文章分享給大家。而那些已經看過有點了解的規范,會在我每有閑暇之時就整理出來分享給大家。有興趣的同學可以關注一下。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容