Home >> Blog >> XMLHttpRequest 簡介
XMLHttpRequest 簡介
XMLHttpRequest是一個內置的瀏覽器對象,允許在 JavaScript 中發出 HTTP 請求。
儘管名稱中有“XML”一詞,但它可以對任何數據進行操作,而不僅僅是 XML 格式。我們可以上傳/下載文件、跟踪進度等等。
現在,還有另一種更現代的方法fetch,它有點過時了XMLHttpRequest。
在現代 Web 開發XMLHttpRequest中使用的原因有以下三個:
- 歷史原因:我們需要用XMLHttpRequest.
- 我們需要支持舊的瀏覽器,並且不想要 polyfills(例如保持腳本很小)。
- 我們需要一些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如果需要更多字段,然後:
- xhr.open('POST', ...)– 使用POST方法。
- 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像上監聽相同的事件。