Home >> Blog >> XMLHttpRequest 簡介

XMLHttpRequest 簡介

XMLHttpRequest是一個內置的瀏覽器對象,允許在 JavaScript 中發出 HTTP 請求。

儘管名稱中有“XML”一詞,但它可以對任何數據進行操作,而不僅僅是 XML 格式。我們可以上傳/下載文件、跟踪進度等等。

現在,還有另一種更現代的方法fetch,它有點過時了XMLHttpRequest。

在現代 Web 開發XMLHttpRequest中使用的原因有以下三個:

  1. 歷史原因:我們需要用XMLHttpRequest.
  2. 我們需要支持舊的瀏覽器,並且不想要 polyfills(例如保持腳本很小)。
  3. 我們需要一些fetch還不能做的事情,例如跟踪上傳進度。

這聽起來很熟悉嗎?如果是,那好吧,繼續XMLHttpRequest。否則,請前往Fetch。

基礎知識

XMLHttpRequest 有兩種操作模式:同步和異步。

讓我們先看看異步,因為它在大多數情況下都使用。

要完成請求,我們需要 3 個步驟:

1.創建XMLHttpRequest:

let xhr = new XMLHttpRequest();

構造函數沒有參數。

2.初始化它,通常是在new XMLHttpRequest:

xhr.open(method, URL, [async, user, password])

該方法指定請求的主要參數:

  • method– HTTP 方法。通常"GET"或"POST"。
  • URL– 請求的 URL,字符串,可以是URL對象。
  • async– 如果顯式設置為false,則請求是同步的,我們稍後會介紹。
  • user, password– 基本 HTTP 身份驗證的登錄名和密碼(如果需要)。

請注意,open與其名稱相反,該調用不會打開連接。它僅配置請求,但網絡活動僅以調用send.

3.發送出去。

xhr.send([body])

此方法打開連接並將請求發送到伺服器。可選body參數包含請求正文。

一些請求方法,例如GET沒有正文。其中一些喜歡POST用於body將數據發送到伺服器。我們稍後會看到這樣的例子。

4.監聽xhr事件以獲取響應。

這三個事件是使用最廣泛的:

  • load– 當請求完成時(即使 HTTP 狀態是 400 或 500),並且響應已完全下載。
  • error– 無法發出請求時,例如網絡關閉或 URL 無效。
  • progress– 在下載響應時定期觸發,報告下載了多少。

xhr.onload = function() {
alert(`Loaded: ${xhr.status} ${xhr.response}`);
};

xhr.onerror = function() { // only triggers if the request couldn't be made at all
alert(`Network Error`);
};

xhr.onprogress = function(event) { // triggers periodically
// event.loaded - how many bytes downloaded
// event.lengthComputable = true if the server sent Content-Length header
// event.total - total number of bytes (if lengthComputable)
alert(`Received ${event.loaded} of ${event.total}`);
};

這是一個完整的例子。下面的程式碼/article/xmlhttprequest/example/load從伺服器加載 URL 並打印進度:

// 1. Create a new XMLHttpRequest object
let xhr = new XMLHttpRequest();

// 2. Configure it: GET-request for the URL /article/.../load
xhr.open('GET', '/article/xmlhttprequest/example/load');

// 3. Send the request over the network
xhr.send();

// 4. This will be called after the response is received
xhr.onload = function() {
if (xhr.status != 200) { // analyze HTTP status of the response
alert(`Error ${xhr.status}: ${xhr.statusText}`); // e.g. 404: Not Found
} else { // show the result
alert(`Done, got ${xhr.response.length} bytes`); // response is the server response
}
};

xhr.onprogress = function(event) {
if (event.lengthComputable) {
alert(`Received ${event.loaded} of ${event.total} bytes`);
} else {
alert(`Received ${event.loaded} bytes`); // no Content-Length
}

};

xhr.onerror = function() {
alert("Request failed");
};

