前端請求大比拼:Fetch、Axios、Ajax、XHR
當(dāng)涉及前端網(wǎng)絡(luò)請求時(shí),有許多工具/技術(shù)可供選擇,包括 Fetch、Axios、Ajax 和 XHR 等。這些技術(shù)在發(fā)送和處理HTTP請求方面提供了不同的功能和方法。本文將深入探討這些技術(shù)的特點(diǎn)、優(yōu)勢和用法,幫你更好地理解并選擇最適合項(xiàng)目需求的技術(shù)。
1、基本概念
Fetch、Axios、Ajax 和XHR都是前端用于發(fā)送HTTP請求的工具或技術(shù):
- Fetch:一種現(xiàn)代化的網(wǎng)絡(luò)請求方法,通過使用 Promise 處理異步操作,簡潔而直觀地發(fā)送HTTP請求、處理響應(yīng),并支持各種功能和API,如設(shè)置請求頭、傳遞參數(shù)、處理流數(shù)據(jù)、上傳下載文件等。
- Axios:一個(gè)基于Promise的現(xiàn)代化HTTP客戶端,是目前最流行的 HTTP 客戶端,可以在瀏覽器和Node.js環(huán)境中發(fā)送HTTP請求,并具有攔截請求和響應(yīng)、支持并發(fā)請求、提供豐富的API等功能。
- Ajax:通過在瀏覽器和服務(wù)器之間進(jìn)行異步通信,實(shí)現(xiàn)部分頁面更新和動(dòng)態(tài)交互,提升用戶體驗(yàn);可以在不重新加載整個(gè)頁面的情況下,通過JavaScript發(fā)送HTTP請求到服務(wù)器,并處理服務(wù)器返回的數(shù)據(jù);減少帶寬消耗,提高頁面加載速度;提高用戶交互性,實(shí)現(xiàn)更多的動(dòng)態(tài)效果和實(shí)時(shí)更新。
- XHR:一種在瀏覽器中用于與服務(wù)器進(jìn)行異步通信的API,通過發(fā)送HTTP請求并處理服務(wù)器返回的數(shù)據(jù),實(shí)現(xiàn)異步獲取各種格式的數(shù)據(jù)(如XML、JSON、HTML等),以實(shí)現(xiàn)頁面的無刷新更新和動(dòng)態(tài)交互。
下面就來看看這些技術(shù)都是怎么用的,以及都有什么特點(diǎn)!
2、XHR
XMLHttpRequest 是一個(gè)內(nèi)置的 JavaScript 對象,XMLHttpRequest(XHR)對象用于與服務(wù)器交互。通過 XMLHttpRequest 可以在不刷新頁面的情況下請求特定 URL,獲取數(shù)據(jù)。這允許網(wǎng)頁在不影響用戶操作的情況下,更新頁面的局部內(nèi)容。
XMLHttpRequest 在 AJAX 編程中被大量使用。盡管名稱包含XML,XMLHttpRequest 也可以用于獲取任何類型的數(shù)據(jù),而不僅僅是 XML。它甚至支持 HTTP 以外的協(xié)議(包括 file:// 和 FTP)。
XMLHttpRequest 存在一些缺點(diǎn):
- 語法復(fù)雜性:使用原始的 XMLHttpRequest 進(jìn)行復(fù)雜的 AJAX 請求需要編寫更多的代碼,并手動(dòng)處理狀態(tài)管理、錯(cuò)誤處理等方面的邏輯。相比之下,Axios 和 Fetch API 提供了更簡單和直觀的語法,使得發(fā)送和處理 HTTP 請求更加方便。
- 功能限制:XHR 提供的功能相對較少,需要手動(dòng)設(shè)置請求頭、處理超時(shí)、取消請求等。而 Axios 和 Fetch API 提供了更豐富的功能,如攔截請求和響應(yīng)、自動(dòng)轉(zhuǎn)換數(shù)據(jù)格式、請求取消等。
- XSRF(跨站請求偽造)保護(hù):在 Axios 中,可以通過設(shè)置 withCredentials 選項(xiàng)來自動(dòng)處理 XSRF 保護(hù)。然而,在 XMLHttpRequest 和 Fetch API 中,需要手動(dòng)設(shè)置請求頭來實(shí)現(xiàn)類似的保護(hù)。
- 錯(cuò)誤處理:XHR 的錯(cuò)誤處理較為繁瑣,需要在回調(diào)函數(shù)中進(jìn)行錯(cuò)誤判斷。而 Axios 和 Fetch API 使用 Promise 和 async/await 語法,能夠更便捷地處理請求和響應(yīng)的錯(cuò)誤。
- 僅限于瀏覽器環(huán)境:XMLHttpRequest 是瀏覽器提供的 API,因此只能在瀏覽器環(huán)境中使用,無法在其他環(huán)境中(如服務(wù)器端)直接使用。
請求步驟
使用 XMLHttpRequest 發(fā)送請求的步驟如下:
- 創(chuàng)建XMLHttpRequest對象:
let xhr = new XMLHttpRequest();
- 設(shè)置請求參數(shù):
xhr.open('GET', 'https://example.com/api/data', true);
- 設(shè)置請求頭(可選):
xhr.setRequestHeader('Content-Type', 'application/json');
- 監(jiān)聽狀態(tài)變化:
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// 請求成功,處理響應(yīng)
console.log(xhr.responseText);
} else {
// 請求失敗
console.error('請求失敗');
}
}
};
- 發(fā)送請求:
xhr.send();
完整代碼如下:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// 請求成功,處理響應(yīng)
console.log(xhr.responseText);
} else {
// 請求失敗
console.error('請求失敗');
}
}
};
xhr.send();
這里創(chuàng)建了一個(gè)XMLHttpRequest對象,并使用open()方法設(shè)置了一個(gè)GET請求類型和URL。然后,通過監(jiān)聽onreadystatechange事件來判斷請求的狀態(tài)并處理響應(yīng)。當(dāng)readyState為4時(shí),表示請求已完成,此時(shí)可以通過status屬性判斷請求是否成功(200表示成功)。如果成功,可以通過responseText屬性獲取服務(wù)器返回的數(shù)據(jù)進(jìn)行處理。如果失敗,將到控制臺輸出錯(cuò)誤信息。
open
XMLHttpRequest 的 open() 方法用于初始化一個(gè)請求。open() 方法接受三個(gè)必填參數(shù)和一個(gè)可選參數(shù),它們是:
- method: 表示請求的 HTTP 方法,例如 GET、POST、PUT 等。
xhr.open("GET", "https://example.com/api/data", true);
- url: 表示請求的 URL 地址。
xhr.open("GET", "https://example.com/api/data", true);
- async: 表示請求是否異步執(zhí)行,即是否使用異步模式。默認(rèn)為 true,表示異步執(zhí)行;false 表示同步執(zhí)行。
javascriptCopy Codexhr.open("GET", "https://example.com/api/data", true);
- username (可選): 表示用于進(jìn)行 HTTP 認(rèn)證的用戶名。
xhr.open("GET", "https://example.com/api/data", true, "username");
- password (可選): 表示用于進(jìn)行 HTTP 認(rèn)證的密碼。
xhr.open("GET", "https://example.com/api/data", true, "username", "password");
綜合起來,open() 方法的完整語法如下:
xhr.open(method, url, async, username, password);
請求頭和響應(yīng)頭
可以使用 setRequestHeader() 方法設(shè)置 XMLHttpRequest 的請求頭。這個(gè)方法接受兩個(gè)參數(shù):頭字段的名稱和值。
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Authorization", "Bearer token123");
這里使用 setRequestHeader() 方法設(shè)置了兩個(gè)請求頭:Content-Type 和 Authorization。第一個(gè)參數(shù)是頭字段的名稱,第二個(gè)參數(shù)是頭字段的值。
可以使用 getResponseHeader() 方法或者 getAllResponseHeaders() 方法來獲取 XMLHttpRequest 的響應(yīng)頭。
- getResponseHeader():通過指定頭字段的名稱,可以獲取指定的響應(yīng)頭字段的值。
const contentType = xhr.getResponseHeader("Content-Type");
這里使用 getResponseHeader() 方法獲取了名為 Content-Type的響應(yīng)頭字段的值。
- getAllResponseHeaders()** **:該方法返回一個(gè)包含所有響應(yīng)頭信息的字符串。
const headers = xhr.getAllResponseHeaders();
這里使用 getAllResponseHeaders() 方法獲取了所有響應(yīng)頭信息,并將其存儲在名為 headers 的變量中。
這里返回的 headers 是一個(gè)包含所有響應(yīng)頭信息的字符串。該字符串中每一行表示一個(gè)響應(yīng)頭字段,具有以下形式:
HeaderName: HeaderValue
例如,如果響應(yīng)頭中包含 Content-Type 和 Authorization 字段,那么返回的 headers 字符串可能如下所示:
Content-Type: application/json
Authorization: Bearer token123
可以使用適當(dāng)?shù)姆椒ǎㄈ缱址馕觯⑦@個(gè)字符串進(jìn)行進(jìn)一步處理,以獲取特定的響應(yīng)頭字段的名稱和值。
注意,要在調(diào)用 open() 方法之后、發(fā)送請求之前使用 setRequestHeader() 方法來設(shè)置請求頭,以確保設(shè)置能夠生效。同樣,要在接收到響應(yīng)之后才能使用 getResponseHeader() 或 getAllResponseHeaders() 來獲取響應(yīng)頭信息。
readyState
上面示例中的 readyState 是 XMLHttpRequest 對象的一個(gè)屬性,用于表示請求的狀態(tài)。該屬性有以下五種可能的取值:
- 0 (未初始化): XMLHttpRequest 對象已創(chuàng)建,但尚未調(diào)用 open 方法。
- 1 (載入中): open 方法已調(diào)用,但尚未調(diào)用 send 方法。
- 2 (載入完成): send 方法已調(diào)用,并且響應(yīng)頭和響應(yīng)狀態(tài)已經(jīng)可用。
- 3 (交互中): 正在接收響應(yīng)數(shù)據(jù),此時(shí)部分響應(yīng)內(nèi)容可能已經(jīng)可以訪問了。
- 4 (完成): 響應(yīng)數(shù)據(jù)接收完成,整個(gè)請求過程已經(jīng)完全結(jié)束。
通常情況下,我們主要關(guān)注 readyState 為 4 的狀態(tài),即請求完成狀態(tài)。在這個(gè)狀態(tài)下,我們可以通過檢查 status 屬性來獲取請求的結(jié)果(比如響應(yīng)狀態(tài)碼),并通過 responseText 或 responseXML 屬性來獲取服務(wù)器返回的數(shù)據(jù)。
注意,readyState 屬性是只讀的,我們不能直接修改它的值。它會在請求過程中自動(dòng)更新,我們可以通過監(jiān)聽 readystatechange 事件來進(jìn)行相應(yīng)的處理。
status
status 是 XMLHttpRequest 對象的一個(gè)屬性,用于表示 HTTP 狀態(tài)碼。 HTTP 狀態(tài)碼是服務(wù)器對請求處理的結(jié)果進(jìn)行響應(yīng)的標(biāo)準(zhǔn)化數(shù)字代碼。常見的一些 HTTP 狀態(tài)碼包括:
- 200 OK:表示請求成功并返回所請求的數(shù)據(jù)。
- 201 Created:表示請求成功并在服務(wù)器上創(chuàng)建了新資源。
- 204 No Content:表示請求成功,但響應(yīng)中無返回的內(nèi)容。
- 400 Bad Request:表示請求有語法錯(cuò)誤或參數(shù)錯(cuò)誤,服務(wù)器無法理解。
- 401 Unauthorized:表示請求未經(jīng)授權(quán),需要用戶進(jìn)行身份驗(yàn)證。
- 403 Forbidden:表示服務(wù)器拒絕請求,通常是因?yàn)檎埱蟮馁Y源沒有訪問權(quán)限。
- 404 Not Found:表示請求的資源不存在。
- 500 Internal Server Error:表示服務(wù)器內(nèi)部發(fā)生錯(cuò)誤,無法完成請求。
在使用 XMLHttpRequest 發(fā)送請求后,可以通過檢查 status 屬性來獲取服務(wù)器對請求的響應(yīng)狀態(tài)碼,并根據(jù)不同的狀態(tài)碼進(jìn)行相應(yīng)的處理。
事件屬性
XMLHttpRequest (XHR) 對象具有以下常用的事件屬性:
- onreadystatechange: 當(dāng) readyState 屬性發(fā)生變化時(shí)觸發(fā)該事件。可以使用 xhr.onreadystatechange 屬性來指定處理狀態(tài)變化的回調(diào)函數(shù)。在每次狀態(tài)變化時(shí)都會觸發(fā)該事件,可以通過檢查 xhr.readyState 屬性來確定當(dāng)前的狀態(tài)。
xhr.onreadystatechange = () => {
if(xhr.readyState === 4) {
// 請求已完成
if(xhr.status === 200) {
// 請求成功
} else {
// 請求失敗
}
} else {
// 請求進(jìn)行中
}
};
- onload: 當(dāng)請求成功完成并且響應(yīng)數(shù)據(jù)完全加載時(shí)觸發(fā)該事件??梢允褂?nbsp;xhr.onload 屬性來指定處理成功加載的回調(diào)函數(shù)。通常在這個(gè)事件中獲取和處理響應(yīng)數(shù)據(jù)。
xhr.onload = () => {
// 獲取和處理響應(yīng)數(shù)據(jù)
const responseData = JSON.parse(xhr.responseText);
// 其他操作...
};
- onerror: 當(dāng)請求發(fā)生錯(cuò)誤時(shí)觸發(fā)該事件。可以使用 xhr.onerror 屬性來指定處理錯(cuò)誤的回調(diào)函數(shù)。常見的錯(cuò)誤包括網(wǎng)絡(luò)錯(cuò)誤、無法完成請求等。
xhr.onerror = () => {
// 處理錯(cuò)誤邏輯
};
- onprogress: 在數(shù)據(jù)傳輸過程中持續(xù)觸發(fā),用于追蹤請求的進(jìn)度??梢允褂?nbsp;xhr.onprogress 屬性來指定處理進(jìn)度的回調(diào)函數(shù)。
xhr.onprogress = (event) => {
// 處理進(jìn)度邏輯
};
- ontimeout: 當(dāng)請求超時(shí)時(shí)觸發(fā)該事件??梢允褂?nbsp;xhr.ontimeout 屬性來指定處理超時(shí)的回調(diào)函數(shù)。
xhr.ontimeout = () => {
// 處理超時(shí)邏輯
};
responseType
responseType 是 XMLHttpRequest 對象的屬性,用于指定響應(yīng)的數(shù)據(jù)類型。它決定了如何解析從服務(wù)器返回的響應(yīng)數(shù)據(jù)。 常見的 responseType 值包括:
- "" (默認(rèn)值): 表示響應(yīng)的數(shù)據(jù)類型是字符串。
xhr.responseType = "";
- "text": 表示響應(yīng)的數(shù)據(jù)類型是字符串。
xhr.responseType = "text";
- "json": 表示響應(yīng)的數(shù)據(jù)類型是 JSON 對象,會自動(dòng)將響應(yīng)數(shù)據(jù)解析為 JavaScript 對象。
xhr.responseType = "json";
- "document": 表示響應(yīng)的數(shù)據(jù)類型是 XML 文檔對象,會自動(dòng)將響應(yīng)數(shù)據(jù)解析為 XML 文檔對象。
xhr.responseType = "document";
- "arraybuffer": 表示響應(yīng)的數(shù)據(jù)類型是 ArrayBuffer 對象,適用于二進(jìn)制數(shù)據(jù)的傳輸和處理。
xhr.responseType = "arraybuffer";
- "blob": 表示響應(yīng)的數(shù)據(jù)類型是 Blob 對象,適用于文件下載等場景。
xhr.responseType = "blob";
通過設(shè)置不同的 responseType 值,可以根據(jù)需要獲取不同類型的響應(yīng)數(shù)據(jù)。注意,在設(shè)置 responseType 之前,最好在調(diào)用 open 方法之后、發(fā)送請求之前設(shè)置,以確保設(shè)置生效。
3、Ajax
AJAX(Asynchronous JavaScript and XML,異步 JavaScript 和 XML)是一種使用現(xiàn)有的網(wǎng)頁技術(shù)來創(chuàng)建異步請求和更新頁面內(nèi)容的方法。Ajax 本身不是一種技術(shù),而是一種將一些現(xiàn)有技術(shù)結(jié)合起來使用的方法,包括:HTML 或 XHTML、CSS、JavaScript、DOM、XML、XSLT、以及最重要的 XMLHttpRequest 對象。
當(dāng)使用結(jié)合了這些技術(shù)的 Ajax 模型以后,網(wǎng)頁應(yīng)用能夠快速地將增量更新呈現(xiàn)在用戶界面上,而不需要重載(刷新)整個(gè)頁面。這使得程序能夠更快地回應(yīng)用戶的操作。Ajax 最吸引人的特性是它的“異步”性質(zhì),這意味著它可以與服務(wù)器通信、交換數(shù)據(jù)并更新頁面,而無需刷新頁面。
Ajax 是一種使用瀏覽器提供的 XMLHttpRequest 對象實(shí)現(xiàn)的技術(shù),用于在不刷新整個(gè)頁面的情況下進(jìn)行異步請求和更新頁面內(nèi)容??梢哉f Ajax 是基于瀏覽器提供的 XMLHttpRequest 對象來實(shí)現(xiàn)的。
以下是基于原生 JavaScript 的 AJAX 請求代碼示例:
// 創(chuàng)建 XMLHttpRequest 對象
const xhr = new XMLHttpRequest();
// 指定請求的方法和 URL
xhr.open('GET', 'api_url', true); // 第三個(gè)參數(shù) true 表示異步請求
// 設(shè)置請求頭(如果需要)
xhr.setRequestHeader('Content-Type', 'application/json'); // 根據(jù)實(shí)際需求設(shè)置請求頭
// 注冊一個(gè)回調(diào)函數(shù)來處理響應(yīng)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText); // 處理響應(yīng)數(shù)據(jù)
// 在這里執(zhí)行相應(yīng)的操作
console.log(response);
}
};
// 發(fā)送請求
xhr.send();
雖然 AJAX 是一種強(qiáng)大的技術(shù),但相對于 Axios 和 Fetch API,它有以下一些缺點(diǎn):
- 兼容性問題:AJAX 的兼容性相對較低,尤其在舊版本的瀏覽器中可能會出現(xiàn)問題。而 Axios 和 Fetch API 使用了更現(xiàn)代的 JavaScript 特性,具有更好的兼容性。
- 代碼冗余:使用原生的 AJAX 需要編寫較多的代碼來處理不同的狀態(tài)碼、錯(cuò)誤處理以及請求的拼裝等。而 Axios 和 Fetch API 提供了更簡潔和易用的接口,減少了代碼冗余。
- 缺乏默認(rèn)配置:AJAX 不提供默認(rèn)的全局配置,如請求和響應(yīng)攔截器、統(tǒng)一的錯(cuò)誤處理等。而 Axios 和 Fetch API 支持全局配置,并且提供了更方便的攔截器機(jī)制。
- 功能限制:AJAX 在處理跨域請求時(shí)需要注意添加額外的處理,比如設(shè)置 CORS 頭部信息或者使用 JSONP。而 Axios 和 Fetch API 提供了更直接的方式來處理跨域請求。
- 可讀性較差:由于 AJAX 使用的是回調(diào)函數(shù)來處理異步請求,可能會導(dǎo)致代碼邏輯比較復(fù)雜,可讀性較差。而 Axios 和 Fetch API 使用的是 Promise 或 async/await,使代碼結(jié)構(gòu)更加清晰易讀。
4、Fetch
Fetch 是一種用于進(jìn)行網(wǎng)絡(luò)請求的現(xiàn)代 JavaScript API。它提供了一種簡單、靈活且功能強(qiáng)大的方式,用于從服務(wù)器獲取資源并處理響應(yīng)。
Fetch API 在瀏覽器中原生支持,并且以 Promise 為基礎(chǔ),使得異步請求更加直觀和易用。使用 Fetch API,可以執(zhí)行各種類型的請求(如 GET、POST、PUT、DELETE 等),發(fā)送請求時(shí)可以設(shè)置請求頭、請求參數(shù),以及處理響應(yīng)數(shù)據(jù)。
與傳統(tǒng)的 AJAX 相比,F(xiàn)etch API 具有以下優(yōu)點(diǎn):
- Promise 支持:Fetch API 使用 Promise 對象來處理異步操作,使得處理異步請求的流程更加清晰、易于閱讀和編寫。
- 更簡潔的 API:Fetch API 提供了一個(gè)簡潔的 API,使發(fā)送請求變得更加直觀和簡單,同時(shí)提供了豐富的配置選項(xiàng)(如設(shè)置請求頭、請求參數(shù)等)。
- 內(nèi)置的 JSON 解析:在處理響應(yīng)時(shí),F(xiàn)etch API 內(nèi)置了對 JSON 數(shù)據(jù)的解析,無需手動(dòng)進(jìn)行解析操作。
- 更好的錯(cuò)誤處理:Fetch API 使用了更全面的錯(cuò)誤處理機(jī)制,允許通過檢查響應(yīng)狀態(tài)碼來確定請求是否成功,并以不同的方式處理錯(cuò)誤。
fetch()
Fetch API 提供了一個(gè)全局的 fetch() 方法,該方法提供了一種簡單、邏輯的方式來通過網(wǎng)絡(luò)異步獲取資源。
fetch() 方法的語法如下:
fetch(url, options)
.then(response => {
// 在這里處理響應(yīng)
})
.catch(error => {
// 在這里處理錯(cuò)誤
});
這里有兩個(gè)參數(shù):
- url:請求的 URL 地址。
- options(可選):一個(gè)包含請求選項(xiàng)的對象,可以指定請求的方法(method)、請求頭(headers)、請求體(body)等。
注意,fetch()默認(rèn)使用的是 GET 請求,如果需要使用其他方法(如 POST、PUT 等),需要通過 options 參數(shù)進(jìn)行設(shè)置。
fetch() 方法返回一個(gè) Promise 對象,可以使用 .then() 方法來處理成功的響應(yīng),使用 .catch() 方法來處理錯(cuò)誤的情況。
- 在 .then() 中,可以訪問到 response 對象,進(jìn)一步處理響應(yīng)的內(nèi)容。
- 在 .catch() 中,我們可以訪問到 error 對象,用于處理請求過程中的任何錯(cuò)誤。
options 對象包含的屬性如下:
{
method: 'POST', // *GET, POST, PUT, DELETE等
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', // no-referrer, *client
body: JSON.stringify(data)
// body 數(shù)據(jù)類型必須與 "Content-Type" 請求頭匹配
}
- method:請求方法,例如 GET、POST、PUT、DELETE 等。
- mode:請求模式,可以是 no-cors、*cors、same-origin 等。
- cache:緩存模式,可以是 default、no-cache、reload、force-cache、only-if-cached 等。
- credentials:請求的憑證模式,可以是 include、*same-origin、omit 等。
- headers:請求頭對象,用于設(shè)置請求頭的鍵值對。
- redirect:重定向模式,可以是 manual、*follow、error 等。
- referrerPolicy:引用頁面隱私設(shè)置,可以是 no-referrer、*client 等。
- body:請求體數(shù)據(jù),必須與 "Content-Type" 請求頭指定的數(shù)據(jù)類型匹配。在示例中,使用JSON.stringify()將數(shù)據(jù)轉(zhuǎn)換為 JSON 字符串。
response
一旦獲取到響應(yīng)(Response),返回的對象包含以下屬性:
- response.body:一個(gè)簡單的 getter,提供了響應(yīng)內(nèi)容的可讀流(ReadableStream)。
- response.bodyUsed:一個(gè)布爾值,用于記錄響應(yīng)體是否已經(jīng)被使用過。
- response.headers:與響應(yīng)相關(guān)聯(lián)的頭部信息對象。
- response.ok:一個(gè)布爾值,指示響應(yīng)是否成功。
- response.redirected:指示響應(yīng)是否是重定向結(jié)果的布爾值。
- response.status:響應(yīng)的狀態(tài)碼。
- response.statusText:與狀態(tài)碼對應(yīng)的狀態(tài)消息。
- response.type:響應(yīng)的類型。
- response.url:響應(yīng)的 URL。
我們可以使用 response.type 來確定響應(yīng)的類型,并根據(jù)不同的類型采取相應(yīng)的處理方法:
fetch(url)
.then(response => {
// 檢查響應(yīng)狀態(tài)碼
if (!response.ok) {
throw new Error('Network response was not ok');
}
// 定義一個(gè)響應(yīng)類型與解析方法的映射關(guān)系
const responseTypes = new Map([
['json', () => response.json()],
['text', () => response.text()],
['formData', () => response.formData()],
['blob', () => response.blob()],
['arrayBuffer', () => response.arrayBuffer()]
]);
// 根據(jù)響應(yīng)類型選擇相應(yīng)的解析方法
const parser = responseTypes.get(response.type);
if (parser) {
return parser();
} else {
throw new Error('Unsupported response type');
}
})
.then(data => {
// 處理數(shù)據(jù)
console.log(data);
})
.catch(error => {
// 處理錯(cuò)誤情況
console.error('Error:', error);
});
Response 對象提供了 5 個(gè)方法,用于從 HTTP 響應(yīng)中獲取不同類型的數(shù)據(jù):
- response.json():將響應(yīng)體解析為 JSON 對象。如果響應(yīng)的 Content-Type 是 application/json,則使用此方法。
- response.text():將響應(yīng)體解析為文本字符串。如果響應(yīng)的 Content-Type 是純文本類型,如 text/plain 或 text/html,則使用此方法。
- response.formData():將響應(yīng)體解析為 FormData 對象。如果響應(yīng)的 Content-Type 是 multipart/form-data,則使用此方法。FormData 通常用于上傳文件或提交表單數(shù)據(jù)。
- response.blob():將響應(yīng)體解析為 Blob 對象。Blob 對象表示二進(jìn)制大對象,可以是圖像、音頻、視頻等類型的數(shù)據(jù)。
- response.arrayBuffer():將響應(yīng)體解析為 ArrayBuffer 對象。ArrayBuffer 是一種表示二進(jìn)制數(shù)據(jù)的固定長度緩沖區(qū)。
這些方法返回一個(gè) Promise,當(dāng)解析完成時(shí),Promise 將被解析為相應(yīng)的數(shù)據(jù)類型。
請求頭和響應(yīng)頭
fetch 函數(shù)的請求頭包含在發(fā)起 HTTP 請求時(shí)發(fā)送給服務(wù)器的信息,用于傳遞額外的參數(shù)和配置。可以使用 headers 對象來設(shè)置和操作請求頭。常見的請求頭字段包括:
- Content-Type:指定請求體的格式類型,如 application/json、application/x-www-form-urlencoded 等。
- Authorization:用于身份驗(yàn)證,通常與 Token 或用戶名密碼一起使用。
- Accept:指定客戶端所能接受的響應(yīng)數(shù)據(jù)類型。
- User-Agent:標(biāo)識發(fā)起請求的用戶代理(瀏覽器或應(yīng)用程序)的信息。
在 fetch 函數(shù)中可以通過第二個(gè)參數(shù)進(jìn)行配置,其中可以指定請求頭:
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
}
})
.then(response => {
// 處理響應(yīng)
})
.catch(error => {
// 處理錯(cuò)誤
});
響應(yīng)頭是服務(wù)器在響應(yīng) HTTP 請求時(shí)發(fā)送給客戶端的頭部信息??梢酝ㄟ^ Response 對象的 headers 屬性訪問響應(yīng)頭。常見的響應(yīng)頭字段包括:
- Content-Type:指定響應(yīng)體的格式類型。
- Set-Cookie:設(shè)置或修改客戶端的 Cookie。
- Cache-Control:控制緩存的行為,如 no-cache、max-age 等。
- Content-Disposition:指定響應(yīng)的內(nèi)容該如何展示(如文件的下載)。
在處理 fetch 返回的 Response 對象時(shí),可以通過調(diào)用 response.headers.get('Header-Name') 方法來獲取特定的響應(yīng)頭字段的值。
fetch(url)
.then(response => {
const contentType = response.headers.get('Content-Type');
// 其他處理邏輯
})
.catch(error => {
// 處理錯(cuò)誤
});
錯(cuò)誤處理
除了可以使用 catch() 來處理錯(cuò)誤之外,與使用其他異步操作一樣,我們也可以使用 async/await 來處理異步請求,使代碼更加簡潔和易讀:
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (response.ok) {
const data = await response.json();
console.log(data); // 處理解析后的數(shù)據(jù)
} else {
throw new Error('請求失敗');
}
} catch (error) {
console.log(error); // 處理錯(cuò)誤
}
}
fetchData();
取消請求
在標(biāo)準(zhǔn)的 Fetch API 中,沒有提供直接取消 Fetch 請求的內(nèi)置方法。但是,可以使用以下方法來模擬或?qū)崿F(xiàn)取消 Fetch 請求的效果。
使用 AbortController 和 AbortSignal:這是一種較新的瀏覽器特性,用于生成可以取消請求的信號??梢詣?chuàng)建一個(gè) AbortController對象,然后將其關(guān)聯(lián)到 Fetch 請求中,當(dāng)需要取消請求時(shí),調(diào)用 AbortController 的 abort()方法:
// 創(chuàng)建 AbortController 和關(guān)聯(lián)的 signal
const abortController = new AbortController();
const signal = abortController.signal;
// 發(fā)起 Fetch 請求,并將 signal 傳遞給 fetch 函數(shù)
fetch(url, { signal })
.then(response => {
// 處理響應(yīng)
})
.catch(error => {
if (error.name === 'AbortError') {
// 請求已被取消
} else {
// 處理其他錯(cuò)誤
}
});
// 當(dāng)需要取消請求時(shí),調(diào)用 abort() 方法
abortController.abort();
瀏覽器兼容
目前,主流瀏覽器都支持 Fetch API:
圖片
5、Axios
Axios 是一個(gè)基于 Promise 網(wǎng)絡(luò)請求庫,用于在瀏覽器和 Node.js 中進(jìn)行 HTTP 請求。在服務(wù)端它使用原生 node.js http 模塊, 而在客戶端 (瀏覽端) 則使用 XMLHttpRequests。Axios 是目前最流行的 HTTP 請求庫,其 npm 每周下載量達(dá)到了 4500w+。
Axios 庫具有以下特點(diǎn):
- 瀏覽器和 Node.js:Axios 可在瀏覽器和 Node.js 環(huán)境中使用,可以在不同的平臺上執(zhí)行 HTTP 請求。
- Promise API:Axios 使用 Promise API 進(jìn)行異步操作,能夠更輕松地處理異步請求和響應(yīng)。
- 請求攔截和響應(yīng)攔截:可以通過攔截器,在請求發(fā)送之前或響應(yīng)返回之后對請求進(jìn)行全局性或個(gè)性化的變換和處理??梢栽谡埱蠡蝽憫?yīng)的不同階段添加公共的請求頭、驗(yàn)證身份、處理錯(cuò)誤等。
- 取消請求:Axios 允許取消未完成的請求,以避免無效的請求,并減輕服務(wù)器的負(fù)擔(dān)。取消請求可以通過創(chuàng)建取消令牌、使用取消令牌進(jìn)行請求配置或者在攔截器中中斷請求來實(shí)現(xiàn)。
- 并發(fā)請求:Axios 提供了執(zhí)行多個(gè)并發(fā)請求的能力,可以同時(shí)發(fā)起多個(gè)請求,并在所有請求完成后進(jìn)行處理。
- 自動(dòng)轉(zhuǎn)換數(shù)據(jù):Axios 可以自動(dòng)將請求和響應(yīng)的數(shù)據(jù)進(jìn)行格式轉(zhuǎn)換,包括 JSON、URL 編碼等。無需手動(dòng)處理數(shù)據(jù)轉(zhuǎn)換的過程。
- 錯(cuò)誤處理機(jī)制:當(dāng)請求過程中出現(xiàn)錯(cuò)誤時(shí),Axios 會返回詳細(xì)的錯(cuò)誤信息,包括 HTTP 錯(cuò)誤狀態(tài)碼、錯(cuò)誤描述等??梢愿鶕?jù)需要對這些錯(cuò)誤進(jìn)行處理和顯示。
- 簡潔的 API:Axios 的 API 設(shè)計(jì)簡潔易用,具有直觀的方法命名和參數(shù)配置??梢暂p松地使用 Axios 進(jìn)行 GET、POST、PUT、DELETE 等常見的 HTTP 請求。
可以通過以下命令來安裝 Axios:
// 使用 npm 安裝
npm install axios
// 使用 yarn 安裝
yarn add axios
下面來進(jìn)行一個(gè)簡單的 get 請求:
axios.get('https://api.example.com/data')
.then(response => {
// 處理成功響應(yīng)
console.log(response.data);
})
.catch(error => {
// 處理錯(cuò)誤
console.error(error);
});
這里使用 axios.get 方法發(fā)起了一個(gè) GET 請求,并將請求的 URL 作為參數(shù)傳遞給該方法。然后使用 Promise 的 .then 方法處理成功響應(yīng),并通過 response.data 獲取響應(yīng)數(shù)據(jù)。如果請求失敗,可以通過 Promise 的 .catch 方法捕獲錯(cuò)誤。
請求方法
axios 支持通過簡寫方式來執(zhí)行不同類型的請求:
- axios.request(config)
- axios.get(url[, config])
- axios.delete(url[, config])
- axios.head(url[, config])
- axios.options(url[, config])
- axios.post(url[, data[, config]])
- axios.put(url[, data[, config]])
- axios.patch(url[, data[, config]])
對于這些方法,第一個(gè)參數(shù)是請求的 URL,config 和 data 分別是請求的配置項(xiàng)和請求參數(shù),這兩個(gè)參數(shù)都是可選的。例如,下面是一個(gè) post 請求:
const options = {
headers: {'X-Custom-Header': 'value'}
};
axios.post('/save', { a: 10 }, options)
.then(response => {
// 處理成功響應(yīng)
console.log(response.data);
})
.catch(error => {
// 處理錯(cuò)誤
console.error(error);
});
當(dāng)作為第二個(gè)參數(shù)傳遞給 axios.post 函數(shù)時(shí),Axios 會自動(dòng)將 JavaScript 對象序列化為 JSON。 這樣就無需將 POST 正文序列化為 JSON。Axios 還會自動(dòng)將 Content-Type 請求頭設(shè)置為 application/json。
多個(gè)請求
在 Axios 中,可以使用 axios.all 和 axios.spread 來處理多個(gè)并發(fā)的請求:
const axios = require('axios');
// 創(chuàng)建多個(gè)請求
const request1 = axios.get('https://api.example.com/data1');
const request2 = axios.get('https://api.example.com/data2');
// 并發(fā)發(fā)送多個(gè)請求
axios.all([request1, request2])
.then(axios.spread((response1, response2) => {
// 處理各個(gè)請求的響應(yīng)
console.log(response1.data);
console.log(response2.data);
}))
.catch(error => {
// 處理錯(cuò)誤
console.error(error);
});
可以看到,在 .then 方法中使用了 axios.spread 函數(shù)將多個(gè)請求的響應(yīng)結(jié)果進(jìn)行解構(gòu),通過多個(gè)參數(shù)分別接收各個(gè)請求的響應(yīng)。可以根據(jù)實(shí)際情況命名這些參數(shù),并通過 response1.data、response2.data 等方式獲取各個(gè)請求的響應(yīng)數(shù)據(jù)。
請求攔截、響應(yīng)攔截
在 Axios 中,可以使用 transformRequest 方法在請求發(fā)送之前對請求數(shù)據(jù)進(jìn)行轉(zhuǎn)換和處理,它是一個(gè)請求攔截器,是一個(gè)可選的函數(shù)。
transformRequest 函數(shù)接收兩個(gè)參數(shù):requestData 和 requestHeaders。其中,requestData 是要發(fā)送的請求數(shù)據(jù),requestHeaders 是要發(fā)送的請求頭信息??梢栽?nbsp;transformRequest 函數(shù)內(nèi)部對這些參數(shù)進(jìn)行修改,并將修改后的值返回。返回的結(jié)果將作為實(shí)際發(fā)送請求的數(shù)據(jù)。
axios({
url: 'https://api.example.com/data',
method: 'post',
data: {
id: 12345,
name: 'John Doe'
},
transformRequest: (data, headers) => {
// 對請求數(shù)據(jù)進(jìn)行轉(zhuǎn)換和處理
const modifiedData = { ...data }; // 復(fù)制原始數(shù)據(jù)
// 修改數(shù)據(jù)或添加額外字段
modifiedData.extraField = 'Extra Value';
// 修改請求頭信息
headers['Content-Type'] = 'application/json';
return JSON.stringify(modifiedData); // 返回處理后的數(shù)據(jù)
}
})
.then(response => {
// 處理成功響應(yīng)
console.log(response.data);
})
.catch(error => {
// 處理錯(cuò)誤
console.error(error);
});
這里使用 Axios 發(fā)起了一個(gè) POST 請求。通過傳遞包含 transformRequest 函數(shù)的配置對象來定義請求。在 transformRequest 函數(shù)內(nèi)部,復(fù)制了原始的請求數(shù)據(jù) data,并進(jìn)行了一些修改和處理,如添加了額外的字段和修改了請求頭信息。最終,將修改后的數(shù)據(jù)以 JSON 字符串的形式返回。Axios 將使用 transformRequest 函數(shù)返回的結(jié)果作為實(shí)際發(fā)送請求的數(shù)據(jù)。
除了可以對請求進(jìn)行攔截之外,Axios 還支持對響應(yīng)進(jìn)行攔截,對響應(yīng)數(shù)據(jù)進(jìn)行轉(zhuǎn)換和處理??梢酝ㄟ^ transformResponse 響應(yīng)攔截器來實(shí)現(xiàn)。該函數(shù)接收一個(gè)參數(shù):responseData,它是從服務(wù)器接收到的原始響應(yīng)數(shù)據(jù)??梢栽?nbsp;transformResponse 函數(shù)內(nèi)部對這個(gè)參數(shù)進(jìn)行修改,并將修改后的值返回。返回的結(jié)果將作為實(shí)際處理響應(yīng)的數(shù)據(jù)。
axios.get('https://api.example.com/data', {
transformResponse: (data) => {
// 對響應(yīng)數(shù)據(jù)進(jìn)行轉(zhuǎn)換和處理
const parsedData = JSON.parse(data); // 解析 JSON 字符串
// 修改數(shù)據(jù)或添加額外字段
parsedData.extraField = 'Extra Value';
return parsedData; // 返回處理后的數(shù)據(jù)
}
})
.then(response => {
// 處理成功響應(yīng)
console.log(response.data);
})
.catch(error => {
// 處理錯(cuò)誤
console.error(error);
});
這里使用 Axios 發(fā)起了一個(gè) GET 請求,并通過傳遞包含 transformResponse 函數(shù)的配置對象來定義請求。在 transformResponse 函數(shù)內(nèi)部,對從服務(wù)器接收到的響應(yīng)數(shù)據(jù) data 進(jìn)行了一些修改和處理,如解析 JSON 字符串,添加了額外的字段。最終將修改后的數(shù)據(jù)返回。
攔截請求和響應(yīng)
Axios 中,可以使用攔截器來攔截請求和響應(yīng),并在其被發(fā)送或接收之前進(jìn)行一些額外的處理,可以通過 axios.interceptors 對象來添加攔截器。
// 添加請求攔截器
axios.interceptors.request.use(config => {
// 在發(fā)送請求之前做一些處理
console.log('請求攔截器');
// 修改請求配置
config.headers['Authorization'] = 'Bearer token';
return config;
}, error => {
// 處理請求錯(cuò)誤
console.error('請求出錯(cuò):', error);
});
// 添加響應(yīng)攔截器
axios.interceptors.response.use(response => {
// 在接收到響應(yīng)數(shù)據(jù)之前做一些處理
console.log('響應(yīng)攔截器');
// 修改響應(yīng)數(shù)據(jù)
response.data = { ...response.data, extraField: 'Extra Value' };
return response;
}, error => {
// 處理響應(yīng)錯(cuò)誤
console.error('響應(yīng)出錯(cuò):', error);
});
// 發(fā)送請求
axios.get('https://api.example.com/data')
.then(response => {
// 處理成功響應(yīng)
console.log(response.data);
})
.catch(error => {
// 處理請求或響應(yīng)錯(cuò)誤
console.error(error);
});
這里首先使用 axios.interceptors.request.use 方法添加了一個(gè)請求攔截器。該攔截器在發(fā)送請求之前被調(diào)用,并接收請求配置對象 config 作為參數(shù)。可以對請求配置進(jìn)行修改,如添加請求頭信息。最后,要確保返回修改后的配置對象。
接下來,使用 axios.interceptors.response.use 方法添加了一個(gè)響應(yīng)攔截器。該攔截器在接收到響應(yīng)數(shù)據(jù)之前被調(diào)用,并接收響應(yīng)對象 response 作為參數(shù)??梢詫憫?yīng)數(shù)據(jù)進(jìn)行修改,如添加額外的字段。同樣,要確保返回修改后的響應(yīng)對象。
客戶端支持 XSRF 防護(hù)
跨站請求偽造(簡稱 XSRF)是一種攻擊 Web 應(yīng)用的方法,其中攻擊者將自己偽裝成合法且受信任的用戶,以影響應(yīng)用程序與用戶瀏覽器之間的交互。 有很多方法可以執(zhí)行此類攻擊,包括 XMLHttpRequest。
幸運(yùn)的是,Axios 通過允許在發(fā)出請求時(shí)嵌入額外的身份驗(yàn)證數(shù)據(jù)來防止 XSRF。 這使得服務(wù)器能夠發(fā)現(xiàn)來自未經(jīng)授權(quán)的位置的請求。以下是使用 Axios 完成此操作的方法:
const options = {
method: 'post',
url: '/login',
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
};
axios(options)
.then(response => {
// 處理成功響應(yīng)
console.log(response.data);
})
.catch(error => {
// 處理請求錯(cuò)誤
console.error(error);
});
這里有兩個(gè) xsrf 相關(guān)的屬性:
- xsrfCookieName: 'XSRF-TOKEN':用于跨站請求偽造(XSRF/CSRF)保護(hù)的配置選項(xiàng)之一。它指定了存儲 XSRF 令牌的 cookie 的名稱。XSRF 令牌用于防止惡意網(wǎng)站發(fā)起對已驗(yàn)證用戶的請求。
- xsrfHeaderName: 'X-XSRF-TOKEN':用于跨站請求偽造(XSRF/CSRF)保護(hù)的配置選項(xiàng)之一。它指定了包含 XSRF 令牌的請求頭的名稱。服務(wù)器端可以通過檢查該請求頭來驗(yàn)證請求的合法性。
請求進(jìn)度
Axios 的另一個(gè)有趣的功能是能夠監(jiān)控請求的進(jìn)度,這在下載或上傳大文件時(shí)特別有用,可以使用 onUploadProgress 和 onDownloadProgress 兩個(gè)配置選項(xiàng)來實(shí)現(xiàn)。
對于上傳進(jìn)度,可以使用 onUploadProgress 配置選項(xiàng)。它會在上傳數(shù)據(jù)時(shí)觸發(fā),并提供關(guān)于上傳進(jìn)度的信息。
axios.post('/upload', data, {
onUploadProgress: progressEvent => {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
console.log(`上傳進(jìn)度:${percentCompleted}%`);
},
})
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
這里發(fā)送了一個(gè) POST 請求,在配置選項(xiàng)中使用了 onUploadProgress。當(dāng)數(shù)據(jù)上傳過程中觸發(fā)進(jìn)度事件時(shí),回調(diào)函數(shù)會被執(zhí)行。在回調(diào)函數(shù)中,我們計(jì)算出了已上傳數(shù)據(jù)的百分比,并將其打印出來。
對于下載進(jìn)度,可以使用 onDownloadProgress 配置選項(xiàng)。它會在接收到響應(yīng)數(shù)據(jù)時(shí)觸發(fā),并提供關(guān)于下載進(jìn)度的信息。
axios.get('/download', {
onDownloadProgress: progressEvent => {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
console.log(`下載進(jìn)度:${percentCompleted}%`);
},
})
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
這里發(fā)送了一個(gè) GET 請求,在配置選項(xiàng)中使用了 onDownloadProgress。當(dāng)數(shù)據(jù)下載過程中觸發(fā)進(jìn)度事件時(shí),回調(diào)函數(shù)會被執(zhí)行。在回調(diào)函數(shù)中,我們計(jì)算出了已下載數(shù)據(jù)的百分比,并將其打印出來。
取消請求
在 Axios 中,可以使用取消令牌(cancel token)來取消請求。取消令牌是一個(gè)對象,它表示一個(gè)具體的取消操作,并允許在需要時(shí)中止請求。
// 創(chuàng)建一個(gè)取消令牌源
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
// 發(fā)送請求
axios.get('/api/data', {
cancelToken: source.token
})
.then(response => {
console.log(response.data);
})
.catch(error => {
if (axios.isCancel(error)) {
console.log('請求已被取消:', error.message);
} else {
console.error(error);
}
});
// 取消請求
source.cancel('取消請求的原因');
這里,先創(chuàng)建了一個(gè)取消令牌源 source。然后,發(fā)送 GET 請求時(shí)將 cancelToken 配置選項(xiàng)設(shè)置為 source.token,即將取消令牌與該請求關(guān)聯(lián)起來。當(dāng)需要取消請求時(shí),調(diào)用 source.cancel() 方法,并傳入取消請求的原因作為參數(shù)。
在請求的 .catch() 方法中,我們使用 axios.isCancel(error) 來判斷捕獲的錯(cuò)誤是否是一個(gè)已取消的請求。如果是取消請求導(dǎo)致的錯(cuò)誤,則會打印出 '請求已被取消' 的提示信息。否則,將打印出其他類型的錯(cuò)誤。
請求超時(shí)
可以使用 timeout 配置選項(xiàng)設(shè)置 Axios 請求的超時(shí)時(shí)間,這個(gè)選項(xiàng)指定了請求在多少毫秒后如果沒有得到響應(yīng)就會超時(shí)。
axios.get('/api/data', {
timeout: 5000 // 設(shè)置超時(shí)時(shí)間為5秒
})
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
發(fā)送了一個(gè) GET 請求,并在配置選項(xiàng)中設(shè)置了 timeout 為 5000 毫秒(即 5 秒)。如果請求在 5 秒內(nèi)沒有得到響應(yīng),就會觸發(fā)超時(shí)錯(cuò)誤。在超時(shí)錯(cuò)誤的情況下,請求會被自動(dòng)取消,并且進(jìn)入 .catch() 分支。您可以根據(jù)需要進(jìn)行錯(cuò)誤處理。
注意,如果不設(shè)置 timeout 選項(xiàng),默認(rèn)情況下 Axios 請求是沒有超時(shí)限制的。
6、小結(jié)
相對于 Fetch、XMLHttpRequest 和 Ajax,我還是更喜歡 Axios。它提供了簡潔易用的 API,統(tǒng)一的錯(cuò)誤處理和攔截器支持,取消請求和超時(shí)處理功能,以及基于 Promise 的鏈?zhǔn)秸{(diào)用和跨瀏覽器兼容性。這些特性使得使用 Axios 更方便、高效,并提供更好的開發(fā)體驗(yàn)。