XMLHttpRequest Level 2 使用指南

http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.htmljavascript

 

XMLHttpRequest是一個瀏覽器接口,使得Javascript能夠進行HTTP(S)通訊。php

最先,微軟在IE 5引進了這個接口。由於它太有用,其餘瀏覽器也模仿部署了,ajax操做所以得以誕生。css

可是,這個接口一直沒有標準化,每家瀏覽器的實現或多或少有點不一樣。HTML 5的概念造成後,W3C開始考慮標準化這個接口。2008年2月,就提出了XMLHttpRequest Level 2 草案。html

這個XMLHttpRequest的新版本,提出了不少有用的新功能,將大大推進互聯網革新。本文就對這個新版本進行詳細介紹。html5

1、老版本的XMLHttpRequest對象java

在介紹新版本以前,咱們先回顧一下老版本的用法。node

首先,新建一個XMLHttpRequest的實例。es6

  var xhr = new XMLHttpRequest();web

而後,向遠程主機發出一個HTTP請求。ajax

  xhr.open('GET', 'example.php');

  xhr.send();

接着,就等待遠程主機作出迴應。這時須要監控XMLHttpRequest對象的狀態變化,指定回調函數。

  xhr.onreadystatechange = function(){

    if ( xhr.readyState == 4 && xhr.status == 200 ) {

      alert( xhr.responseText );

    } else {

      alert( xhr.statusText );

    }

  };

上面的代碼包含了老版本XMLHttpRequest對象的主要屬性:

  * xhr.readyState:XMLHttpRequest對象的狀態,等於4表示數據已經接收完畢。

  * xhr.status:服務器返回的狀態碼,等於200表示一切正常。

  * xhr.responseText:服務器返回的文本數據

  * xhr.responseXML:服務器返回的XML格式的數據

  * xhr.statusText:服務器返回的狀態文本。

2、老版本的缺點

老版本的XMLHttpRequest對象有如下幾個缺點:

  * 只支持文本數據的傳送,沒法用來讀取和上傳二進制文件。

  * 傳送和接收數據時,沒有進度信息,只能提示有沒有完成。

  * 受到"同域限制"(Same Origin Policy),只能向同一域名的服務器請求數據。

3、新版本的功能

新版本的XMLHttpRequest對象,針對老版本的缺點,作出了大幅改進。

  * 能夠設置HTTP請求的時限。

  * 可使用FormData對象管理表單數據。

  * 能夠上傳文件。

  * 能夠請求不一樣域名下的數據(跨域請求)。

  * 能夠獲取服務器端的二進制數據。

  * 能夠得到數據傳輸的進度信息。

下面,我就一一介紹這些新功能。

4、HTTP請求的時限

有時,ajax操做很耗時,並且沒法預知要花多少時間。若是網速很慢,用戶可能要等好久。

新版本的XMLHttpRequest對象,增長了timeout屬性,能夠設置HTTP請求的時限。

  xhr.timeout = 3000;

上面的語句,將最長等待時間設爲3000毫秒。過了這個時限,就自動中止HTTP請求。與之配套的還有一個timeout事件,用來指定回調函數。

  xhr.ontimeout = function(event){

    alert('請求超時!');

  }

目前,Opera、Firefox和IE 10支持該屬性,IE 8和IE 9的這個屬性屬於XDomainRequest對象,而Chrome和Safari還不支持。

5、FormData對象

ajax操做每每用來傳遞表單數據。爲了方便表單處理,HTML 5新增了一個FormData對象,能夠模擬表單。

首先,新建一個FormData對象。

  var formData = new FormData();

而後,爲它添加表單項。

  formData.append('username', '張三');

  formData.append('id', 123456);

最後,直接傳送這個FormData對象。這與提交網頁表單的效果,徹底同樣。

  xhr.send(formData);

FormData對象也能夠用來獲取網頁表單的值。

  var form = document.getElementById('myform');

  var formData = new FormData(form);

  formData.append('secret', '123456'); // 添加一個表單項

  xhr.open('POST', form.action);

  xhr.send(formData);

6、上傳文件

新版XMLHttpRequest對象,不只能夠發送文本信息,還能夠上傳文件。

假定files是一個"選擇文件"的表單元素(input[type="file"]),咱們將它裝入FormData對象。

  var formData = new FormData();

  for (var i = 0; i < files.length;i++) {

    formData.append('files[]', files[i]);

  }

而後,發送這個FormData對象。

  xhr.send(formData);

7、跨域資源共享(CORS)

新版本的XMLHttpRequest對象,能夠向不一樣域名的服務器發出HTTP請求。這叫作"跨域資源共享"(Cross-origin resource sharing,簡稱CORS)。

使用"跨域資源共享"的前提,是瀏覽器必須支持這個功能,並且服務器端必須贊成這種"跨域"。若是可以知足上面的條件,則代碼的寫法與不跨域的請求徹底同樣。

  xhr.open('GET', 'http://other.server/and/path/to/script');

目前,除了IE 8和IE 9,主流瀏覽器都支持CORS,IE 10也將支持這個功能。服務器端的設置,請參考《Server-Side Access Control》

