本文將說明使用 Fetch API 時的部分錯誤處理方法。Fetch API 可讓您向遠端網路資源提出要求。當您發出遠端網路呼叫時,網頁就可能會發生各種網路錯誤。
以下各節將說明潛在錯誤,以及如何編寫程式碼,提供可抵禦錯誤和意外網路情況的合理功能。具備復原力的程式碼可讓使用者滿意,並為網站維持標準服務水準。
預測可能的網路錯誤
本節說明使用者建立名為 "My Travels.mp4"
的新影片,然後嘗試將影片上傳至影片分享網站的情況。
使用 Fetch 時,您可以輕鬆考量使用者成功上傳影片的順利路徑。不過,還有其他路徑不太順暢,但網頁程式開發人員必須規劃這些路徑。這種不理想的路徑可能會因使用者錯誤、環境狀況不佳或影片分享網站的錯誤而發生。
使用者錯誤示例
- 使用者上傳的是圖片檔案 (例如 JPEG),而非影片檔案。
- 使用者開始上傳錯誤的影片檔案。然後,在上傳過程中,使用者指定要上傳的正確影片檔案。
- 使用者在上傳影片時不小心按下「取消上傳」
環境變更的例子
- 影片上傳期間,網際網路連線中斷。
- 瀏覽器會在上傳影片時重新啟動。
- 影片分享網站的伺服器在上傳影片時重新啟動。
影片分享網站錯誤示例
- 影片分享網站無法處理含有空格的檔案名稱。系統會預期名稱為
"My_Travels.mp4"
或"MyTravels.mp4"
的值,而不是"My Travels.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)
}
結論
處理錯誤時,定義可能出錯的各個部分是一項重要環節。針對每個情況,請務必為使用者提供適當的備用方案。請針對擷取要求問自己以下問題:
- 如果目標伺服器發生故障,會發生什麼情況?
- 如果 Fetch 收到非預期的回應,會發生什麼事?
- 如果使用者的網路連線失敗,會發生什麼情況?
視網頁複雜度而定,您也可以勾勒出流程圖,說明不同情境下的功能和使用者介面。