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