使用 Fetch API 时实现错误处理
本文介绍了使用 Fetch API 时的一些错误处理方法。通过 Fetch API,您可以向远程网络资源发出请求。当您进行远程网络调用时,您的网页可能会受到各种潜在网络错误的影响。
下面几部分将说明潜在的错误,以及如何编写代码,提供应对错误和意外网络状况的合理功能级别。弹性代码可确保用户满意,并为您的网站维持标准的服务水平。
预测潜在的网络错误
本部分介绍了这样一种情景:用户创建了名为 "My Travels.mp4"
的新视频,然后尝试将视频上传到视频共享网站。
使用抓取时,可以轻松考虑用户成功上传视频的顺利路径。不过,还有一些其他途径不那么顺畅,但 Web 开发者必须规划这些途径。此类(不愉快的)路径可能是由于用户错误、意外的环境条件或视频共享网站上的错误造成的。
用户错误示例
- 用户上传图片文件(如 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 状态代码 429 Too Many Requests
进行响应的 HTTP 测试服务发出请求。有趣的是,响应没有到达 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 收到意外响应,会发生什么情况?
- 如果用户的互联网连接失败会怎么样?
根据网页的复杂程度,您还可以草拟流程图,描述不同情景下的功能和界面。