本文說明使用 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 收到非預期的回應,會發生什麼事?
- 如果使用者的網路連線失敗,會發生什麼情況?
視網頁複雜度而定,您也可以勾勒出流程圖,說明不同情境下的功能和使用者介面。