web3.js 的 HTTP 传输层进化论:从变更日志到源码,读懂 web3-providers-http 的每一次关键升级
【免费下载链接】web3.jsCollection of comprehensive TypeScript libraries for Interaction with the Ethereum JSON RPC API and utility functions.项目地址: https://gitcode.com/gh_mirrors/we/web3.js
本篇文章以 packages/web3-providers-http/CHANGELOG.md 为核心骨架,逐条还原web3-providers-http从 4.0 系列 alpha 到 4.2.0 的演进脉络,并深入其源码(src/index.ts)、类型定义(src/types.ts)、打包配置(package.json)与单元测试(test/unit),说明每次版本变更背后的工程动因与对使用者的实际影响。读完你将掌握HttpProvider的完整实现原理、配置方式、测试方法,以及升级到 4.1.0 / 4.2.0 时值得注意的行为变化。
一、包定位与变更日志规范
web3-providers-http是 web3.js 4.x 的官方 HTTP 协议 Provider 子包,负责将 Web3 JSON-RPC 请求通过 HTTP POST 发送到以太坊节点,并解析 JSON-RPC 响应。它基于Web3BaseProvider基类实现,是 web3.js 与 HTTP 节点交互的默认传输通道——在 web3-core/src/web3_request_manager.ts 中,HttpProvider被直接引入并作为默认 Provider 使用;同时它也通过 packages/web3/src/index.ts 的export { HttpProvider }与 packages/web3/src/providers.exports.ts 的export * as http暴露给顶层 web3 包使用者。
该 CHANGELOG 遵循 Keep a Changelog 规范(Added/Changed/Deprecated/Removed/Fixed/Security分类),并遵循语义化版本控制(Semantic Versioning):4.0.x 为 4.0 正式版前的 alpha/rc 迭代,4.1.0 与 4.2.0 为向后兼容的次版本功能增强,文件末尾保留了[Unreleased]区块用于登记尚未发布的变更。
二、4.0 系列:从 alpha 到正式发布的工程化打磨
CHANGELOG 中 4.0.1 之前的五次预发布(alpha.2 ~ alpha.5)内容全部集中在依赖升级与构建产物调整上,其中4.0.1-alpha.4是一次影响所有使用者的结构变更:
mainandfilesentries inpackage.jsonchanged tolib/directory fromdist/(#5739)
即包的发布产物目录由dist/调整为lib/。对照当前 package.json 可以验证这一变更的最终形态:
"main": "./lib/commonjs/index.js", "module": "./lib/esm/index.js", "exports": { ".": { "types": "./lib/types/index.d.ts", "import": "./lib/esm/index.js", "require": "./lib/commonjs/index.js" } }, "files": ["lib/**/*", "src/**/*"]这里的exports字段为 Node.js 的「条件导出」,让 ESM(import)与 CommonJS(require)使用者各自获得对应产物,TypeScript 类型则统一指向lib/types/index.d.ts。
4.0.1-rc.0的关键变更是「为HttpProvider添加了命名导出(named export)」(#5771)。在 src/index.ts 文件末尾可以看到export { HttpProvider };与export { HttpProviderOptions },这意味着除了默认导出外,还支持:
import { HttpProvider } from 'web3-providers-http'; import HttpProviderDefault from 'web3-providers-http'; // 默认导出同样可用4.0.1-rc.1则引入两项与源码可分发性直接相关的变更:
- Added source files (#5956):
package.json的files字段包含"src/**/*",将 TypeScript 源码一并发布到 npm,便于使用者调试与查看实现; - Added hybrid build (ESM and CJS) of library (#5904):构建脚本(见
package.json的scripts)采用三路并行编译:
"build:cjs": "tsc --build tsconfig.cjs.json && echo '{\"type\": \"commonjs\"}' > ./lib/commonjs/package.json", "build:esm": "tsc --build tsconfig.esm.json && echo '{\"type\": \"module\"}' > ./lib/esm/package.json", "build:types": "tsc --build tsconfig.types.json"注意 CJS 与 ESM 产物各自通过向子目录写入{"type": "commonjs"}/{"type": "module"}声明模块格式,避免 Node.js 对.js扩展名的格式歧义——这是 npm 包混合构建的经典做法。配置文件可参见 tsconfig.cjs.json、tsconfig.esm.json 与 tsconfig.types.json。此外engines字段声明了运行环境下限:node >= 14、npm >= 6.12.0。
三、4.1.0:升级 cross-fetch v4,解锁 Service Worker 环境
4.1.0 是一次值得重点关注的行为变更:
- Bump cross-fetch to version 4 (#6463).
- Fix issue lquixada/cross-fetch#78, enabling to run web3.js in service worker (#6463)
HttpProvider的底层网络层完全依赖cross-fetch这个同构 fetch 实现,这一点在 src/index.ts 顶部即可确认:import fetch from 'cross-fetch';。旧版 cross-fetch 在 Service Worker 环境中会因依赖XMLHttpRequest而报错,导致 web3.js 无法在 Service Worker 中运行;升级到 v4 后,请求路径改为原生fetch实现,从而修复了该问题。
当前 package.json 中"cross-fetch": "^4.0.0"印证了这一升级的落地。对使用者的实际意义是:4.1.0 及以上版本可以在 Service Worker、浏览器扩展等受限网络环境中使用HttpProvider;若你仍在使用 4.0.x,则需注意此类环境下的兼容性限制。
四、4.2.0:ResponseError 携带 HTTP statusCode
4.2.0 引入的是错误信息的增强:
Added
statusCodeof response in ResponseError,statusCodeis optional property in ResponseError.
ResponseError定义在 packages/web3-errors/src/errors/response_errors.ts,核心结构如下:
export class ResponseError<ErrorType = unknown, RequestType = unknown> extends BaseWeb3Error { public code = ERR_RESPONSE; public data?: ErrorType | ErrorType[]; public request?: JsonRpcPayload<RequestType>; public statusCode?: number; // 4.2.0 新增的可选属性 public constructor( response: JsonRpcResponse<unknown, ErrorType>, message?: string, request?: JsonRpcPayload<RequestType>, statusCode?: number, ) { /* ... */ } }statusCode被声明为可选属性(statusCode?: number),通过构造函数第四个参数传入,并同步出现在toJSON()的序列化结果中(便于日志与错误上报)。而真正把 HTTP 状态码灌入异常的地方,正是 src/index.ts 的request()方法:
if (!response.ok) { throw new ResponseError(await response.json(), undefined, undefined, response.status); }当节点返回非 2xx 状态时(如 400、429、500),HTTP 状态码会作为第四个参数传入ResponseError。因此从 4.2.0 起,开发者可以在 catch 中直接区分限流(429)、参数错误(400)与节点故障(500)等不同场景,例如:
import { ResponseError } from 'web3-errors'; try { await web3.eth.getBalance('0x407d73d8a49eeb85d32cf465507dd71d507100c1'); } catch (err) { if (err instanceof ResponseError && err.statusCode === 429) { // 触发速率限制,可在此实现退避重试 } }五、HttpProvider 核心实现解析
HttpProvider的完整实现只有百余行(src/index.ts),但承载了 web3.js 与 HTTP 节点交互的全部逻辑,值得逐段拆解。
5.1 构造与 URL 校验
export default class HttpProvider< API extends Web3APISpec = EthExecutionAPI, > extends Web3BaseProvider<API> { private readonly clientUrl: string; private readonly httpProviderOptions: HttpProviderOptions | undefined; public constructor(clientUrl: string, httpProviderOptions?: HttpProviderOptions) { super(); if (!HttpProvider.validateClientUrl(clientUrl)) throw new InvalidClientError(clientUrl); this.clientUrl = clientUrl; this.httpProviderOptions = httpProviderOptions; } private static validateClientUrl(clientUrl: string): boolean { return typeof clientUrl === 'string' ? /^http(s)?:\/\//i.test(clientUrl) : false; } }URL 校验使用正则^http(s)?:\/\//i,只接受http://或https://开头的字符串。测试用例 test/unit/constructor.test.ts 与 test/fixtures/test_data.ts 给出了完整边界:
- 合法:
http://localhost:8545、http://www.localhost、https://foo.com、http://foo.com:8545等; - 非法:
htt://localhost:8545、http//localhost:8545、ws://localhost:8545、空字符串、null、undefined、数字42等,均会抛出InvalidClientError,错误消息形如`Client URL "${invalidClient}" is invalid.`。
注意ws://属于 WebSocket 协议,必须使用web3-providers-ws包,而非本包。
5.2 请求处理流程:request()
public async request< Method extends Web3APIMethod<API>, ResultType = Web3APIReturnType<API, Method>, >( payload: Web3APIPayload<API, Method>, requestOptions?: RequestInit, ): Promise<JsonRpcResponseWithResult<ResultType>> { const providerOptionsCombined = { ...this.httpProviderOptions?.providerOptions, ...requestOptions, }; const response = await fetch(this.clientUrl, { ...providerOptionsCombined, method: 'POST', headers: { ...providerOptionsCombined.headers, 'Content-Type': 'application/json', }, body: JSON.stringify(payload), }); if (!response.ok) { throw new ResponseError(await response.json(), undefined, undefined, response.status); } return (await response.json()) as JsonRpcResponseWithResult<ResultType>; }请求流程的关键细节:
- 配置合并优先级:实例化时传入的
httpProviderOptions.providerOptions与单次调用的requestOptions做浅合并,后者覆盖前者; - 强制 POST + JSON:无论传入的 options 如何,
method一律覆盖为POST,headers中确保存在Content-Type: application/json,body为JSON.stringify(payload)(即标准 JSON-RPC 载荷); - 非 2xx 抛错:
!response.ok时解析响应体并抛出携带statusCode的ResponseError; - 成功响应直接反序列化:返回
JsonRpcResponseWithResult<ResultType>。
5.3 明确不支持的能力:HTTP 无订阅、无连接生命周期
HttpProvider对事件与连接类方法一律抛MethodNotImplementedError,因为 HTTP 是无状态请求-响应协议,无法承载订阅或持久连接:
supportsSubscriptions()返回false——这是与WebSocketProvider最本质的区别,合约事件订阅(web3.eth.subscribe)必须使用 WS 或 IPC Provider;getStatus()、on()、removeListener()、once()、removeAllListeners()、connect()、disconnect()、reset()、reconnect()全部抛出MethodNotImplementedError(见 src/index.ts)。
test/unit/not_implemented_methods.test.ts 对上述每个方法逐一断言了抛错行为,这可以视为该包的「能力边界契约」:HTTP Provider 只承诺请求-响应式 RPC 调用。
六、安装与实战配置指南
6.1 安装
npm install web3-providers-http # 或 yarn add web3-providers-http详见 README.md。该包是 web3.js 的子包,也可通过顶层 web3 包间接使用(import { HttpProvider } from 'web3')。
6.2 实例化与 providerOptions
HttpProvider构造签名为new HttpProvider(clientUrl, httpProviderOptions?),第二参数类型HttpProviderOptions定义于 src/types.ts:
export interface HttpProviderOptions { providerOptions: RequestInit; }providerOptions本质是标准RequestInit(fetch 的选项对象)。测试夹具 test/fixtures/test_data.ts 给出了一个非常完整的配置示例,覆盖了绝大多数可用字段:
export const httpProviderOptions = { providerOptions: { body: undefined, cache: 'force-cache', // 强制使用缓存 credentials: 'same-origin', // 同源请求携带凭据 headers: { 'Content-Type': 'application/json' }, integrity: 'foo', keepalive: true, // 允许长连接存活 method: 'GET', // 注意:实际请求中会被强制覆盖为 POST mode: 'same-origin', redirect: 'error', referrer: 'foo', referrerPolicy: 'same-origin', signal: undefined, // 可传入 AbortSignal 实现请求取消 window: undefined, } as RequestInit, };实战中常用的配置组合包括:
import HttpProvider from 'web3-providers-http'; const provider = new HttpProvider('https://mainnet.infura.io/v3/YOUR_KEY', { providerOptions: { headers: { 'Content-Type': 'application/json', Authorization: 'Bearer YOUR_AUTH_TOKEN', // 私有节点鉴权 }, signal: AbortSignal.timeout(10_000), // 10 秒超时 }, });6.3 与 Web3 实例集成
由于 web3-core/src/web3_request_manager.ts 默认基于HttpProvider构建请求管理器,HttpProvider既可作为独立 Provider 使用,也可直接注入 web3 实例:
import { Web3 } from 'web3'; import HttpProvider from 'web3-providers-http'; const provider = new HttpProvider('http://localhost:8545'); const web3 = new Web3(provider); // 直接发起 JSON-RPC 调用 const balance = await provider.request({ jsonrpc: '2.0', id: 42, method: 'eth_getBalance', params: ['0x407d73d8a49eeb85d32cf465507dd71d507100c1', 'latest'], });七、单元测试如何验证这些行为
该包的测试体系为理解其行为契约提供了直接证据:
- test/unit/implemented_methods.test.ts:通过
jest-fetch-mock将cross-fetch替换为 mock(jest.setMock('cross-fetch', fetchMock)),验证supportsSubscriptions()返回false、正常响应按原样返回、HTTP 400 时request()reject 且抛出ResponseError; - test/unit/constructor.test.ts:验证构造时可用
providerOptions、合法/非法 URL 的通过/拒绝行为; - test/unit/not_implemented_methods.test.ts:逐一验证未实现方法抛
MethodNotImplementedError。
运行方式(见 package.json 的scripts):
yarn test:unit # 单元测试 yarn test:integration # 集成测试 yarn test:e2e:chrome # 基于 Cypress 的浏览器端 e2e八、升级到 4.2.0 的检查清单
结合 CHANGELOG 与源码,从 4.0.x 升级到 4.2.0 时建议确认以下几点:
- 构建产物路径:4.0.1-alpha.4 起产物目录由
dist/变为lib/,若你直接引用包内文件路径需同步更新; - 导入方式:4.0.1-rc.0 起支持
import { HttpProvider }命名导入,可摆脱仅默认导出的写法; - Service Worker 支持:需要 SW 环境运行 web3.js 时,请使用 ≥ 4.1.0(cross-fetch v4);
- 错误处理:≥ 4.2.0 时
ResponseError.statusCode可区分 HTTP 层错误(4xx/5xx),建议在 catch 逻辑中补充状态码分支; - 能力边界:HTTP Provider 不支持订阅与连接生命周期管理,需要事件订阅时切换到 WS/IPC Provider。
结语
web3-providers-http的变更日志记录了一个成熟 Provider 子包的工程化演进:从构建产物结构调整(dist/→lib/)、命名导出与混合构建,到网络层升级(cross-fetch v4)与错误信息增强(statusCode)。透过 CHANGELOG 对照 src/index.ts、src/types.ts 与测试用例,可以完整理解其「POST + JSON + 无订阅」的简洁设计,以及它在 web3.js 传输层中所承担的确定性角色。
【免费下载链接】web3.jsCollection of comprehensive TypeScript libraries for Interaction with the Ethereum JSON RPC API and utility functions.项目地址: https://gitcode.com/gh_mirrors/we/web3.js
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考