本文說明使用 Fetch API 時會發生的一些錯誤處理方法。Fetch API 可讓您向遠端網路資源提出要求。當您發出遠端網路呼叫時,網頁可能會發生各種潛在的網路錯誤。
以下各節將說明潛在錯誤,並說明如何編寫程式碼來提供靈活等級的功能,以抵禦錯誤和非預期的網路狀況。具有彈性的程式碼除了能滿足使用者的需求,也能維持網站的標準服務水準。
預測潛在網路錯誤
本節說明在以下情境中,使用者建立名為 "My Travels.mp4"
的新影片,然後嘗試將影片上傳到影片分享網站。
使用 Fetch 時,您可輕鬆思考使用者成功上傳影片的快樂路徑。不過,有些路徑也不太順暢,而是網頁程式開發人員必須針對這類路徑制定規劃。這類 (不愉快) 路徑的原因可能是使用者錯誤、未預期的環境條件,或影片分享網站發生錯誤。
使用者錯誤的例子
- 使用者上傳的是圖片檔 (例如 JPEG) ,而非影片檔案。
- 使用者開始上傳錯誤的影片檔案。然後,在上傳過程中,使用者可指定要上傳的正確影片檔案。
- 使用者在上傳影片時不小心按 [取消上傳]。
環境變化範例
- 影片上傳期間,網際網路連線會中斷。
- 影片上傳期間,瀏覽器會重新啟動。
- 影片分享網站伺服器會在影片上傳期間重新啟動。
影片分享網站錯誤的例子
- 影片分享網站無法處理含有空格的檔案名稱。而不是
"My Travels.mp4"
,而不是"My_Travels.mp4"
或"MyTravels.mp4"
之類的名稱。 - 影片分享網站無法上傳超出系統允許檔案大小上限的影片。
- 影片分享網站不支援上傳影片中的視訊轉碼器。
這些例子在實際環境中的可能發生。您或許過去也遇到了這類錯誤!讓我們從先前的每個類別中挑選一個範例,然後討論以下幾點:
- 如果影片分享服務無法處理指定的範例,預設行為為何?
- 此範例中的使用者預期會發生什麼情況?
- 該如何改善流程?
使用 Fetch API 處理錯誤
請注意,下列程式碼範例使用頂層 await
(瀏覽器支援),因為這項功能可以簡化程式碼。
Fetch API 擲回錯誤時
這個範例使用 try
/catch
區塊陳述式,擷取 try
區塊中擲回的任何錯誤。例如,如果 Fetch API 無法擷取指定資源,就會擲回錯誤。請在這類 catch
區塊中,提供「有意義的」使用者體驗。如果旋轉圖示是代表某種進度的常見使用者介面,且已向使用者顯示,您可以在 catch
區塊內執行以下操作:
- 移除頁面上的旋轉圖示。
- 提供實用訊息,說明問題所在,以及使用者可採取的選項。
- 根據可用的選項,向使用者顯示「再試一次」按鈕。
- 在背景,將錯誤的詳細資料傳送到錯誤追蹤服務或後端。這個動作會記錄錯誤,以供日後診斷。
try {
const response = await fetch('https://website');
} catch (error) {
// TypeError: Failed to fetch
console.log('There was an error', error);
}
您也可以在之後診斷您所記錄的錯誤時,撰寫測試案例,在使用者發現錯誤前找出這類錯誤。視錯誤而定,測試可以是單元、整合或接受測試。
聯播網狀態碼代表錯誤時
這個程式碼範例向 HTTP 測試服務發出要求,且一律回應 HTTP 狀態碼 429 Too Many Requests
。有趣的是,回應不會到達 catch
區塊。404 狀態 (以及其他某些狀態碼) 不會傳回網路錯誤,而會正常解析。
如要檢查 HTTP 狀態碼是否成功,您可以使用下列任一選項:
- 使用
Response.ok
屬性判斷狀態碼是否介於200
到299
之間。 - 使用
Response.status
屬性判斷回應是否成功。 - 使用任何其他中繼資料 (例如
Response.headers
) 評估回應是否成功。
let response;
try {
response = await fetch('https://httpbin.org/status/429');
} catch (error) {
console.log('There was an error', error);
}
// Uses the 'optional chaining' operator
if (response?.ok) {
console.log('Use the response here!');
} else {
console.log(`HTTP Response Code: ${response?.status}`)
}
最佳做法是與貴機構和團隊中的人員合作,瞭解潛在的 HTTP 回應狀態碼。後端開發人員、開發人員作業人員和服務工程師有時會提供獨特的深入分析資料,讓您瞭解您可能預期的極端情況。
剖析網路回應時發生錯誤
這個程式碼範例說明剖析回應主體可能會引發的另一種錯誤。Response
介面提供便利的方法,可用來剖析文字或 JSON 等不同類型的資料。下列程式碼會發出網路要求,讓 HTTP 測試服務傳回 HTML 字串做為回應內文。不過,系統會嘗試將回應主體剖析為 JSON,進而擲回錯誤。
let json;
try {
const response = await fetch('https://httpbin.org/html');
json = await response.json();
} catch (error) {
if (error instanceof SyntaxError) {
// Unexpected token < in JSON
console.log('There was a SyntaxError', error);
} else {
console.log('There was an error', error);
}
}
if (json) {
console.log('Use the JSON here!', json);
}
您必須準備好要採用各種回應格式的程式碼,並確認非預期的回應不會對網頁造成損害。
請考量以下情境:您擁有會傳回有效 JSON 回應的遠端資源,且該資源已透過 Response.json()
方法成功剖析。服務可能會停止運作。降級後,系統會傳回 500 Internal Server Error
。如未在剖析 JSON 期間使用適當的錯誤處理技術,就會因為擲回未處理的錯誤,中斷使用者的網頁。
何時必須先取消網路要求才能完成
這個程式碼範例使用 AbortController
取消傳輸中要求。執行中的要求是指已啟動但尚未完成的網路要求。
可能需要取消執行中的要求各有不同,但最終取決於您的用途和環境。下列程式碼示範如何將 AbortSignal
傳遞至 Fetch API。AbortSignal
會附加至 AbortController
,而 AbortController
包含 abort()
方法,會向瀏覽器表明應取消網路要求。
const controller = new AbortController();
const signal = controller.signal;
// Cancel the fetch request in 500ms
setTimeout(() => controller.abort(), 500);
try {
const url = 'https://httpbin.org/delay/1';
const response = await fetch(url, { signal });
console.log(response);
} catch (error) {
// DOMException: The user aborted a request.
console.log('Error: ', error)
}
結論
處理錯誤的重要部分是定義可能發生錯誤的各個部分。無論何時,請務必為使用者設定適當的備用方案。您可以針對擷取要求提出下列問題:
- 如果目標伺服器當機,會發生什麼情況?
- 如果擷取作業收到未預期的回應,會發生什麼情況?
- 如果使用者網路連線失敗,會發生什麼情況?
視網頁的複雜程度而定,您也可以草擬流程圖,說明不同情境的功能和使用者介面。