JavaScript Fetch API 教程:GET、POST、错误处理和超时

  1. 首页
  2. 博客
  3. 软件开发

软件开发

JavaScript Fetch API 教程:GET、POST、错误处理和超时

Fetch 是从 JavaScript 调用 API 的内置方法,但它有一个几乎让所有人着迷的惊喜。学习发送请求,处理各种故障,并构建一个可以重用的小助手。

阅读约 9 分钟
JavaScript Fetch API 教程:GET、POST、错误处理和超时

要点速览

  • fetch() 返回 Response 的承诺,读取正文需要一秒钟 await。
  • 404 和 500 等 HTTP 错误 不会拒绝 ,因此请务必检查 response.ok。
  • 要发送 JSON,请使用 JSON.stringify 并设置 Content-Type 标头。
  • Fetch 没有默认超时选项,因此使用 AbortSignal.timeout()。

大多数现代网络应用程序始终与服务器通信。 JavaScript Fetch API 是执行此操作的标准方法,它内置于浏览器和 Node.js 18 及更高版本中,因此无需安装任何内容。

基本使用需要两行,但许多教程就到此为止并忽略了导致真正错误的部分:故障处理。我们针对 Node.js 24 上的小型本地服务器测试了本指南中的示例,显示的输出来自该运行。代码中的 example.com 代表您自己的 API。如果 Promise 对您来说是新的,请从 JavaScript Promise 解释 开始。

什么是获取 API?

Fetch 是一个基于 Promise 的接口,用于发出 HTTP 请求。它取代了旧的、回调驱动的XMLHttpRequest,现在许多项目都使用它,而不是添加一个库来进行简单的调用。

特点 获取 Axios(图书馆)
安装 内置 添加为依赖项
JSON 正文 自己串起来 自动
解析 JSON 响应 致电 response.json() 自动
HTTP 错误拒绝? 否 是的,默认情况下
超时 通过AbortSignal timeout 选项

GET 请求

Fetch 默认发出 GET 请求。有两个步骤,每个步骤都需要 await:首先响应标头到达,然后读取正文。

const response = await fetch("https://example.com/users/1");
const user = await response.json();

console.log(user);

// { id: 1, name: 'Asha' }

存在这两个步骤是因为正文可能很大并且比标头晚到达。在浏览器脚本中,顶级 await 需要一个模块,因此您通常会将其放入 async 函数中。请参阅我们的 JavaScript 异步/等待指南。

响应对象

在阅读正文之前,您可以检查响应:

console.log(response.status, response.ok, response.headers.get("content-type"));

// 200 true application/json
属性或方法 给你
response.ok true 适用于状态 200 至 299
response.status 状态码,例如404
response.headers.get(name) 1个响应头
response.json() 正文解析为 JSON
response.text() 身体就像一根绳子
response.blob() 二进制数据,例如图像

正文只能读取一次。在我们的测试中第二次调用 json() 失败:

await res.json();
await res.json();

// TypeError: Body is unusable: Body has already been read

如果您确实需要阅读两遍,请先调用response.clone()。

最大的惊喜:404 不会抛出异常

这是本文中最重要的事情。我们请求一个不存在的用户:

const res = await fetch("https://example.com/users/999");
console.log(res.status, res.ok);

// 404 false

没有抛出异常。就 fetch 而言,请求有效:已到达服务器并做出响应。仅当根本没有可用的响应到达时(例如网络故障),承诺才会拒绝。所以这样的代码有一个隐藏的错误:

try {
  const res = await fetch(url);
  const data = await res.json();   // may be an error payload
  showUser(data);                  // runs anyway for 404 and 500
} catch (error) {
  showError();                     // does not run for 404 or 500
}

查看我们的 HTTP 状态代码备忘单 以了解每个代码的含义。

可重复使用的帮手

不要到处重复该检查。缠绕一次:

async function getJson(url, options) {
  const response = await fetch(url, options);
  if (!response.ok) {
    throw new Error(`Request failed with status ${response.status}`);
  }
  return response.json();
}

try {
  await getJson("https://example.com/users/999");
} catch (error) {
  console.log("caught:", error.message);
}

// caught: Request failed with status 404

现在 HTTP 错误和网络错误都以相同的 catch 结束。这个小功能值得每个项目都拥有。真实版本还可能从响应正文中读取错误消息,并处理 204 No Content 响应,该响应没有要解析的 JSON。

POST JSON 数据

传递选项对象作为第二个参数。对于 JSON,您必须自己做两件事:将数据转换为字符串并设置标头。

const created = await getJson("https://example.com/users", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "Ben" }),
});

console.log(created);

// { id: 2, name: 'Ben', type: 'application/json' }

