node-fetch 完整指南:在 Node.js 中引入标准 Fetch API

原创2026-09-24 20:22:33918 阅读
文章标签:后端

node-fetch 完整指南:在 Node.js 中引入标准 Fetch API

node-fetch 是一个轻量级模块,把浏览器原生的 window.fetch API 移植到 Node.js 运行时,让你可以用同构的 Promise 语法发起 HTTP(S) 请求、流式读写请求与响应体、自动解码 gzip/deflate/brotli 压缩内容,并借助 AbortSignal 取消请求。本文以仓库 README 为主体,结合源码实现与测试用例,系统讲解从安装配置、日常请求到高级流式用法与完整 API 的实战方案,读完即可在 Node.js 项目中可靠地落地 fetch 开发。

设计动机与定位

为什么不用 XMLHttpRequest 兼容层

社区早期通过把浏览器的 XMLHttpRequest 移植到 Node.js 来实现 fetch polyfill,而 node-fetch 选择了一条更直接的路线:从 Node.js 原生 http 模块直接构建出兼容 window.fetch 的 API,跳过 XHR 这层"中间人",因此代码量最小、语义最贴近 Fetch 标准。需要 isomorphic(同构)用法的场景,可以搭配 isomorphic-unfetch(服务端导出 node-fetch、客户端导出 whatwg-fetch)或 cross-fetch 使用。

核心特性

按 README 的 Features 一节,node-fetch 具备以下关键能力:

  • 与 window.fetch API 保持一致,尽量贴合 WHATWG Fetch 规范与 Stream 规范,同时对已知差异做显式取舍并在文档中说明;
  • 原生 Promise 与 async/await 支持,无回调嵌套;
  • 请求与响应体均使用 Node.js 原生 Readable 流;
  • 正确解码 gzip/deflate/brotli 内容编码,并将 res.text()、res.json() 等字符串输出自动转为 UTF-8;
  • 提供重定向次数限制、响应体大小限制、显式错误类型(docs/ERROR-HANDLING.md)等实用的扩展能力。

从 package.json 可以看到,当前仓库版本为 3.1.1,包入口为 src/index.js,类型定义随包发布在 @types/index.d.ts,依赖仅 data-uri-to-buffer、fetch-blob、formdata-polyfill 三个轻量库。

与客户端 fetch 的已知差异

node-fetch 刻意维护了一份与浏览器 window.fetch 的差异清单,用于在规范实现与 Node.js 现实之间做取舍:

如果你发现某个 window.fetch 提供的能力在 node-fetch 中缺失,可以在项目仓库提交 issue,也欢迎直接提交 Pull Request。

安装与模块加载

环境要求与安装

当前稳定版(3.x)要求 Node.js 12.20.0 及以上。更精确的引擎约束记录在 package.json 的 engines 字段:^12.20.0 || ^14.13.1 || >=16.0.0。

npm install node-fetch

ES Modules(ESM)

v3 起 node-fetch 是 ESM-only 模块(package.json 中 "type": "module"),标准导入方式:

import fetch from 'node-fetch';

CommonJS

v3 无法用 require() 直接导入。如果你暂时无法切换到 ESM,有两个选择:

  1. 使用 v2:v2 保持 CommonJS 兼容,且关键 bug 修复仍会持续发布到 v2 线:
npm install node-fetch@2
  1. 从 CommonJS 异步加载 v3:利用动态 import() 包装一个兼容函数:
// mod.cjs
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));

注入全局对象

不想在每个文件里显式导入,可以写一个 polyfill 模块把 fetch 及其相关类挂到 globalThis:

// fetch-polyfill.js
import fetch, {
  Blob,
  blobFrom,
  blobFromSync,
  File,
  fileFrom,
  fileFromSync,
  FormData,
  Headers,
  Request,
  Response,
} from 'node-fetch'

if (!globalThis.fetch) {
  globalThis.fetch = fetch
  globalThis.Headers = Headers
  globalThis.Request = Request
  globalThis.Response = Response
}

// index.js
import './fetch-polyfill'

// ...

