如何在 Node.js 中使用 Fetch API 发送 HTTP 请求

Advanced Data Extraction Specialist
关键要点
- Fetch API 提供了一个现代化、基于 Promise 的接口,用于在 Node.js 中进行 HTTP 请求,符合浏览器端开发实践。
- Node.js v18 及以上版本原生支持 Fetch API,消除了基础操作中对诸如
node-fetch
等外部库的需求。 - 理解各种 HTTP 方法(GET、POST、PUT、DELETE、PATCH)以及头部、超时和错误处理等高级特性,对于稳健的 API 交互至关重要。
- 有效利用 Fetch API 可以简化数据获取,提高代码可读性,并增强 Node.js 环境中的应用性能。
- 对于复杂的网页抓取和数据获取需求,专门的服务如 Scrapeless 提供了超出原生 Fetch API 的高级功能。
介绍
在现代 web 开发中,进行 HTTP 请求是一项基本任务。无论是从 REST API 获取数据、提交表单数据,还是与第三方服务交互,可靠的网络通信机制都是必不可少的。对于 Node.js 开发者来说,Fetch API 已成为一个强大且标准化的解决方案。本文提供了关于 Node.js Fetch API 的全面指南,详细介绍了各种请求方法、高级配置和最佳实践,以确保高效和稳健的数据交互。我们将探讨十个详细解决方案,并附上代码示例,帮助您构建高性能的 Node.js 应用程序。到最后,您将清楚地了解如何利用 Fetch API 来应对多种用例,从简单的数据检索到复杂的身份验证请求,从而优化您的开发工作流程。
1. 基本的 GET 请求
最常见的 HTTP 请求类型是 GET
,用于从指定资源中检索数据。Node.js Fetch API 显著简化了这个过程。它返回一个 Promise,解析为 Response
对象,之后需要处理该对象以提取实际数据。此方法非常适合从 API 端点获取公共信息或只读数据。
javascript
async function fetchData() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('获取的数据:', data);
} catch (error) {
console.error('获取数据时出错:', error);
}
}
fetchData();
此示例演示了如何发起基本的 GET
请求以从公共 API 检索单个帖子。response.ok
属性检查 HTTP 状态码是否在 200-299 范围内,指示请求成功。这是使用 Node.js Fetch API 进行正确错误处理的重要一步。
2. 基本的 POST 请求
POST
请求用于向服务器发送数据,通常是为了创建一个新资源。当使用 Node.js Fetch API 执行 POST
请求时,您需要在选项对象中将 method
指定为 'POST',并在 body
属性中包含数据。通常情况下,数据以 JSON 格式发送,因此需要将 Content-Type
头设置为 application/json
。
javascript
async function createPost() {
try {
const newPost = {
title: 'foo',
body: 'bar',
userId: 1,
};
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(newPost),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('新创建的帖子:', data);
} catch (error) {
console.error('创建帖子时出错:', error);
}
}
createPost();
此代码片段演示了如何使用 POST
请求创建新帖子。JSON.stringify()
方法将 JavaScript 对象转换为 JSON 字符串,然后作为请求体发送。这是通过 Node.js Fetch API 发送结构化数据的标准做法。
3. 处理 HTTP 头部
HTTP 头部提供有关请求或响应的额外信息。您可以使用 Node.js Fetch API 的选项对象中的 headers
属性自定义请求头部。这在发送身份验证令牌、指定内容类型或设置自定义用户代理时尤其有用。妥善管理头部对于安全和有效的 API 通信至关重要。
javascript
async function fetchWithHeaders() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users/1', {
headers: {
'Authorization': 'Bearer your_token_here',
'User-Agent': 'MyNodeApp/1.0',
'Accept': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
javascript
console.log('带有自定义头的用户数据:', data);
} catch (error) {
console.error('使用头部获取数据时出错:', error);
}
}
fetchWithHeaders();
在这个例子中,我们为API身份验证添加了一个Authorization
头,一个User-Agent
头来识别我们的应用程序,以及一个Accept
头来指定所需的响应格式。这展示了Node.js Fetch API在处理多样化头部需求方面的灵活性。
4. 使用PUT请求更新资源
PUT
请求用于更新服务器上的现有资源。与PATCH
不同,PUT
通常用提供的新数据替换整个资源。当使用Node.js Fetch API进行PUT
请求时,您需要将method
指定为'PUT',并在body
中包含更新的数据。
javascript
async function updatePost() {
try {
const updatedPost = {
id: 1,
title: '更新标题',
body: '更新内容',
userId: 1,
};
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(updatedPost),
});
if (!response.ok) {
throw new Error(`HTTP错误! 状态: ${response.status}`);
}
const data = await response.json();
console.log('帖子已更新:', data);
} catch (error) {
console.error('更新帖子时出错:', error);
}
}
updatePost();
这段代码展示了如何使用PUT
请求更新帖子。整个updatedPost
对象被发送,替换指定URL中现有的资源。这是使用Node.js Fetch API管理数据的常见模式。
5. 使用DELETE请求删除资源
DELETE
请求用于从服务器中删除指定的资源。这些请求通常不需要请求体。Node.js Fetch API通过将method
设置为'DELETE'来处理DELETE
请求。
javascript
async function deletePost() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'DELETE',
});
if (!response.ok) {
throw new Error(`HTTP错误! 状态: ${response.status}`);
}
console.log('帖子已成功删除。');
} catch (error) {
console.error('删除帖子时出错:', error);
}
}
deletePost();
该示例演示了一个简单的DELETE
请求。在成功删除后,服务器通常返回200 OK或204 No Content状态。Node.js Fetch API提供了一种干净的方式来执行这样的操作。
6. 使用PATCH请求进行部分更新
PATCH
请求用于对资源进行部分修改。与PUT
不同,PATCH
仅发送更改。这对于仅需要更新几个字段的大型资源来说可能更高效。Node.js Fetch API通过相应地设置method
来支持PATCH
。
javascript
async function patchPost() {
try {
const partialUpdate = {
title: '部分更新标题',
};
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(partialUpdate),
});
if (!response.ok) {
throw new Error(`HTTP错误! 状态: ${response.status}`);
}
const data = await response.json();
console.log('帖子部分更新:', data);
} catch (error) {
console.error('部分更新帖子时出错:', error);
}
}
patchPost();
这段代码展示了如何执行PATCH
请求仅更新帖子的title
。这种方法对于增量更新效率很高,使得Node.js Fetch API在各种数据管理任务中十分灵活。
7. 处理超时和中止请求
网络请求有时可能会挂起或耗时过长,从而影响用户体验。Node.js Fetch API可以与AbortController
结合使用,以实现请求超时和取消。这是构建能够优雅处理网络问题的稳健应用程序的关键功能。
javascript
async function fetchWithTimeout() {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000); // 5秒超时
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`HTTP错误! 状态: ${response.status}`);
}
const data = await response.json();
console.log('在超时内获取的数据:', data.slice(0, 2)); // 记录前2项
} catch (error) {
if (error.name === 'AbortError') {
console.error('由于超时而中止获取。');
} else {
console.error('在超时内获取数据时出错:', error);
}
}
}
fetchWithTimeout();
这个例子演示了如何为一个 fetch 请求设置 5 秒的超时。如果请求没有在这个时间内完成,它将被中止,并且会捕获到 AbortError
。这种健壮的错误处理对于依赖 Node.js Fetch API 进行外部通信的应用程序至关重要。
8. 发送表单数据 (multipart/form-data)
在处理文件上传或复杂表单提交时,multipart/form-data
是标准内容类型。Node.js Fetch API 可以通过使用 FormData
API 来处理这一点。这对于需要与传统 HTML 表单或文件上传端点交互的 Web 应用程序特别有用。
javascript
async function uploadFile() {
try {
const formData = new FormData();
// 在实际应用中,'file' 将是一个 Blob 或 File 对象
// 为了演示,我们将用一个字符串模拟文件
formData.append('username', 'JohnDoe');
formData.append('profilePicture', 'fake_file_content', 'profile.txt');
const response = await fetch('https://httpbin.org/post', {
method: 'POST',
body: formData,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('文件上传响应:', data);
} catch (error) {
console.error('上传文件时出错:', error);
}
}
uploadFile();
这个例子展示了如何构造 FormData
并用 POST
请求发送它。当 FormData
对象作为 body
提供时,Node.js Fetch API 会自动设置 Content-Type
头为 multipart/form-data
。这简化了处理复杂表单提交的过程。
9. 流式响应
对于大响应或实时数据流,流式响应比等待整个响应下载更高效。Node.js Fetch API 允许您将响应体访问为 ReadableStream
,使您能够分块处理数据。这对于性能至关重要的应用程序或处理连续数据流时特别有益。
javascript
async function streamResponse() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/comments');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const reader = response.body.getReader();
let receivedLength = 0; // 目前接收的字节数
let chunks = []; // 接收到的二进制块数组(构成了主体)
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
chunks.push(value);
receivedLength += value.length;
console.log(`接收到 ${receivedLength} 字节`);
}
const received = new Blob(chunks); // (Blob 是一种类似文件的对象)
const text = await received.text();
console.log('流式响应完成。总长度:', receivedLength, '字节。首 200 个字符:', text.substring(0, 200));
} catch (error) {
console.error('流式响应时出错:', error);
}
}
streamResponse();
这个例子演示了如何将响应体作为流来读取,并按块处理。这种方法可以显著减少内存使用,提高处理大型数据集的应用程序的响应能力,通过 Node.js Fetch API。
10. Fetch API vs. Axios:比较
虽然 Node.js Fetch API 现在是原生的,但 Axios
仍然是一个流行的 HTTP 请求替代方案。了解它们之间的差异有助于选择合适的工具来满足您的项目需求。两者各有优劣,选择通常取决于项目要求和开发者偏好。
特性 | Fetch API (原生) | Axios (第三方库) |
---|---|---|
基于 Promise | 是 | 是 |
浏览器支持 | 现代浏览器原生支持 | 需要为旧浏览器提供 polyfills |
Node.js 支持 | 原生 (v18+) | 需要安装 (npm install axios ) |
自动 JSON 解析 | 手动 (response.json() ) |
自动 |
错误处理 | response.ok 用于 HTTP 错误,catch 用于网络错误 |
在 HTTP 错误(4xx,5xx)时拒绝 promise |
请求中止 | AbortController |
CancelToken (已弃用) / AbortController |
拦截器 | 无原生支持 | 有(请求和响应拦截器) |
上传进度 | 手动流媒体 | 内置 |
XSRF保护 | 无原生支持 | 是 |
捆绑大小 | 零(原生) | 增加捆绑大小 |
Axios提供了更多的开箱即用功能,例如自动JSON解析和拦截器,这可以简化复杂应用程序的开发。然而,原生的Node.js Fetch API提供了一种轻量级、符合标准的解决方案,无需额外的依赖项,使其成为简单用例或者当最小化捆绑大小为优先时的绝佳选择。例如,Cloudflare最近的一份报告指出,HTTP请求仍然是网络流量的重要组成部分,API调用的优化直接影响性能[1]。这突显了选择高效的HTTP请求方法的重要性。
案例研究和应用场景
Node.js Fetch API的多功能性扩展到许多现实世界的应用程序。以下是一些证明其宝贵价值的场景:
场景1:构建服务器端数据聚合器
想象一下,你正在构建一个后端服务,它从多个外部API(例如天气、新闻、股票价格)聚合数据,并向你的前端呈现统一的视图。Node.js Fetch API非常适合这个需求。你可以对不同的端点发起并发请求,处理响应,然后在将它们发送到客户端之前将其组合起来。这种方法对于创建仪表板或数据丰富的应用程序非常高效。
javascript
async function aggregateData() {
try {
const [weatherRes, newsRes] = await Promise.all([
fetch('https://api.weatherapi.com/v1/current.json?key=YOUR_API_KEY&q=London'),
fetch('https://newsapi.org/v2/top-headlines?country=us&apiKey=YOUR_API_KEY')
]);
const weatherData = await weatherRes.json();
const newsData = await newsRes.json();
console.log('聚合数据:', { weather: weatherData, news: newsData.articles.slice(0, 1) });
} catch (error) {
console.error('聚合数据时出错:', error);
}
}
// aggregateData(); // 取消注释以运行,需要有效的API密钥
这个示例展示了使用Node.js Fetch API的Promise.all
并发获取数据,显著加快了数据聚合的速度。
场景2:实现Webhook监听器
Webhook是在发生某些事情时从应用程序发送的自动消息。你的Node.js应用程序可能需要充当Webhook监听器,接收来自GitHub、Stripe或自定义物联网设备的POST
请求。Node.js Fetch API(或者说,底层HTTP服务器)在处理传入请求时至关重要,而fetch
本身则可以用来响应这些webhook或将数据转发到其他服务。
javascript
// 这是一个使用Express.js的Webhook监听器的概念性示例
// Fetch API将用*在*这个监听器中发起出站请求。
// const express = require('express');
// const app = express();
// app.use(express.json());
// app.post('/webhook', async (req, res) => {
// console.log('接收到Webhook:', req.body);
// // 示例:使用Fetch API将数据转发到另一个服务
// try {
// const response = await fetch('https://another-service.com/api/data', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(req.body),
// });
// if (!response.ok) {
// throw new Error(`转发失败: ${response.status}`);
// }
// console.log('Webhook数据成功转发.');
// res.status(200).send('已接收');
// } catch (error) {
// console.error('转发Webhook时出错:', error);
// res.status(500).send('出错');
// }
// });
// const PORT = process.env.PORT || 3000;
// app.listen(PORT, () => console.log(`Webhook监听器运行在端口 ${PORT}`));
这个概念性示例说明了如何将Node.js Fetch API集成到Webhook监听器中以处理和转发数据,展示了其在服务器与服务器之间通信中的作用。
场景3:使用代理的自动化网页抓取
对于需要从网站自动提取数据的任务,Node.js Fetch API可以与代理服务结合使用,以绕过速率限制或地理限制。这是市场研究、价格监控或内容聚合的常见用例。虽然fetch
提供了核心请求功能,但对于大规模抓取操作,通常需要一个强大的代理解决方案。HTTP Archive的年度报告始终显示网页复杂性不断增加,使得高效数据获取变得至关重要[2]。
javascript
async function scrapeWithProxy() {
javascript
const proxyUrl = 'http://your_proxy_ip:your_proxy_port'; // 替换为您的代理详情
const targetUrl = 'https://example.com'; // 替换为目标网站
try {
// 注意:在 Node.js 中使用带代理的原生 Fetch API 可能需要
// 配置全局代理或者使用像 'https-proxy-agent' 这样的库
// 为简单起见,这个示例假设直接连接或已配置的环境。
const response = await fetch(targetUrl, {
// agent: new HttpsProxyAgent(proxyUrl) // 如果使用 https-proxy-agent
});
if (!response.ok) {
throw new Error(`HTTP 错误!状态:${response.status}`);
}
const html = await response.text();
console.log('抓取的 HTML(前 500 个字符):', html.substring(0, 500));
} catch (error) {
console.error('使用代理抓取时出错:', error);
}
// scrapeWithProxy(); // 取消注释以运行,需要代理设置
这个场景突显了 Node.js Fetch API 在网页抓取中的潜力,特别是在增强匿名性和访问权限的代理配置下。
推荐 Scrapeless
虽然 Node.js Fetch API 非常适合一般的 HTTP 请求,但复杂的网页抓取和数据获取任务往往需要更高级的功能,例如处理 CAPTCHA、管理代理、渲染 JavaScript 和应对反机器人措施。对于这些特定需求,我们强烈建议您探索 Scrapeless。Scrapeless 是一项强大的服务,旨在通过提供强大的基础设施简化网页抓取,处理这些复杂性。它使开发人员能够专注于数据提取逻辑,而不是基础设施挑战,对于大规模数据项目来说,这是一个不可或缺的工具。无论您需要抓取电子商务产品数据、监测搜索引擎结果,还是收集社交媒体洞察,Scrapeless 都提供无缝集成到现有工作流程中的量身定制解决方案。例如,他们的 抓取 API 和 通用抓取 API 专为处理动态内容而构建,提供干净、结构化的数据。对于特定需求,如 谷歌搜索数据 或 电子商务数据,Scrapeless 提供的优化解决方案超出了基本的 Node.js Fetch API 实现的能力。他们的平台还提供 社交媒体抓取 和 开发者工具 以进一步帮助您的数据获取之旅。
结论
Node.js Fetch API 提供了一种现代、有效和标准化的方式来执行 HTTP 请求,使其成为任何 Node.js 开发人员不可或缺的工具。从基本的 GET
和 POST
操作到处理复杂场景,如超时、文件上传和流响应,Fetch API 提供了一整套综合功能。它在 Node.js v18+ 中的原生集成进一步简化了开发,消除了外部依赖。虽然它在许多方面表现出色,但理解其局限性以及何时利用像 Scrapeless 这样的专用工具来处理更苛刻的任务是构建真正强大和可扩展应用程序的关键。充分利用 Node.js Fetch API 的力量,增强您应用程序的数据交互能力。
准备好简化您的数据获取和网页抓取工作吗? 今天就注册 Scrapeless!
常见问题
Q1:在 Node.js 中使用原生 Fetch API 相对于外部库的主要优势是什么?
主要优势在于原生 Node.js Fetch API 是直接集成在 Node.js 运行时中的(从 v18 开始),这意味着您不需要安装任何像 node-fetch
或 axios
这样的外部包。这减少了项目依赖,简化了设置,并可能导致应用程序体积更小。它还为浏览器和服务器环境中的 HTTP 请求提供了一致的 API,这对全栈 JavaScript 开发人员非常有利。
Q2:Fetch API 如何处理错误,相对于 Axios 有何不同?
Node.js Fetch API 的错误处理与 Axios 不同。Fetch API 的 fetch()
Promise 只会在网络错误(例如,没有互联网连接、DNS 解析失败)时被拒绝。对于 HTTP 错误(如 404 未找到或 500 服务器内部错误),Promise 仍然会解析,但 response.ok
属性会为 false
。您必须显式检查 response.ok
以确定请求是否成功。相反,Axios 会在任何超出 2xx 范围的 HTTP 状态码时自动拒绝 Promise,使许多开发人员的错误处理变得更简单。
Q3:我可以在 Node.js 中使用 Fetch API 上传文件吗?
是的,您可以使用 Node.js Fetch API 上传文件。通常,您可以通过创建一个 FormData
对象并将文件(或模拟文件内容)附加到其中来实现。当您将 FormData
对象作为 fetch
请求的 body
传递时,API 会自动将 Content-Type
头设置为 multipart/form-data
,这是文件上传的标准。这使得发送二进制数据或复杂表单提交变得简单明了。
Q4:在 Node.js 中使用 Fetch API 时有哪些常见陷阱?
常见陷阱包括忘记检查 response.ok
以处理 HTTP 错误状态、未使用 .catch()
块处理网络错误,以及在对不同域发起请求时与 CORS(跨域资源共享)相关的问题(尽管这在浏览器环境中更常见,但在特定的 Node.js 设置中仍可能出现)。此外,与一些第三方库相比,使用 Fetch API 管理 Cookie 会更加复杂,因为它的行为是基于浏览器标准的。
Q5:Fetch API 适合用于 Node.js 中的网页爬虫吗?
是的,Node.js Fetch API 可以用于基本的网页爬虫任务,特别是获取静态 HTML 内容。然而,对于更高级的爬虫需求,例如渲染 JavaScript 密集型页面、绕过验证码、管理大型代理池或应对复杂的反机器人机制,仅使用原生 Fetch API 可能不够。在这种情况下,像 Scrapeless 这样的专业工具和服务通常更有效,因为它们提供了专门的基础设施和功能来应对这些复杂性。
参考资料
在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。