我们的测试服务器回显收到的内容类型,确认标头已到达。忘记该标头是导致服务器错误的常见原因,因为许多框架仅在声明 JSON 时才对其进行解析。要上传表单或文件,请传递 FormData 对象作为正文,并不设置 Content-Type,因为浏览器需要自行添加边界值。

其他方法和选项

选项 示例 目的
方法 "PUT"、"PATCH"、"DELETE" HTTP 方法
标头 { 授权:“持有者...” } 请求标头
身体 JSON.stringify(data) 请求正文
信号 AbortSignal.timeout(5000) 取消
凭证 “包括” 跨域发送cookie
缓存 “无商店” 浏览器缓存行为

在 REST 和其他 API 风格之间进行选择?我们对 REST 与 GraphQL 的比较显示了 fetch 如何适合两者。

查询字符串

不要通过连接文本来构建查询字符串,因为特殊字符会破坏它们。 URLSearchParams 为您处理编码:

const params = new URLSearchParams({ q: "red shoes", page: 2 });
console.log(params.toString());

// q=red+shoes&page=2

await getJson(`https://example.com/search?${params}`);

// { q: 'red shoes', page: '2' }

服务器正确接收red shoes,空间恢复。

超时和取消

除非您另有说明,否则 Fetch 将在速度较慢的服务器上等待很长时间。 AbortSignal.timeout()设定了限制。我们的测试端点花费了两秒,而限制为 200 毫秒:

try {
  await fetch("https://example.com/slow", { signal: AbortSignal.timeout(200) });
} catch (error) {
  console.log(error.name);
}

// TimeoutError

要手动取消,例如当用户离开页面或键入新的搜索词时,请使用 AbortController:

const controller = new AbortController();
const pending = fetch("https://example.com/slow", { signal: controller.signal });

controller.abort();

try {
  await pending;
} catch (error) {
  console.log(error.name);
}

// AbortError

要一次运行多个请求,请使用 Promise.all,这比一次等待一个请求要快得多:

const [user, search] = await Promise.all([
  getJson("https://example.com/users/1"),
  getJson("https://example.com/search?q=a&page=1"),
]);

每一种失败

以下是我们运行中每次失败的情况,以及如何区分它们:

情况 会发生什么 如何检测
服务器返回404或500 承诺兑现 response.ok 是错误的
无法访问服务器 拒绝TypeError 抓住
已达到超时时间 拒绝TimeoutError error.name
手动取消 拒绝AbortError error.name
正文不是有效的 JSON json() 拒绝 SyntaxError 抓住

最后一行在生产中很常见。返回 502 的代理通常会发送 HTML 错误页面。我们复制了这个,并且 res.json() 抛出了 SyntaxError。在解析之前检查 response.ok 意味着您报告真正的问题,而不是令人困惑的解析错误。

提示: 有目的地测试这些故障路径。浏览器开发工具可以模拟离线状态,测试工具可以模拟 500 响应。我们的使用 Postman 进行 API 测试 指南涵盖了检查 API 方面的内容。

CORS 和凭据

在浏览器中,从一个源到另一个源的请求受 CORS(跨源资源共享规则)的约束。如果您看到提及 Access-Control-Allow-Origin 的错误,则修复属于服务器,该服务器必须允许您的站点。在您的 JavaScript 中,故障仅显示为一般情况 TypeError,因此请查看浏览器控制台以了解实际原因。 Node.js 不强制实施 CORS,因此在脚本中有效的请求在浏览器中仍然可能会失败。

默认情况下,浏览器不会针对跨域请求发送 cookie。如果需要,请设置credentials: "include",并确保服务器允许。切勿将秘密 API 密钥放入浏览器 JavaScript 中,因为任何人都可以读取它们。将它们保留在您的服务器上。

常见错误

  • 不检查response.ok。错误响应被视为成功。
  • 忘记第二个 await. response.json() 也会返回一个承诺。
  • 忘记JSON.stringify。在正文发送[object Object]时传递对象。
  • 将Content-Type设置为FormData.这会破坏边界,因此请将其保留。
  • 没有超时。 挂起的请求可能会使页面的微调器无限期地运行。
  • 解析 JSON 以获得空响应。 204 没有正文,json() 将抛出异常。

最后的话

一旦您记住其主要规则,Fetch 就很简单:响应并不等于成功。将小助手复制到您的项目中,添加超时并测试您的失败路径。下一步,构建一个小页面,从 API 加载数据,显示清晰的错误消息,并让用户取消缓慢的搜索。

常见问题

fetch 会抛出 404 错误吗?

否。仅当未收到响应(例如网络故障)时,fetch 才会拒绝。对于HTTP错误,请自行检查response.ok或response.status。

如何使用 fetch 发送 JSON?

设置method为POST,添加application/json的Content-Type header,并通过JSON.stringify作为body传递数据。

为什么 fetch 需要两次等待?

一旦响应标头到达,第一个问题就会解决。第二个等待正文下载并被解析,这可能会在稍后发生。