伺服器響應後,我們可以在以下xhr屬性中接收結果:

status

HTTP 狀態碼(一個數字):200、404等403,可能是0在非 HTTP 失敗的情況下。

statusText

HTTP 狀態消息(字符串):通常OK為 for 200, Not Foundfor 404, Forbiddenfor403等。

response(舊腳本可能使用responseText)

伺服器響應正文。

我們還可以使用相應的屬性指定超時:

xhr.timeout = 10000; // timeout in ms, 10 seconds

如果請求在給定時間內沒有成功,它將被取消並timeout觸發事件。

let url = new URL('https://google.com/search');
url.searchParams.set('q', 'test me!');

// the parameter 'q' is encoded
xhr.open('GET', url); // https://google.com/search?q=test+me%21

響應類型

我們可以使用xhr.responseType屬性來設置響應格式:

  • ""(默認)– 以字符串形式獲取,
  • "text"– 獲取字符串,
  • "arraybuffer"– 獲取為ArrayBuffer(對於二進制數據,請參閱ArrayBuffer,二進制數組一章),
  • "blob"– 獲取為Blob(對於二進制數據,請參閱Blob章節),
  • "document"– 獲取為 XML 文檔(可以使用 XPath 和其他 XML 方法)或 HTML 文檔(基於接收數據的 MIME 類型),
  • "json"– 以 JSON 格式獲取(自動解析)。

例如,讓我們以 JSON 格式獲取響應:

let xhr = new XMLHttpRequest();

xhr.open('GET', '/article/xmlhttprequest/example/json');

xhr.responseType = 'json';

xhr.send();

// the response is {"message": "Hello, world!"}
xhr.onload = function() {
let responseObj = xhr.response;
alert(responseObj.message); // Hello, world!
};

請注意:

在舊腳本中,您還可以找到xhr.responseText甚至xhr.responseXML屬性。

它們出於歷史原因而存在,用於獲取字符串或 XML 文檔。現在,我們應該設置格式xhr.responseType並獲取xhr.response如上所示。

就緒狀態

XMLHttpRequest隨著它的進展,狀態之間的變化。當前狀態可作為 xhr.readyState.

所有狀態,如規範中所示:

UNSENT = 0; // initial state
OPENED = 1; // open called
HEADERS_RECEIVED = 2; // response headers received
LOADING = 3; // response is loading (a data packet is received)
DONE = 4; // request complete


一個XMLHttpRequest物體按照0→ 1→ 2→ 3→ ... → 3→的順序移動它們4。3每次通過網絡接收到數據包時,狀態都會重複。

我們可以使用readystatechange事件跟踪它們:

xhr.onreadystatechange = function() {
if (xhr.readyState == 3) {
// loading
}
if (xhr.readyState == 4) {
// request finished
}
};

您可以在非常舊的程式碼中找到readystatechange偵聽器,這是由於歷史原因而存在的,因為曾經有一段時間沒有load事件和其他事件。如今,load/error/progress處理程序已棄用它。

中止請求

我們可以隨時終止請求。調用這樣xhr.abort()做:

xhr.abort(); // terminate the request

觸發abort事件,並xhr.status變為0

同步請求

如果在open方法中第三個參數async設置為false,則請求是同步進行的。

換句話說,JavaScript 執行在send()收到響應時暫停並恢復。有點喜歡alert或prompt命令。

這是重寫的示例,的第三個參數open是false:

let xhr = new XMLHttpRequest();

xhr.open('GET', '/article/xmlhttprequest/hello.txt', false);

try {
xhr.send();
if (xhr.status != 200) {
alert(`Error ${xhr.status}: ${xhr.statusText}`);
} else {
alert(xhr.response);
}
} catch(err) { // instead of onerror
alert("Request failed");
}

它可能看起來不錯,但很少使用同步調用,因為它們會阻塞頁面內的 JavaScript,直到加載完成。在某些瀏覽器中,滾動變得不可能。如果同步調用耗時過長,瀏覽器可能會建議關閉“掛起”的網頁。

