本文演示了使用 Fetch API 时的某些错误处理方法。借助 Fetch API,您可以向远程网络资源发出请求。当您进行远程网络调用时,您的网页可能会出现各种潜在的网络错误。
以下各部分介绍了潜在错误,并介绍了如何编写能够提供合理级别的功能来应对错误和意外网络状况的代码。弹性代码可让用户满意,并为您的网站提供标准服务。
预防潜在的网络连接错误
本部分介绍了以下场景:用户创建了名为 "My Travels.mp4"
的新视频,然后尝试将视频上传到视频共享网站。
使用 Fetch 时,很容易考虑用户成功上传视频的正常路径。不过,还有一些路径不太顺畅,但 Web 开发者必须为此做好规划。此类不理想的路径可能是由用户错误、意外的环境条件或视频分享网站上的 bug 导致的。
用户错误示例
- 用户上传的是图片文件(例如 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)
}
总结
处理错误的一个重要方面是定义可能出错的各个部分。对于每种情况,请确保为用户准备了适当的后备方案。关于提取请求,请思考以下问题:
- 如果目标服务器出现故障,会发生什么情况?
- 如果提取操作收到意外响应,会怎么样?
- 如果用户的互联网连接失败,会怎么样?
根据网页的复杂程度,您还可以绘制一个流程图,描述不同场景的功能和界面。