XMLHttpRequest(XHR)是一個(gè)API對(duì)象,其中的方法可以用來(lái)在瀏覽器和服務(wù)器端傳輸數(shù)據(jù)。這個(gè)對(duì)象是瀏覽器的js環(huán)境提供的。從XHR獲取數(shù)據(jù)的目的是為了持續(xù)修改一個(gè)加載過(guò)的頁(yè)面,XHR是Ajax設(shè)計(jì)的底層概念。XHR使用的協(xié)議不同于HTTP,不僅可以使用XML格式的數(shù)據(jù),也支持JSON,HTML或者純文本。
WHATWG組織負(fù)責(zé)維護(hù)一個(gè)動(dòng)態(tài)的XHR標(biāo)準(zhǔn)文檔。W3C基于WHATWG標(biāo)準(zhǔn)創(chuàng)建了一個(gè)固定的規(guī)范。
歷史
XMLHttpRequest對(duì)象背后的概念最開始是被微軟Outlook Web Access工作組為Microsoft Exchange Server 2000提出的。一個(gè)IXMLHTTPRequest接口被開發(fā)出來(lái),第二代的MSXML庫(kù)實(shí)現(xiàn)了這個(gè)概念。1999年的發(fā)布的IE5使用了第二代的MSXML庫(kù),通過(guò)ActiveX訪問(wèn)IXMLHTTPRequest接口,這個(gè)接口在MSXML中通過(guò)XMLHTTP包裝。
IE5,IE6沒有在他們的腳本語(yǔ)言中定義XMLHttpRequest對(duì)象的標(biāo)識(shí)符,當(dāng)時(shí)IE5,IE6發(fā)布時(shí),XMLHttpRequest標(biāo)識(shí)符本身還不是一個(gè)標(biāo)準(zhǔn)。如果XMLHttpRequest標(biāo)識(shí)符不存在,通過(guò)對(duì)象檢測(cè)可以獲得向后兼容性。微軟在2006年發(fā)布的IE7時(shí),定義了XMLHttpRequest對(duì)象標(biāo)識(shí)符。
Mozilla項(xiàng)目組為Gecko布局引擎開發(fā)實(shí)現(xiàn)的接口稱為nsIXMLHttpRequest。這個(gè)接口被建模成盡可能的接近微軟的IXMLHTTPRequest接口。Mozilla通過(guò)js對(duì)象為這個(gè)接口創(chuàng)建了一個(gè)包裝器稱為XMLHttpRequest。XMLHttpRequest首次可用是在2000年12月6號(hào)發(fā)布的Gecko 0.6版本中,但是還不是全功能版本直到2002年6月5號(hào)發(fā)布的1.0版本的Gecko。XMLHttpRequest對(duì)象在其他主要的web客戶端中變成了一個(gè)事實(shí)標(biāo)準(zhǔn),在2004年2月發(fā)布得Safari 1.2版本,2005年4月發(fā)布的Konqueror,Opera8.0版本,2005年9月發(fā)布的iCab 3.0b352版本中都實(shí)現(xiàn)了這個(gè)對(duì)象。
隨著跨瀏覽器JS庫(kù)(例如jQuery)流行,開發(fā)者再調(diào)用XMLHttpRequest功能時(shí)不用再直接接觸底層API。
標(biāo)準(zhǔn)
W3C在2006年4月5號(hào)發(fā)布了一個(gè)關(guān)于XMLHttpRequest對(duì)象的工作草案規(guī)范,起草人是Opera的Anne van Kesteren和W3C的Dean Jackson。它的目標(biāo)是“基于現(xiàn)有的實(shí)現(xiàn),文檔化一個(gè)最小的可以互相協(xié)作的特性,以便web開發(fā)者可以不用編寫特定平臺(tái)代碼來(lái)使用這些特性”。
W3C在2008年2月25號(hào)又發(fā)布了另一個(gè)關(guān)于XMLHttpRequest對(duì)象的工作草案,稱為"XMLHttpRequest Level 2"。這個(gè)版本的XMLHttpRequest包括了XMLHttpRequest對(duì)象的擴(kuò)展功能,例如事件處理,支持跨域請(qǐng)求,支持處理字節(jié)流。2011年底,這個(gè)規(guī)范被遺棄了,其中的內(nèi)容收錄在原始的規(guī)范中。
在2012年底,WHATWG接管了這個(gè)事情,并且用Web IDL定義了一個(gè)標(biāo)準(zhǔn)。W3C目前的草案就是基于WHATWG標(biāo)準(zhǔn)創(chuàng)建的。
HTTP請(qǐng)求
下面的章節(jié)展示了符合W3C工作草案標(biāo)準(zhǔn)的用戶代理如何使用XMLHttpRequest對(duì)象功能發(fā)起http請(qǐng)求。因?yàn)閃3C關(guān)于XMLHttpRequest對(duì)象的標(biāo)準(zhǔn)仍然是一個(gè)草案,所以用戶代理可能沒有完全實(shí)現(xiàn)草案規(guī)定的功能,也就是下面的這些是有可能變化的。當(dāng)使用XMLHttpRequest對(duì)象的腳本跨用戶代理使用時(shí),要考慮下這種影響。本文將試著列出主要的用戶代理之間不一致的地方。
open方法
XMLHttpRequest對(duì)象的HTTP和HTTPS請(qǐng)求必須通過(guò)opent方法初始化。這個(gè)方法必須在實(shí)際發(fā)送請(qǐng)求之前調(diào)用,以用來(lái)驗(yàn)證請(qǐng)求方法,URL以及用戶信息。這個(gè)方法不能確保URL存在或者用戶信息必須正確。初始化請(qǐng)求可以接受5個(gè)參數(shù),
open( Method, URL, Asynchronous, UserName, Password )
第一個(gè)參數(shù)是一個(gè)字符串值標(biāo)識(shí)HTTP的請(qǐng)求方法。請(qǐng)求方法必須是用戶代理支持的方法以及W3C的XMLHttpRequest對(duì)象草案規(guī)定的方法,如下:
- GET (IE7+,Mozilla 1+)
- POST (IE7+,Mozilla 1+)
- HEAD (IE7+)
- PUT
- DELETE
- OPTIONS (IE7+)
然而請(qǐng)求方法并不限于以上列出的這些。W3C草案聲明瀏覽器可以自行決定支持的請(qǐng)求方法。
第二個(gè)參數(shù)也是一個(gè)字符串值,標(biāo)示請(qǐng)求的URL。W3C推薦當(dāng)有跨域請(qǐng)求時(shí),瀏覽器應(yīng)該報(bào)個(gè)錯(cuò)誤。
第三個(gè)參數(shù)是一個(gè)布爾值類型,標(biāo)示請(qǐng)求是否是異步的,在W3C草案中并不是一個(gè)必須參數(shù)。如果沒有提供,符合W3C規(guī)范的用戶代理應(yīng)該默認(rèn)為true。異步請(qǐng)求("true")不會(huì)等待服務(wù)器響應(yīng)在繼續(xù)執(zhí)行其他腳本之前,可以調(diào)用XMLHttpRequest對(duì)象的onreadystatechange事件監(jiān)聽器來(lái)獲取請(qǐng)求的不同狀態(tài)。一個(gè)同步的請(qǐng)求("false")會(huì)阻塞js執(zhí)行直到請(qǐng)求完成,這時(shí)就沒必要調(diào)用onreadystatechange事件監(jiān)聽器。
第四個(gè)和第五個(gè)參數(shù)分別是用戶名和密碼。這些參數(shù)是服務(wù)端為了驗(yàn)證請(qǐng)求使用的。
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", filepath , false);
xmlhttp.send(null);
}
sendRequestHeader方法
在成功初始化請(qǐng)求之后,XMLHttpRequest對(duì)象的setRequestHeader方法可以用來(lái)設(shè)置請(qǐng)求頭。
setRequestHeader( Name, Value )
第一個(gè)參數(shù)是header的字符串名稱,第二個(gè)參數(shù)是字符串值。如果請(qǐng)求需要多個(gè)header,這個(gè)方法就要被調(diào)用多次。這個(gè)方法附加的請(qǐng)求頭,在下次open方法調(diào)用時(shí)會(huì)被清空。
send方法
XMLHttpRequest對(duì)象的send方法用來(lái)發(fā)送請(qǐng)求,這個(gè)方法接收一個(gè)參數(shù),這個(gè)參數(shù)就是要發(fā)送的內(nèi)容。
send(Data)
如果不需要發(fā)送內(nèi)容,這個(gè)參數(shù)可以省略。W3C草案聲明這個(gè)參數(shù)可以是任意類型的值只要能被js轉(zhuǎn)成字符串,除了DOM對(duì)象。如果用戶代理無(wú)法序列化這個(gè)參數(shù),這個(gè)參數(shù)會(huì)被忽略。Firefox3.0.x以及之前版本在send
方法沒傳參數(shù)時(shí)會(huì)拋出異常。
如果參數(shù)是DOM對(duì)象,用戶代理應(yīng)該確保文檔已經(jīng)被轉(zhuǎn)成XML格式,通過(guò)文檔對(duì)象的inputEncoding屬性編碼。如果請(qǐng)求頭的Content-Type還沒有通過(guò)setRequestHeader方法設(shè)置,用戶代理應(yīng)該自動(dòng)的增加一個(gè)值"application/xml;charset=charset",其中的charset應(yīng)該是用來(lái)編碼文檔的編碼格式。
如果用戶代理被配置成使用代理服務(wù)器,XMLHttpRequest對(duì)象應(yīng)該適當(dāng)修改請(qǐng)求連接代理而不是源服務(wù)器,發(fā)送Proxy-Authorization
頭配置。
onreadystatechange事件監(jiān)聽器
如果XMLHttpRequest對(duì)象的send
方法第三個(gè)參數(shù)是true,也就是發(fā)送了異步請(qǐng)求,onreadystatechange
事件監(jiān)聽器將自動(dòng)在XMLHttpRequest對(duì)象的readyState
屬性改變時(shí)被觸發(fā)。
狀態(tài)改變過(guò)程如下:
- 當(dāng)
open
方法被成功調(diào)用,readyState
屬性被置為1(OPEND) - 當(dāng)
send
方法被調(diào)用,成功接收到HTTP響應(yīng)頭,readyState
屬性被置為2(HEADERS_RECEIVED) - 一旦HTTP響應(yīng)內(nèi)容開始加載,
readyState
屬性被置為3(LOADING) - 一旦HTTP響應(yīng)內(nèi)容結(jié)束加載,
readyState
屬性被置為4(DONE)
當(dāng)監(jiān)聽器被定義之后,每次狀態(tài)改變時(shí)都會(huì)觸發(fā)。為了檢測(cè)狀態(tài)1和狀態(tài)2,監(jiān)聽器必須在open
方法調(diào)用前調(diào)用。open
方法必須在send
方法調(diào)用前調(diào)用。
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
var DONE = this.DONE || 4;
if (this.readyState === DONE){
alert(this.readyState);
}
};
request.open('GET', 'somepage.xml', true);
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
request.send(null);
abort方法
如果XMLHttpRequest對(duì)象的readyState
屬性還沒有變成4,這個(gè)方法可以終止請(qǐng)求。這個(gè)方法可以確保異步請(qǐng)求中的回調(diào)不被執(zhí)行。
abort()
一些AJAX庫(kù)使用abort
方法來(lái)取消潛在重復(fù)請(qǐng)求以及無(wú)序請(qǐng)求。
HTTP響應(yīng)
當(dāng)成功調(diào)用XMLHttpRequest對(duì)象的send
方法之后,如果服務(wù)端響應(yīng)式格式正確的XML,并且已經(jīng)把Content-Type
頭設(shè)置成用戶代理支持的XML類型,XMLHttpRequest對(duì)象的responseXML
屬性將會(huì)包含一個(gè)DOM文檔對(duì)象。另外一個(gè)屬性responseText
將會(huì)包含服務(wù)端返回的文本類型,而不管它是否被理解為XML。
跨域請(qǐng)求
早起的web開發(fā)中,通過(guò)使用js在一個(gè)web站點(diǎn)和另一個(gè)不安全的站點(diǎn)交換信息的方式,很容易突破用戶的安全防線。因此所有的現(xiàn)代瀏覽器實(shí)現(xiàn)了一個(gè)同源策略來(lái)阻止類似攻擊,例如跨站腳本。XMLHttpRequest數(shù)據(jù)也受這種安全策略支配,但是有時(shí)web開發(fā)想有意的規(guī)避這種限制。因?yàn)橛袝r(shí)需要合法使用子域,例如在foo.example.com
域上的頁(yè)面發(fā)送XMLHttpRequest請(qǐng)求獲取bar.example.com
域上的數(shù)據(jù)通常會(huì)失敗。
存在各種規(guī)避這種安全策略的方法,例如可以使用JSONP,跨域資源共享(CORS),或者flash,silverlight插件。跨域XMLHttpRequest請(qǐng)求在W3C的XMLHttpRequest Level 2規(guī)范中有提及。IE10+才支持CORS。IE8,IE9提供類似功能的XDomainRequest API。
CORS協(xié)議也有幾點(diǎn)限制,支持兩種模式。簡(jiǎn)單模式不允許設(shè)置自定義頭并且忽略cookie,只支持HEAD,GET,POST請(qǐng)求方法,其中POST方法只允許"text/plain","application/x-www-urlencoded","multipart/form-data"的MIME類型,最初支持的只有"text/plain"類型。另一個(gè)模式檢測(cè)何時(shí)請(qǐng)求復(fù)雜特性之一,然后給服務(wù)端發(fā)送一個(gè)請(qǐng)求確認(rèn)來(lái)協(xié)商特性。
做好前端開發(fā)必須對(duì)HTTP的相關(guān)知識(shí)有所了解,所以我創(chuàng)建了一個(gè)專題前端必備HTTP技能專門收集前端相關(guān)的HTTP知識(shí),歡迎關(guān)注,投稿。
PS:本文翻譯自維基百科,原文地址https://en.wikipedia.org/wiki/XMLHttpRequest