的許多高級功能XMLHttpRequest,例如從另一個域請求或指定超時,對於同步請求是不可用的。此外,如您所見,沒有進度指示。

正因為如此,同步請求的使用非常謹慎,幾乎從不使用。我們不會再談論它們了。

HTTP標頭

XMLHttpRequest允許發送自定義標頭和從響應中讀取標頭。

HTTP標頭有3種方法:

setRequestHeader(name, value)

name使用給定的和設置請求標頭value。

例如:

xhr.setRequestHeader('Content-Type', 'application/json');

標頭限制

幾個標頭由瀏覽器專門管理,例如Referer和Host。完整列表在規範中。

XMLHttpRequest為了用戶的安全和請求的正確性,不允許更改它們。

無法刪除標題

的另一個特點XMLHttpRequest是無法撤消setRequestHeader。

一旦設置了標題,它就被設置了。額外的調用將信息添加到標題中,不要覆蓋它。

例如:

xhr.setRequestHeader('X-Auth', '123');
xhr.setRequestHeader('X-Auth', '456');

// the header will be:
// X-Auth: 123, 456

getResponseHeader(name)

獲取具有給定的響應標頭name(除Set-Cookie和Set-Cookie2)。

例如:

xhr.getResponseHeader('Content-Type')

getAllResponseHeaders()

返回所有響應標頭,除了Set-Cookie和Set-Cookie2。

標頭作為單行返回,例如:

Cache-Control: max-age=31536000
Content-Length: 4260
Content-Type: image/png
Date: Sat, 08 Sep 2012 16:53:16 GMT

標頭之間的換行符總是"\r\n"(不依賴於操作系統),因此我們可以輕鬆地將其拆分為單獨的標頭。名稱和值之間的分隔符始終是一個冒號,後跟一個空格": "。這在規範中是固定的。

所以,如果我們想得到一個具有名稱/值對的對象,我們需要投入一點 JS。

像這樣(假設如果兩個標題具有相同的名稱,那麼後一個會覆蓋前一個):

let headers = xhr
.getAllResponseHeaders()
.split('\r\n')
.reduce((result, current) => {
let [name, value] = current.split(': ');
result[name] = value;
return result;
}, {});

// headers['Content-Type'] = 'image/png'

POST,表單數據

要發出 POST 請求,我們可以使用內置的FormData對象。

語法:

let formData = new FormData([form]); // creates an object, optionally fill from < form>
formData.append(name, value); // appends a field

我們創建它,可以選擇從表單中填寫,append如果需要更多字段,然後:

  1. xhr.open('POST', ...)– 使用POST方法。
  2. xhr.send(formData)將表單提交到伺服器。

例如:

< form name="person">
< input name="name" value="John">
< input name="surname" value="Smith">
< /form>

< script>
// pre-fill FormData from the form
let formData = new FormData(document.forms.person);

// add one more field
formData.append("middle", "Lee");

// send it out
let xhr = new XMLHttpRequest();
xhr.open("POST", "/article/xmlhttprequest/post/user");
xhr.send(formData);

xhr.onload = () => alert(xhr.response);
< /script>

表單使用multipart/form-data編碼發送。

或者,如果我們更喜歡 JSON,則將JSON.stringify其作為字符串發送。

只是不要忘記設置 header Content-Type: application/json,許多伺服器端框架會自動使用它來解碼 JSON:

let xhr = new XMLHttpRequest();

let json = JSON.stringify({
name: "John",
surname: "Smith"
});

xhr.open("POST", '/submit')
xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');

xhr.send(json);

該.send(body)方法非常雜食。它幾乎可以發送任何body,包括Blob和BufferSource對象。

上傳進度

該progress事件僅在下載階段觸發。