注意这里通过 if (!globalThis.fetch) 做存在性保护,避免覆盖 Node.js 新版本自带的原生 fetch。从源码看,这些导出在 src/index.js 中统一 re-export。

版本升级

下文所有用法示例均以 3.x 为准。

常见用法

抓取纯文本或 HTML

import fetch from 'node-fetch';

const response = await fetch('https://github.com/');
const body = await response.text();

console.log(body);

response.text() 内部通过 consumeBody 把整个流累积为 Buffer,再交给 TextDecoder 解码为 UTF-8 字符串(见 src/body.js)。

抓取 JSON

import fetch from 'node-fetch';

const response = await fetch('https://api.github.com/users/github');
const data = await response.json();

console.log(data);

json() 是 text() + JSON.parse 的语法糖(见 src/body.js),如果响应体不是合法 JSON 会抛出解析异常。

简单 POST

import fetch from 'node-fetch';

const response = await fetch('https://httpbin.org/post', {method: 'POST', body: 'a=1'});
const data = await response.json();

console.log(data);

POST JSON

import fetch from 'node-fetch';

const body = {a: 1};

const response = await fetch('https://httpbin.org/post', {
	method: 'post',
	body: JSON.stringify(body),
	headers: {'Content-Type': 'application/json'}
});
const data = await response.json();

console.log(data);

这里显式设置 Content-Type: application/json。需要留意的是:请求方法名不区分大小写——在 src/request.js 中,delete/get/head/options/post/put 会被自动规范化为大写。

POST 表单参数

URLSearchParams 自 Node.js v10.0.0 起就是全局对象,可直接使用。

import fetch from 'node-fetch';

const params = new URLSearchParams();
params.append('a', 1);

const response = await fetch('https://httpbin.org/post', {method: 'POST', body: params});
const data = await response.json();

console.log(data);

注意:只有当你传入的 body 是 URLSearchParams 实例时,Content-Type 才会被自动设为 x-www-form-urlencoded。这个逻辑在 src/body.js 的 extractContentType 中实现,且带 ;charset=UTF-8 后缀。

异常处理

重要:3xx-5xx 状态码响应不是异常,必须放在 then() / 结果判断里处理,详见下一节。用 try/catch 包裹能捕获的所有异常包括:来自 Node 核心库的错误(如网络错误)、以及 FetchError 实例的操作类错误。完整错误体系见 docs/ERROR-HANDLING.md。

import fetch from 'node-fetch';

try {
	await fetch('https://domain.invalid/');
} catch (error) {
	console.log(error);
}

从源码看,fetch 内部在请求失败时会把底层错误包装成 FetchError(type: 'system')并 reject,见 src/index.js。

处理客户端与服务端错误(4xx/5xx)

实践中常写一个辅助函数检查响应状态:

import fetch from 'node-fetch';

class HTTPResponseError extends Error {
	constructor(response) {
		super(`HTTP Error Response: ${response.status} ${response.statusText}`);
		this.response = response;
	}
}

const checkStatus = response => {
	if (response.ok) {
		// response.status >= 200 && response.status < 300
		return response;
	} else {
		throw new HTTPResponseError(response);
	}
}

const response = await fetch('https://httpbin.org/status/400');

try {
	checkStatus(response);
} catch (error) {
	console.error(error);

	const errorBody = await error.response.text();
	console.error(`Error body: ${errorBody}`);
}

response.ok 是一个便捷属性,其实现为 status >= 200 && status < 300,见 src/response.js。

处理 Cookie

node-fetch 默认不存储 Cookie。你可以通过手动读取响应头中的 Set-Cookie、再在后续请求中构造 Cookie 请求头来实现会话维持,具体读取方法见下文"提取 Set-Cookie 头"。

高级用法

流式下载

"Node 风格"是尽量使用流。可以把 res.body 直接 pipe 到另一个流。下面的例子使用 stream.pipeline 挂接流错误处理并等待下载完成:

import {createWriteStream} from 'node:fs';
import {pipeline} from 'node:stream';
import {promisify} from 'node:util'
import fetch from 'node-fetch';