如何设置获取超时?

传递信号:选项中的AbortSignal.timeout(5000)。如果花费的时间超过那么多毫秒,则该请求将被拒绝,并出现 TimeoutError。

Node.js 中可以使用 fetch 吗?

是的。 Fetch 在 Node.js 18 及更高版本中全局可用。旧版本需要一个包,但 CORS 规则仅适用于浏览器。

继续学习

与Stack Quora合作

需要测试、WordPress 或 SEO 方面的帮助?

我们帮助团队交付更好的软件并发展他们的网站。告诉我们您正在做什么,我们将建议明确的下一步。

继续阅读

更多指南 测试、人工智能和现代开发

喜欢这篇文章吗?选择一个主题并直接跳至我们最新的教程。

人工智能

人工智能幻觉解释:语言模型为何会产生幻觉以及如何减少它

人工智能幻觉解释:它们是什么,为什么语言模型产生它们,常见类型,如何通过基础和提示来减少它们,以及我们进行的简单检查。

阅读文章 →
DevOps

环境变量解释:.env 文件、秘密和配置正确完成

使用我们运行的 Python 示例解释环境变量:读取和设置它们、为什么每个值都是字符串、.env 文件、进程继承、秘密和验证。

阅读文章 →
软件开发

API 速率限制解释:算法、标头和处理 429 错误

API 速率限制通过我们运行的模拟进行了解释:令牌桶、固定窗口、滑动窗口、429 响应、速率限制标头、带回退的重试和测试限制。

阅读文章 →
测试自动化

Python 中的模拟:unittest.mock、补丁以及导致测试谎言的错误

使用unittest.mock 在Python 中进行模拟,经过测试:模拟和return_value、side_effect、修补使用名称的位置、spec 和autospec、模拟日期以及常见错误。

阅读文章 →
软件测试

代码覆盖率解释:行覆盖率与分支覆盖率以及 100% 的真正含义

用真实的coverage.py运行来解释代码覆盖率:行、分支和条件覆盖率,阅读报告,为什么100%覆盖率仍然会错过错误,以及合理的目标。

阅读文章 →
数据库与 SQL

SQL 中的 DELETE、TRUNCATE 和 DROP:差异、回滚以及何时使用它们

DELETE、TRUNCATE 和 DROP 通过经过测试的 SQL 进行了解释:各自删除什么、回滚、触发器、身份重置、速度,以及 PostgreSQL、MySQL、SQL Server 和 SQLite 有何不同。

阅读文章 →
数据库与 SQL

SQL 视图解释:创建、使用和避免陷阱(附示例)

SQL 视图通过我们运行的查询进行解释:创建视图、实时结果、隐藏列、性能、可更新和物化视图、破坏的依赖关系和最佳实践。

阅读文章 →
软件开发

TypeScript 接口与类型:真正的差异,使用编译器进行测试

TypeScript 接口与类型,使用真实编译器进行检查:两者都可以做什么,只有一个可以做什么,声明合并,扩展与交集以及要遵循的简单规则。

阅读文章 →
软件开发

Python *args 和 **kwargs 解释:灵活的函数和清晰的示例

Python *args 和 **kwargs 用我们运行的代码进行了解释:打包和解包、参数顺序、仅关键字和仅位置参数、装饰器和常见错误。

阅读文章 →
软件开发

Python Lambda 函数:语法、实际用途以及何时不使用它们

Python lambda 函数用我们运行的代码进行了解释:语法、使用键排序、映射和过滤器、闭包、后期绑定陷阱,以及何时普通 def 是更好的选择。

阅读文章 →
人工智能

什么是人工智能代理?它们是如何工作的,只需运行一个简单的代理循环

什么是人工智能代理?关于代理循环、工具、内存和护栏的简单指南,以及我们运行的一个小型 Python 循环、真正的风险以及何时简单的聊天机器人是更好的选择。

阅读文章 →
DevOps

监控与可观察性:真正的区别,包括日志、指标和跟踪

监控与可观察性解释:已知和未知的故障、日志、指标和跟踪、检查事件演练、黄金信号、OpenTelemetry 和有用的警报。

阅读文章 →
DevOps

Docker Volumes 与 Bind Mounts:以正确的方式保留容器数据

Docker 卷与绑定挂载解释:为什么容器数据消失、命名卷、绑定挂载、tmpfs、Compose 示例、备份、权限以及何时使用它们。

阅读文章 →
测试自动化

TDD 与 BDD:主要区别、以两种方式构建的一个功能以及何时使用每种方式

TDD 与 BDD 的解释是通过一种双向构建的折扣功能:使用我们运行的代码进行红绿重构、Given-When-Then 场景、工具、权衡以及何时使用每种方案。

阅读文章 →
软件测试

用户验收测试 (UAT):流程、清单和真实示例