那就是:如果我們有POST東西,XMLHttpRequest首先上傳我們的數據(請求正文),然後下載響應。

如果我們要上傳大的東西,那麼我們肯定對跟踪上傳進度更感興趣。但xhr.onprogress在這裡沒有幫助。

還有另一個對象,沒有方法,專門用於跟踪上傳事件:xhr.upload.

它生成事件,類似於xhr,但xhr.upload僅在上傳時觸發它們:

  • loadstart– 上傳開始。
  • progress– 在上傳期間定期觸發。
  • abort– 上傳中止。
  • error– 非 HTTP 錯誤。
  • load– 上傳成功完成。
  • timeout– 上傳超時(如果timeout設置了屬性)。
  • loadend– 上傳完成,成功或錯誤。

處理程序示例:

xhr.upload.onprogress = function(event) {
alert(`Uploaded ${event.loaded} of ${event.total} bytes`);
};

xhr.upload.onload = function() {
alert(`Upload finished successfully.`);
};

xhr.upload.onerror = function() {
alert(`Error during the upload: ${xhr.status}`);
};

這是一個真實的示例:帶有進度指示的文件上傳:

< input type="file" onchange="upload(this.files[0])">

< script>
function upload(file) {
let xhr = new XMLHttpRequest();

// track upload progress
xhr.upload.onprogress = function(event) {
console.log(`Uploaded ${event.loaded} of ${event.total}`);
};

// track completion: both successful or not
xhr.onloadend = function() {
if (xhr.status == 200) {
console.log("success");
} else {
console.log("error " + this.status);
}
};

xhr.open("POST", "/article/xmlhttprequest/post/upload");
xhr.send(file);
}
< /script>

跨域請求

XMLHttpRequest可以使用與fetch相同的 CORS 策略進行跨域請求。

就像 一樣fetch,默認情況下它不會將 cookie 和 HTTP 授權發送到另一個源。要啟用它們,請設置xhr.withCredentials為true:

let xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.open('POST', 'http://anywhere.com/request');
...

有關跨域標頭的詳細信息,請參閱獲取:跨域請求一章。

概括

GET 請求的典型程式碼XMLHttpRequest:

let xhr = new XMLHttpRequest();

xhr.open('GET', '/my/url');

xhr.send();

xhr.onload = function() {
if (xhr.status != 200) { // HTTP error?
// handle error
alert( 'Error: ' + xhr.status);
return;
}

// get the response from xhr.response
};

xhr.onprogress = function(event) {
// report progress
alert(`Loaded ${event.loaded} of ${event.total}`);
};

xhr.onerror = function() {
// handle non-HTTP error (e.g. network down)
};

實際上還有更多事件,現代規範列出了它們(按生命週期順序):

  • loadstart– 請求已開始。
  • progress– 一個響應的數據包已經到達,此時整個響應體在response。
  • abort– 請求被呼叫取消xhr.abort()。
  • error– 發生連接錯誤,例如錯誤的域名。不會發生像 404 這樣的 HTTP 錯誤。
  • load– 請求已成功完成。
  • timeout– 請求因超時而被取消(僅在設置時才會發生)。
  • loadendload– 在、或error之後觸發。timeoutabort
  • error、abort和事件是互斥的timeout。load只有其中一種可能發生。

最常用的事件是加載完成(load)、加載失敗(error),或者我們可以使用單個loadend處理程序並檢查請求對象的屬性xhr以查看發生了什麼。

我們已經看到了另一個事件:readystatechange. 從歷史上看,它出現在很久以前,在規範確定之前。現在不需要使用它,我們可以用更新的事件替換它,但它經常可以在舊腳本中找到。

如果我們需要專門跟踪上傳,那麼我們應該在對xhr.upload像上監聽相同的事件。

the

xhr

js

certificate

javascript 初心者筆記

referenced

whatwg org

mdn

mdn

mdn

xml

xml

xml

xml

xml

request

request

request

request

request

request

request

request