8、接收二進制數據(方法A:改寫MIMEType)

老版本的XMLHttpRequest對象,只能從服務器取回文本數據(不然它的名字就不用XML起首了),新版則能夠取回二進制數據。

這裏又分紅兩種作法。較老的作法是改寫數據的MIMEType,將服務器返回的二進制數據假裝成文本數據,而且告訴瀏覽器這是用戶自定義的字符集。

  xhr.overrideMimeType("text/plain; charset=x-user-defined");

而後,用responseText屬性接收服務器返回的二進制數據。

  var binStr = xhr.responseText;

因爲這時,瀏覽器把它當作文本數據,因此還必須再一個個字節地還原成二進制數據。

  for (var i = 0, len = binStr.length; i < len; ++i) {

    var c = binStr.charCodeAt(i);

    var byte = c & 0xff;

  }

最後一行的位運算"c & 0xff",表示在每一個字符的兩個字節之中,只保留後一個字節,將前一個字節扔掉。緣由是瀏覽器解讀字符的時候,會把字符自動解讀成Unicode的0xF700-0xF7ff區段。

8、接收二進制數據(方法B:responseType屬性)

從服務器取回二進制數據,較新的方法是使用新增的responseType屬性。若是服務器返回文本數據,這個屬性的值是"TEXT",這是默認值。較新的瀏覽器還支持其餘值,也就是說,能夠接收其餘格式的數據。

你能夠把responseType設爲blob,表示服務器傳回的是二進制對象。

  var xhr = new XMLHttpRequest();

  xhr.open('GET', '/path/to/image.png');

  xhr.responseType = 'blob';

接收數據的時候,用瀏覽器自帶的Blob對象便可。

  var blob = new Blob([xhr.response], {type: 'image/png'});

注意,是讀取xhr.response,而不是xhr.responseText。

你還能夠將responseType設爲arraybuffer,把二進制數據裝在一個數組裏。

  var xhr = new XMLHttpRequest();

  xhr.open('GET', '/path/to/image.png');

  xhr.responseType = "arraybuffer";

接收數據的時候,須要遍歷這個數組。

  var arrayBuffer = xhr.response;

  if (arrayBuffer) {

    var byteArray = new Uint8Array(arrayBuffer);

    for (var i = 0; i < byteArray.byteLength; i++) {

      // do something

    }
  }

更詳細的討論,請看Sending and Receiving Binary Data

9、進度信息

新版本的XMLHttpRequest對象,傳送數據的時候,有一個progress事件,用來返回進度信息。

它分紅上傳和下載兩種狀況。下載的progress事件屬於XMLHttpRequest對象,上傳的progress事件屬於XMLHttpRequest.upload對象。

咱們先定義progress事件的回調函數。

  xhr.onprogress = updateProgress;

  xhr.upload.onprogress = updateProgress;

而後,在回調函數裏面,使用這個事件的一些屬性。

  function updateProgress(event) {

    if (event.lengthComputable) {

      var percentComplete = event.loaded / event.total;

    }

  }

上面的代碼中,event.total是須要傳輸的總字節,event.loaded是已經傳輸的字節。若是event.lengthComputable不爲真,則event.total等於0。

與progress事件相關的,還有其餘五個事件,能夠分別指定回調函數:

  * load事件:傳輸成功完成。

  * abort事件:傳輸被用戶取消。

  * error事件:傳輸中出現錯誤。

  * loadstart事件:傳輸開始。

  * loadEnd事件:傳輸結束,可是不知道成功仍是失敗。

10、閱讀材料

  1. Introduction to XMLHttpRequest Level 2: 新功能的綜合介紹。

  2. New Tricks in XMLHttpRequest 2:一些用法的介紹。

  3. Using XMLHttpRequest:一些高級用法,主要針對Firefox瀏覽器。

  4. HTTP Access Control:CORS綜述。

  5. DOM access control using cross-origin resource sharing:CORS的9種HTTP頭信息

  6. Server-Side Access Control:服務器端CORS設置。

  7. Enable CORS:服務端CORS設置。

(完)

一燈學堂

QCon 北京2018

留言(31條)

學習了,html5的新特性真讓人着迷

其實IE8,9也支持跨域資源共享,不過用的是 XDomainRequest 對象,並且作法和w3c標準不一樣,不是設置在header裏而是要放一個文件在服務器上(相似Flash跨域的作法)。

引用RedNax的發言:

其實IE8,9也支持跨域資源共享,不過用的是 XDomainRequest 對象,並且作法和w3c標準不一樣,不是設置在header裏而是要放一個文件在服務器上(相似Flash跨域的作法)。

IE8,9也支持跨域沒有錯,可用接口單一,可是並不須要放一個文件在服務器上,也不相似flash。

我作過測試: http://cssor.com/cross-origin-resource-sharing.html

剛看了《黑客與畫家》,確實是一本不可多得的好書!

引用ToFishes的發言:

 

IE8,9也支持跨域沒有錯,可用接口單一,可是並不須要放一個文件在服務器上,也不相似flash。

我作過測試: http://cssor.com/cross-origin-resource-sharing.html