const streamPipeline = promisify(pipeline);

const response = await fetch('https://github.githubassets.com/images/modules/logos_page/Octocat.png');

if (!response.ok) throw new Error(`unexpected response ${response.statusText}`);

await streamPipeline(response.body, createWriteStream('./octocat.png'));

在 Node.js 14 中还可以用 async iterator 读取 body,但要注意捕获错误——响应运行时间越长,越可能遇到错误:

import fetch from 'node-fetch';

const response = await fetch('https://httpbin.org/stream/3');

try {
	for await (const chunk of response.body) {
		console.dir(JSON.parse(chunk.toString()));
	}
} catch (err) {
	console.error(err.stack);
}

在 Node.js 12 中 async iterator 也可用,但流的 async iteration 直到 Node.js 14 才成熟,需要额外处理流错误并等待响应完全关闭:

import fetch from 'node-fetch';

const read = async body => {
	let error;
	body.on('error', err => {
		error = err;
	});

	for await (const chunk of body) {
		console.dir(JSON.parse(chunk.toString()));
	}

	return new Promise((resolve, reject) => {
		body.on('close', () => {
			error ? reject(error) : resolve();
		});
	});
};

try {
	const response = await fetch('https://httpbin.org/stream/3');
	await read(response.body);
} catch (err) {
	console.error(err.stack);
}

访问响应头与元数据

import fetch from 'node-fetch';

const response = await fetch('https://github.com/');

console.log(response.ok);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers.raw());
console.log(response.headers.get('content-type'));