用户验收测试解释:谁运行它、它与 QA 有何不同、七步流程、UAT 测试用例模板、清单、签核标准和常见错误。

阅读文章 →
数据库与 SQL

SQL NULL 值解释:IS NULL、COALESCE、NULLIF 和要避免的陷阱

通过我们运行的查询解释了 SQL NULL 值:三值逻辑、IS NULL、COALESCE、NULLIF,聚合、连接和排序如何处理 NULL,以及它悄悄引起的错误。

阅读文章 →
数据库与 SQL

SQL CASE WHEN 语句:语法、实例和常见错误

SQL CASE WHEN 解释了我们运行的查询:简单和搜索表单、条件顺序、SUM 透视、COUNT 技巧、自定义排序、更新和要避免的 NULL 陷阱。

阅读文章 →
软件开发

什么是智威汤逊?通过工作示例解释 JSON Web 令牌

什么是智威汤逊? JSON Web Tokens 解释:这三个部分,在我们运行的代码中构建和篡改的令牌,JWT 与会话,存储,刷新令牌和安全错误。

阅读文章 →
软件开发

JavaScript 事件循环解释:调用堆栈、微任务和计时器以及示例

JavaScript 事件循环用我们运行的代码进行了解释:调用堆栈、任务和微任务队列、为什么 setTimeout 0 不是立即执行、阻塞、饥饿和面试测验。

阅读文章 →
软件开发

Python 生成器和 Yield 解释:通过真实示例节省内存

Python 生成器和yield 用我们运行的代码进行了解释:它们如何暂停和恢复、测量内存节省、管道、无限序列、yield from、发送和常见陷阱。

阅读文章 →
人工智能

什么是嵌入?向量和余弦相似度简单解释

什么是嵌入?关于向量、余弦相似度和语义搜索的简单语言指南,其中包含我们运行的一个小示例、常见用途、选择模型和已知限制。

阅读文章 →
DevOps

蓝绿部署与金丝雀部署:差异、权衡以及如何选择

蓝绿部署与金丝雀部署解释:每种部署的工作原理、成本、风险和回滚、我们测试的稳定用户分桶、数据库迁移、滚动更新以及如何选择。

阅读文章 →
测试自动化

不稳定的测试:造成这些问题的原因以及如何彻底解决它们

我们运行的演示解释了不稳定的测试:主要原因、如何确认和诊断、正确的时间修复、共享状态和日期以及隔离过程。

阅读文章 →
软件测试

负载测试与压力测试:差异、指标和 k6 示例

负载测试与压力测试的解释:各自证明什么、峰值和浸泡测试、重要的指标、为什么平均值会误导、k6 个示例以及如何计划测试。

阅读文章 →
软件测试

测试用例与测试场景:主要区别、示例以及何时使用它们

测试用例与测试场景的解释:清晰的定义、并排的表格、从场景到案例的密码重置功能、模板以及何时使用每个功能。

阅读文章 →
数据库与 SQL

SQL 查询优化:读取执行计划并修复慢速查询(带计时)

对一百万行进行 SQL 查询优化测试:读取执行计划、添加正确的索引、修复不可控制的过滤器、使用复合索引和键集分页。

阅读文章 →
数据库与 SQL

SQL UNION 与 UNION ALL:差异、示例和性能测试

SQL UNION 与 UNION ALL 通过我们运行的查询进行了解释:重复、都遵循的规则、真实的数据错误、测量的性能、INTERSECT 和 EXCEPT 以及何时使用它们。

阅读文章 →
软件开发

JavaScript Fetch API 教程:GET、POST、错误处理和超时

针对本地服务器测试的 JavaScript Fetch API 教程:GET 和 POST、为什么不抛出 404、可重用的帮助程序、查询字符串、超时、CORS 和常见错误。

阅读文章 →
软件开发

Python 文件处理:读取、写入、CSV、JSON 和 pathlib 以及示例

使用我们运行的代码解释了 Python 文件处理:打开模式、使用、读取大文件、CSV、JSON、pathlib、二进制文件、安全写入、错误处理和常见错误。

阅读文章 →
软件开发

Git merge 与 rebase 的区别:示例及如何选择

用真实的 log 输出、对比表、rebase 黄金法则和安全的团队工作流,讲清 Git merge 与 rebase 的区别。

阅读文章 →
软件开发

Python 列表推导式:语法、示例与何时避免使用

Python 列表推导式详解:语法、过滤、if-else、嵌套循环、实测耗时,以及何时普通 for 循环更合适。

阅读文章 →
数据库与 SQL

SQL JOIN 详解:附示例(INNER、LEFT、RIGHT、FULL)

通过示例表和查询,一步步讲解 SQL JOIN:INNER、LEFT、RIGHT、FULL、CROSS 和 SELF,并介绍常见错误与性能技巧。

阅读文章 →