哦,看了一下文檔確實如此。看來是我和什麼混起來了……

Websocket比xhr更強大,能夠實現實時的push,如今只能靠flash,隨着支持瀏覽器的普及,將會看到更多用websocket的網站,阮先生也能夠介紹一下

最近的內容涉及到好多技術的細節問題,沒有太大的興趣,固然了,寫什麼是阮兄的自由。

js跨域還在研究當中,學習啦

不錯,假如能夠順便知道支持的瀏覽器的大體版本號就行了。

據我瞭解文中提到的「跨域資源共享(CORS)」目前仍是w3c的草案,尚未正式發佈。

博主,關於CORS,最好再加上Preflighted request的內容

若是要使用除GET和POST之外的方法,或者想用content-type不是application/x-www-form-urlencoded, multipart/form-data, or text/plain 的POST,須要在request的header中添加額外的custom header

Preflighted requests

Unlike simple requests (discussed above), "preflighted" requests first send an HTTP OPTIONS request header to the resource on the other domain, in order to determine whether the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data. In particular, a request is preflighted if:

It uses methods other than GET or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.
It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)

https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control#Preflighted_requests

http://arunranga.com/examples/access-control/preflightInvocation.html

這裏有很多的Demo能夠參考:http://tiffanybbrown.com/presentation/2011/xhr2/

樓主您好,請問您本人是否試過XMLHttpRequest上傳文件?若是有,請指教,最好是您完整的JS代碼,我無惡意,經過實踐以後只是對XMLHttpRequest上傳文件表示質疑。畢竟他歸根結底仍是一個AJAX,歡迎QQ或是郵件交流和指點,謝謝O(∩_∩)O.qq:303834036

正在學習跨文檔通訊 謝謝樓主的資料0w0

引用jalorchen的發言:

樓主您好,請問您本人是否試過XMLHttpRequest上傳文件?若是有,請指教,最好是您完整的JS代碼,我無惡意,經過實踐以後只是對XMLHttpRequest上傳文件表示質疑。畢竟他歸根結底仍是一個AJAX,歡迎QQ或是郵件交流和指點,謝謝O(∩_∩)O.qq:303834036

說到底,就是http協議,固然能夠上傳文件。 利用formdata上傳過文件,將頭信息更改成application/multiple,請求信息中還須要加入boundary信息,後臺就能夠進行解析了。後臺用第三方組件解析要方便一些,我用的就是java的commonupload組件。

引用dirty的發言:

Websocket比xhr更強大,能夠實現實時的push,如今只能靠flash,隨着支持瀏覽器的普及,將會看到更多用websocket的網站,阮先生也能夠介紹一下

是呀,阮老師能夠介紹一下web socket.

引用dirty的發言:

Websocket比xhr更強大,能夠實現實時的push,如今只能靠flash,隨着支持瀏覽器的普及,將會看到更多用websocket的網站,阮先生也能夠介紹一下

Flash的錯在於它只是一個插件,小三永遠轉不了正。我下載新版firefox後,一啓動就提示Flash崩潰,用戶遇到這種狀況怎麼跟他解析?!

chrome 38.0已經支持xhr的timeout了

引用Thyiad的發言:


說到底,就是http協議,固然能夠上傳文件。
利用formdata上傳過文件,將頭信息更改成application/multiple,請求信息中還須要加入boundary信息,後臺就能夠進行解析了。後臺用第三方組件解析要方便一些,我用的就是java的commonupload組件。

 


謝謝兄弟指點,我會按照您的指點嘗試一下。

請問發送數據時,能否發送blob數據?文章只說了接收數據能夠接收blob

謝謝阮兄,恰好要用到這個知識點!

使用阮老師提供的formData對象上傳的文件(異步方式),後臺(java)不知道該如何解析啊,求各位大神指點,謝謝!

謝謝樓主大大!感受收穫良多!

使用起來和Android的AsyncTask挺像啊

引用山姆大叔的發言:

使用阮老師提供的formData對象上傳的文件(異步方式),後臺(java)不知道該如何解析啊,求各位大神指點,謝謝!

java沒有像php的$_FILES類嗎,同樣的處理方式啊,和直接form提交沒啥區別吧

引用請問阮老師的發言:

請問發送數據時,能否發送blob數據?文章只說了接收數據能夠接收blob

void send();
void send(ArrayBuffer data);
void send(Blob data);
void send(Document data);
void send(DOMString? data);
void send(FormData data);

問下,關於上傳進度 【event.loaded是已經傳輸的字節】 event裏面的數據是從本地獲取的仍是服務的返回的?

onreadystatechange應該在open和send以前設定吧,不然一些狀態會捕捉不到

引用阮老師你好的發言:

onreadystatechange應該在open和send以前設定吧,不然一些狀態會捕捉不到

如今使用onload

文件進度須要服務端支持把,能不能寫幾行服務端的僞代碼,咱們也好理解

引用小小蘇的發言:

文件進度須要服務端支持把,能不能寫幾行服務端的僞代碼,咱們也好理解

這個應該不須要服務端支持吧

我要發表見解

相關文章
相關標籤/搜索