Headers.raw() 是 node-fetch 独有的非规范方法,返回 Record<string, string<a href="https://link.gitcode.com/i/e01f9c033e97a3232ce95fe1e64f3a3f" target="_blank">]>(见 [src/headers.js),能拿到同一 header 名的全部原始值数组。

提取 Set-Cookie 头

与浏览器不同,node-fetch 可以手动访问原始的 Set-Cookie 头:

import fetch from 'node-fetch';

const response = await fetch('https://example.com');

// 返回一个数组,而不是逗号拼接的字符串
console.log(response.headers.raw()['set-cookie']);

这与 Headers.get() 的行为不同:get() 会把同一名字的多个值用 ', ' 拼接(见 src/headers.js),而 raw() 保留独立条目——这正是提取多个 Set-Cookie 的关键。

用文件作为 POST 数据

node-fetch 自带符合规范的 Blob/File 实现,可以直接从本地文件构造请求体:

import fetch, {
  Blob,
  blobFrom,
  blobFromSync,
  File,
  fileFrom,
  fileFromSync,
} from 'node-fetch'

const mimetype = 'text/plain'
const blob = fileFromSync('./input.txt', mimetype)
const url = 'https://httpbin.org/post'

const response = await fetch(url, { method: 'POST', body: blob })
const data = await response.json()

console.log(data)

fileFromSync/fileFrom 与 blobFromSync/blobFrom 由 fetch-blob 提供并在 src/index.js 中统一导出,分别对应同步与异步两种创建方式。

用 FormData 提交 multipart/form-data

node-fetch 附带规范兼容的 FormData 实现,用于提交 multipart/form-data:

import fetch, { FormData, File, fileFrom } from 'node-fetch'

const httpbin = 'https://httpbin.org/post'
const formData = new FormData()
const binary = new Uint8Array([ 97, 98, 99 ])
const abc = new File([binary], 'abc.txt', { type: 'text/plain' })

formData.set('greeting', 'Hello, world!')
formData.set('file-upload', abc, 'new name.txt')

const response = await fetch(httpbin, { method: 'POST', body: formData })
const data = await response.json()

console.log(data)

在源码中,FormData 请求体会被自动转换为带 boundary 的 Blob,并据此生成 multipart/form-data; boundary=... 的 Content-Type(见 src/body.js 与 src/body.js)。

追加"类 Blob/File"对象:如果需要在任意位置产生一个流并塞进 FormData,可以追加一个"看起来像" Blob 或 File 的对象,最低要求是:

  1. 具有值为 Blob 或 File 的 Symbol.toStringTag getter 或属性;
  2. 有一个已知的 size;
  3. 提供 stream() 方法(返回产出 Uint8Array/Buffer 的任意 async iterable)或 arrayBuffer() 方法(返回 ArrayBuffer)。
formData.append('upload', {
	[Symbol.toStringTag]: 'Blob',
	size: 3,
  *stream() {
    yield new Uint8Array([97, 98, 99])
	},
	arrayBuffer() {
		return new Uint8Array([97, 98, 99]).buffer
	}
}, 'abc.txt')

只要 stream() 产出 Uint8Array(或 Buffer),Node.js Readable 流和 whatwg streams 都能直接使用。

用 AbortSignal 取消请求

通过 AbortController 可以随时取消请求,推荐的 polyfill 实现是 abort-controller。下面的例子演示 150ms 超时取消:

import fetch, { AbortError } from 'node-fetch';

// AbortController 从 node v14.17.0 起成为全局对象
const AbortController = globalThis.AbortController || await import('abort-controller')

const controller = new AbortController();
const timeout = setTimeout(() => {
	controller.abort();
}, 150);

try {
	const response = await fetch('https://example.com', {signal: controller.signal});
	const data = await response.json();
} catch (error) {
	if (error instanceof AbortError) {
		console.log('request was aborted');
	}
} finally {
	clearTimeout(timeout);
}

源码层面的取消链路在 src/index.js:fetch 在收到 abort 事件后会 reject 一个 AbortError,销毁尚未完成的请求体流,并向响应体发出 error 事件;如果 signal 在调用前已处于 aborted 状态,则会立即中止。更多取消场景的测试用例见仓库的 test 目录。

API 参考

fetch(url[, options])

  • url:表示要请求的 URL 的字符串;
  • options:HTTP(S) 请求的 Options;
  • 返回:Promise<Response>。

url 必须是绝对 URL(如 https://example.com/)。路径相对 URL(/file/under/root)或协议相对 URL(//can-be-http-or-https.com/)会导致 Promise 被 reject。

从 src/index.js 的实现看,fetch 内部支持 data:、http:、https: 三种协议:非支持协议直接抛出 TypeError,data: URL 则通过 data-uri-to-buffer 直接构造 Response 返回,无需真实网络请求。

Options

默认值紧随各选项键后展示:

{
	// 以下属性属于 Fetch 标准
	method: 'GET',
	headers: {},            // 请求头,格式与 Headers 构造函数接受的格式一致
	body: null,             // 请求体,可以是 null 或 Node.js Readable 流
	redirect: 'follow',     // 设为 manual 可提取重定向头,设为 error 则拒绝重定向
	signal: null,           // 传入 AbortSignal 实例以可选地中止请求

	// 以下属性是 node-fetch 扩展
	follow: 20,             // 最大重定向次数,0 表示不跟随重定向
	compress: true,         // 支持 gzip/deflate 内容编码,false 则禁用
	size: 0,                // 最大响应体字节数,0 表示不限制
	agent: null,            // http(s).Agent 实例或返回实例的函数
	highWaterMark: 16384,   // 内部缓冲区最大字节数,超过则暂停从底层资源读取
	insecureHTTPParser: false	// 为 true 时使用接受非法 HTTP 头的不安全解析器
}

这些扩展选项在 src/request.js 中逐一落地:follow 默认 20、compress 默认 true、counter 默认 0、highWaterMark 默认 16384、insecureHTTPParser 默认 false,并支持从被克隆的 Request 上继承。

默认请求头

未显式设置时,node-fetch 会自动发送以下请求头:

Header Value
Accept-Encoding gzip, deflate, br (当 options.compress === true)
Accept */*
Content-Length (可计算时自动算出)
Host (目标 URI 的 host 与 port 信息)
Transfer-Encoding chunked (当 req.body 是流时)
User-Agent node-fetch

注意:当 body 是流时,Content-Length 不会被自动设置(因为流长度未知)。这些默认头在 src/request.js 的 getNodeRequestOptions 中按条件填充:Accept 缺失时补 */*、POST/PUT 且无 body 时补 Content-Length: 0、已知长度的 body 填实际字节数、User-Agent 缺失时补 node-fetch、开启 compress 且无 Accept-Encoding 时补 gzip, deflate, br。

自定义 Agent

agent 选项允许指定超出 Fetch 规范范围的网络相关配置,包括但不限于:

  • 支持自签名证书;
  • 仅使用 IPv4 或 IPv6;
  • 自定义 DNS 查询。

若未指定 agent,则使用 Node.js 默认 agent。注意 Node.js 19 起默认 agent 的 keepalive 变为 true;在更早版本中想要启用 keepalive,可以按下面代码覆盖 agent。

此外,agent 选项接受一个函数,给定当前 URL 返回 http(s).Agent 实例——这在跨 HTTP/HTTPS 协议的重定向链中非常有用:

import http from 'node:http';
import https from 'node:https';

const httpAgent = new http.Agent({
	keepAlive: true
});
const httpsAgent = new https.Agent({
	keepAlive: true
});

const options = {
	agent: function(_parsedURL) {
		if (_parsedURL.protocol == 'http:') {
			return httpAgent;
		} else {
			return httpsAgent;
		}
	}
};

函数形式的 agent 会在 src/request.js 中按当前解析后的 URL 协议动态求值。

自定义 highWaterMark

Node.js 流的内部缓冲区较小(16kB,即默认 highWaterMark),而浏览器端普遍大于 1MB 且各浏览器不一致。因此在编写 isomorphic 应用并使用 res.clone() 时,大响应在 Node 端可能挂起。

推荐做法是并行消费克隆后的响应:

import fetch from 'node-fetch';

const response = await fetch('https://example.com');
const r1 = response.clone();

const results = await Promise.all([response.json(), r1.text()]);

console.log(results[0]);
console.log(results[1]);

如果不喜欢上述方案,从 3.x 起可以显式调大 highWaterMark:

import fetch from 'node-fetch';

const response = await fetch('https://example.com', {
	// 约 1MB
	highWaterMark: 1024 * 1024
});

const result = await res.clone().arrayBuffer();
console.dir(result);

highWaterMark 会同时作用于响应体流与克隆时的两个 PassThrough 流(见 src/body.js 的 clone 实现与 src/response.js 的 Response.clone)。

不安全 HTTP 解析器(Insecure HTTP Parser)

insecureHTTPParser 选项会原样透传给 http(s).request 的同名选项,为 true 时使用接受非法 HTTP 头的宽松解析器,具体语义可查阅 Node.js http.request 文档。

手动处理重定向

node-fetch 的 redirect: 'manual' 与浏览器/规范不同:规范中会得到 opaque-redirect 过滤响应,而 node-fetch 返回的是普通的基本过滤响应,让你可以读取重定向响应的头与状态:

import fetch from 'node-fetch';

const response = await fetch('https://httpbin.org/status/301', { redirect: 'manual' });

if (response.status === 301 || response.status === 302) {
	const locationURL = new URL(response.headers.get('location'), response.url);
	const response2 = await fetch(locationURL, { redirect: 'manual' });
	console.dir(response2);
}

跟随模式的重定向逻辑在 src/index.js 中实现:error 模式直接 reject no-redirect 类型的 FetchError;follow 模式会检查 follow 上限(超限 reject max-redirect)、跨域/跨协议时移除 authorization、www-authenticate、cookie、cookie2 等敏感头、303 或 301/302 跟随 POST 时降级为 GET 并清空 body 与 content-length。

Class: Request

Request 封装 HTTP(S) 请求的 URL、方法、头与 body 信息,实现了 Body 接口。

由于 Node.js 的运行时特性,以下属性目前未实现:

  • type
  • destination
  • mode
  • credentials
  • cache
  • integrity
  • keepalive

以下 node-fetch 扩展属性已提供:

  • follow
  • compress
  • counter
  • agent
  • highWaterMark

其含义与上文 Options 中的同名扩展一致。

new Request(input[, options])

(符合规范)

  • input:URL 字符串,或另一个 Request(会被克隆);
  • options:HTTP(S) 请求的 Options。

构造器与浏览器版 Request 一致。多数场景直接 fetch(url, options) 比先建 Request 对象更简洁。

在 src/request.js 中可以看到构造器的重要约束:URL 不允许内嵌用户名/密码(会抛 TypeError);GET/HEAD 方法不允许携带 body(会抛 TypeError);signal 必须是 AbortSignal 或 EventTarget;referrer 为 '' 时等价于 'no-referrer'。

Class: Response

Response 封装 HTTP(S) 响应,实现了 Body 接口。当前未实现 trailer 属性。

new Response([body[, options]])

(符合规范)

  • body:String 或 Readable 流;
  • options:ResponseInit 选项字典。

构造器与浏览器版 Response 一致。由于 Node.js 没有 Service Worker(该类本为它设计),日常极少需要直接构造 Response。源码中 status 缺省为 200,body 非空且无 Content-Type 时会自动推导类型(见 src/response.js)。

此外 src/response.js 还提供了三个静态工厂方法:

  • Response.redirect(url, status = 302):构造携带 location 头的重定向响应,非重定向状态码会抛 RangeError;
  • Response.error():构造 type: 'error'、状态 0 的响应;
  • Response.json(data, init):将数据序列化为 JSON 并自动设置 content-type: application/json。

response.ok

(符合规范)

表示请求是否正常结束的便捷属性:状态码 ≥ 200 且 < 300 时为 true。

response.redirected

(符合规范)

表示请求是否至少被重定向过一次:内部重定向计数器大于 0 时为 true(见 src/response.js,基于 counter 实现)。

response.type

(与规范的偏差)

表示响应类型的便捷属性。node-fetch 仅支持 'default' 与 'error' 两种,不使用规范中的过滤响应机制。

Class: Headers

Headers 类用于操作和遍历一组 HTTP 头,实现了 Fetch 标准规定的全部方法。

new Headers([init])

(符合规范)

  • init:可选,预填充 Headers 对象。

init 可以是 null、另一个 Headers 对象、键值对映射对象或任意可迭代对象:

// 示例改编自 https://fetch.spec.whatwg.org/#example-headers-class
import {Headers} from 'node-fetch';

const meta = {
	'Content-Type': 'text/xml'
};
const headers = new Headers(meta);

// 以上写法等价于:
const meta = [['Content-Type', 'text/xml']];
const headers = new Headers(meta);

// 事实上任何可迭代对象都可以,比如 Map 甚至是另一个 Headers
const meta = new Map();
meta.set('Content-Type', 'text/xml');
const headers = new Headers(meta);
const copyOfHeaders = new Headers(headers);

从实现看,src/headers.js 的 Headers 继承自 URLSearchParams:构造时统一校验 header 名/值合法性、把名字小写化,并返回一个 Proxy 以保证 append/set/delete/has/getAll 都经过校验与小写化、keys() 前自动排序。raw() 与 getAll() 的组合是提取多值头(如 Set-Cookie)的官方途径。

Interface: Body

Body 是对 Request 和 Response 都适用的抽象接口,提供以下成员。

body.body

(与规范的偏差)

  • Node.js Readable 流。

数据封装在 Body 对象中。Fetch 标准要求该属性恒为 WHATWG ReadableStream,而 node-fetch 中它是 Node.js 的 Readable 流(见 src/body.js)。

body.bodyUsed

(符合规范)

  • Boolean

表示该 body 是否已被消费。按规范,已消费的 body 不能再次使用;重复调用消费方法会抛出 TypeError: body used already for: ...(见 src/body.js)。

body.arrayBuffer()

异步把整个 body 读为 ArrayBuffer。

body.formData()

解析 multipart/form-data 或 x-www-form-urlencoded 负载为 FormData。这个能力源于 Service Worker 可以在请求发送到服务器前拦截并修改消息的思想,对搭建服务端、解析消费负载的人尤其有用:

import http from 'node:http'
import { Response } from 'node-fetch'

http.createServer(async function (req, res) {
  const formData = await new Response(req, {
    headers: req.headers // 传递 boundary 值
  }).formData()
  const allFields = [...formData]

  const file = formData.get('uploaded-files')
  const arrayBuffer = await file.arrayBuffer()
  const text = await file.text()
  const whatwgReadableStream = file.stream()

  // 消费请求的其他方式:
  const json = await new Response(req).json()
  const text = await new Response(req).text()
  const arrayBuffer = await new Response(req).arrayBuffer()
  const blob = await new Response(req, {
    headers: req.headers // 让 type 继承 Content-Type
  }).blob()
})

在 src/body.js 的实现中,formData() 会根据 content-type 分流:application/x-www-form-urlencoded 走 URLSearchParams 转换,multipart 则交给 src/utils/multipart-parser.js 的解析器。

body.blob()

异步把整个 body 读为 Blob,其 type 继承响应的 Content-Type。

body.json()

异步把整个 body 解析为 JSON(text() + JSON.parse)。

body.text()

异步把整个 body 解码为 UTF-8 字符串。

另外需要注意:消费时若超出 size 限制,consumeBody 会销毁流并抛出 type: 'max-size' 的 FetchError(见 src/body.js),这是 size 选项的底层实现。

Class: FetchError

(node-fetch 扩展)

抓取过程中的操作类错误,所有非中止类操作错误都会以 FetchError 形式 reject。它继承自 FetchBaseError(见 src/errors/base.js),并带有 type 属性;当类型为 system 时,还会从 Node.js 系统错误上拷贝 code、errno 与 erroredSysCall(见 src/errors/fetch-error.js)。完整分类见 docs/ERROR-HANDLING.md。

Class: AbortError

(node-fetch 扩展)

响应 AbortSignal 的 abort 事件、请求被中止时抛出的错误,其 name 属性为 AbortError(见 src/errors/abort-error.js)。

错误处理体系

因为 window.fetch 并不透明地说明请求失败的成因,node-fetch 建立了自己的错误约定,详见 docs/ERROR-HANDLING.md,要点如下:

  • 被取消的请求以 AbortError reject,可通过检查 error.name === 'AbortError' 判断是否为中止导致的失败:
try {
	await fetch(url, {signal});
} catch (error) {
	if (error.name === 'AbortError') {
		console.log('request was aborted');
	}
}
  • 除中止外的所有操作类错误都以 FetchError reject,统一通过 try/catch 或 Promise 的 catch 处理;
  • 所有错误都带 error.message 说明成因;
  • 所有来自 node-fetch 的错误都有自定义的 err.type 标记;
  • 所有源自 Node.js 核心的错误标记为 error.type = 'system',并额外提供 error.code 与 error.errno(与 Node.js 核心抛出的错误码互为别名)供精细化处理;
  • 编程错误(programmer errors)则尽早抛出,或用带 error.message 的默认 Error reject,方便排查。

仓库维护 100% 覆盖率,完整的自定义 FetchError 类型清单与常见 Node.js 错误可见 test/main.js。

TypeScript 支持

从 3.x 起类型定义随 node-fetch 一并发布,无需再安装额外类型包。 类型文件位于 @types/index.d.ts,覆盖 fetch、Headers、Request、Response、Body、FetchError、AbortError 以及 Blob/File/FormData 等导出,并完整声明了 RequestInit 中 agent、compress、follow、size、highWaterMark、insecureHTTPParser 等 node-fetch 扩展选项。

对于旧版本,请使用 DefinitelyTyped 的类型定义:

npm install --save-dev @types/node-fetch@2.x

许可证

node-fetch 采用 MIT 许可证,全文见 LICENSE.md。

提示:本文所述用法均基于当前仓库的 3.x 代码(版本号见 package.json),data: URL 支持、敏感头跨域重定向剥离、流式 formData 解析等行为以源码实现为准;文中示例涉及的 httpbin 等外部服务仅用于演示,生产环境请替换为真实接口。

登录后查看全文
node-fetch