☰
Midway @midwayjs/axios 组件演进与实战:从 HTTP 客户端组件诞生到 axios v1 的完整解析
2026/9/28 6:35:42 网站建设 项目流程
  • 后端
  • 微服务
  • 云原生

【免费下载链接】midway

🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈

项目地址:https://gitcode.com/gh_mirrors/mi/midway
点击查看免费下载

@midwayjs/axios 是 Midway 官方提供的 HTTP 客户端组件,它把 axios 的能力以 IoC 服务的形式接入 Midway 应用,让开发者通过注入HttpService即可完成请求发送,并通过统一的配置管理实现多实例、超时、鉴权头与链路追踪等能力。本文以仓库内 packages/axios/CHANGELOG.md 的变更记录为主线,结合组件源码与测试用例,梳理该组件从诞生(v2.12.1)到升级 axios v1(v3.6.1)的能力演进脉络,并给出可直接落地的配置与使用方案。读完本文,你将掌握 @midwayjs/axios 的配置模型(default与clients)、源码实现原理、兼容写法,以及如何在真实业务中注入与调用 HTTP 客户端。

一、先读懂这份 CHANGELOG:monorepo 发版机制与“版本号同步”的含义

在解读具体条目之前,需要先理解 Midway 仓库的发布方式。Midway 是一个使用 Lerna 管理、以packages/为目录组织子包的单仓库(monorepo),每次框架整体发版时,所有子包都会同步提升版本号。因此,packages/axios/CHANGELOG.md 中大量形如**Note:** Version bump only for package @midwayjs/axios的条目,含义是:

本次发版中该包本身没有代码变更,仅仅是跟随仓库整体版本号同步。

例如 3.7.0、3.6.0、3.5.3、3.5.1、3.4.13 到 3.4.1、3.4.0 及多个 3.0.0-beta 小版本,都属于此类“同步发版”。真正属于 @midwayjs/axios 组件自身的功能变更,集中在下面几条:

版本日期变更类型核心内容
v2.12.12021-08-01Features新增 http client 组件(对应议题 #1098),组件自此诞生
v2.12.42021-08-13Bug Fixes修复 POST 请求数据丢失问题(#1225)
v3.0.0-beta.142022-01-04Bug Fixesaxios 依赖升级到 ^0.24.0(#1506)
v3.0.0-beta.172022-01-18Bug Fixesaxios 依赖升级到 ^0.25.0(#1596)
v3.0.0-beta.32021-11-18Features增加组件与框架的配置定义(#1367)
v3.4.122022-08-20Bug Fixesservice factory 的client与clients配置合并(#2248)
v3.5.02022-08-29Bug Fixes修复 @midway/axios 多配置实例相关问题(#2273)
v3.6.02022-10-10Features框架级 Guard 能力引入(#2345)
v3.6.12022-10-13Bug Fixes修复 axios typings 并升级到 axios v1(#2379)

可以看到,组件的核心能力在 v2.12.1 到 v3.6.1 之间逐步成型:从“能发请求”,到“配置可定义”“多实例可合并”,再到“类型正确、基于 axios v1”。下文逐一展开。

二、组件演进时间线:从 http client 到 axios v1

1. v2.12.1:@midwayjs/axios 组件诞生

add http client component (#1098)是这份 CHANGELOG 中该组件的起点。在此之前,Midway 应用中要发起 HTTP 请求,通常需要手动创建 axios 实例并管理其生命周期;组件化之后,axios 实例由 Midway 容器统一创建、统一注入,业务代码只需声明依赖即可。

组件诞生后的核心抽象在 packages/axios/src/index.ts 中对外暴露:

  • Configuration(即AxiosConfiguration):组件的装配入口;
  • HttpServiceFactory:负责按配置创建和管理多个 axios 实例的工厂;
  • HttpService:业务侧直接注入使用的 HTTP 服务类;
  • Axios:直接导出 axios 本体,便于需要原生能力的场景使用。

2. v2.12.4:修复 POST 数据丢失

post missing data (#1225)修复的是 POST 请求体数据在传递过程中丢失的问题。对照当前实现,packages/axios/src/http-service.ts 中post方法完整透传了url、data、config三个参数:

post<T = any, R = AxiosResponse<T>, D = any>( url: string, data?: D, config?: AxiosRequestConfig<D> ): Promise<R> { return this.instance.post(url, data, config); }

该修复确保HttpService.post与原生 axios 的行为一致,data不再被遗漏。

3. v3.0.0-beta 系列:跟随 axios 依赖版本升级

在 Midway 3.0 的 beta 阶段,组件两次升级底层 axios 依赖:^0.24.0(#1506)与^0.25.0(#1596)。这类升级保证组件始终跟随上游 axios 的安全修复与 API 演进,也说明该组件对 axios 大版本的兼容动作是持续维护的。

4. v3.0.0-beta.3:组件与框架配置定义

add component and framework config definition (#1367)为组件引入了标准化的配置定义。自此,axios命名空间下的配置可以被框架正确识别、合并,并在应用启动时加载。对应实现为 packages/axios/src/config.default.ts:

export default { axios: {}, };

默认配置为空对象,实际配置由用户在应用配置中提供。

5. v3.4.12:service factory 的 client 与 clients 配置合并

service factory client & clients merge (#2248)是配置模型的关键修正:当用户在axios.default之外再通过axios.clients声明实例时,default中的通用配置(如基础 URL、公共请求头)需要与clients.default中的实例级配置合并,而不是互相覆盖。

这一行为被 packages/axios/test/factory.test.ts 中的多配置用例明确断言:default提供的Authorization头与clients.default提供的timeout: 10000、addHead头被同时保留在最终的默认实例上,而独立的test客户端则只拥有自己的配置。

6. v3.5.0:修复多配置实例问题

fix @midway/axios more configuration instance problems (#2273)继续完善多实例场景:在同时配置多个客户端时,实例的创建、获取与默认实例的选取不再产生错乱。与之配套的归一化逻辑在 packages/axios/src/http-service.factory.ts 的init中:

@Init() async init() { let axiosConfig = this.axiosConfig; if (!this.axiosConfig['clients']) { axiosConfig = { default: {}, clients: { default: this.axiosConfig, }, }; } await this.initClients(axiosConfig); }

即:只要用户没有显式声明clients,工厂就会把平铺的axios配置包装成clients.default,保证内部逻辑始终以统一的多客户端模型运行。

7. v3.6.0:框架 Guard 能力引入

add guard (#2345)属于 Midway 框架级能力(中间件/守卫体系)的引入,axios 包在本次发版中完成版本同步。对组件使用者而言,这意味着可以在框架层面统一对请求进行守卫处理,组件本身则继续专注于 HTTP 客户端的创建与管理。

8. v3.6.1:axios typings 修复与 v1 升级

fix axios typings and upgrade to v1 (#2379)是本组件最重要的一次升级:底层 axios 从 0.x 跨越到 1.x,并同步修复了类型定义。1.x 对请求/响应拦截器、AxiosHeaders等内部结构进行了重构,因此组件在 packages/axios/src/interface.ts 中重新定义了自身的AxiosRequestConfig与AxiosResponse类型,显式保证response.config.headers为AxiosRequestHeaders类型:

export interface AxiosResponse<T = any, D = any> extends OriginAxiosResponse<T, D> { config: AxiosRequestConfig<D> & { headers: AxiosRequestHeaders; }; }

当前仓库中该包的依赖为axios: 1.18.0(见 packages/axios/package.json),即组件已稳定运行在 axios v1 之上。

三、组件架构与源码级原理

理解了演进脉络后,再看组件当前的完整架构。它由四个文件协作完成,职责清晰:

1. 装配入口:namespace 为 axios 的 Configuration

packages/axios/src/configuration.ts 声明了组件的装配信息:

@Configuration({ namespace: 'axios', importConfigs: [ { default: ConfigDefault }, ], importConfigFilter: config => { /* 兼容旧版配置 */ }, }) export class AxiosConfiguration { public async onReady(container: IMidwayContainer): Promise<void> { await container.getAsync(HttpServiceFactory); } }
  • namespace: 'axios':组件配置统一挂在axios键下;
  • onReady:应用启动就绪阶段预取HttpServiceFactory单例,确保工厂提前初始化;
  • importConfigFilter:对用户配置做归一化处理(详见下文)。

2. 配置归一化:兼容旧版平铺写法

importConfigFilter中有一段“解决循环引用、兼容 older 写法”的逻辑(packages/axios/src/configuration.ts):

if (config['axios']) { if (config['axios']['clients'] || config['axios']['default']) { return config; } // 兼容 older if (!config['axios']['clients'] || !config['axios']['client']) { config['axios'] = { default: {}, clients: { default: config['axios'], }, }; } }

也就是说,如果用户直接把 axios 配置平铺在axios键下(既没有clients也没有default),过滤器会自动将其包装为default下的clients.default。这让早期版本的配置写法在新版本中依然可用,无需迁移。

3. HttpServiceFactory:多客户端管理与链路追踪注入

packages/axios/src/http-service.factory.ts 是整个组件的核心工厂,继承自框架的ServiceFactory<AxiosInstance>,单例作用域(ScopeEnum.Singleton)。其职责包括:

  • 读取axios配置并调用initClients批量创建实例;
  • 通过createClient(config, clientName)创建 axios 实例(http-service.factory.ts);
  • 在创建实例时注册请求拦截器,注入链路追踪上下文。

链路追踪部分尤为值得一提:组件支持axios.tracing.enable与axios.tracing.injector两个配置项,配合框架的MidwayTraceService,在每个请求发出前自动注入 Trace 上下文(http-service.factory.ts):

client.interceptors.request.use(requestConfig => { if (this.traceService && this.traceEnabled !== false) { const configuredCarrier = typeof this.traceInjector === 'function' ? this.traceInjector({ request: requestConfig, custom: { clientName } }) : undefined; const carrier = configuredCarrier ?? requestConfig.headers ?? new axios.AxiosHeaders(); requestConfig.headers = carrier; this.traceService.injectContext(carrier); } return requestConfig; });

默认情况下(traceEnabled不为false)请求拦截器会自动生效;traceInjector允许开发者自定义链路载体(carrier)的生成方式。

4. HttpService:面向业务的全量代理

packages/axios/src/http-service.ts 是业务代码直接注入的类,单例作用域。它在init阶段从工厂获取默认客户端实例(http-service.ts):

@Init() protected async init() { const clientName = this.serviceFactory.getDefaultClientName() || 'default'; this.instance = this.serviceFactory.get(clientName); if (!this.instance) { throw new MidwayCommonError('axios default instance not found.'); } }

随后以转发(proxy)方式暴露 axios 实例的完整 API:getUri、request、get、delete、head、options、post、put、patch,以及 axios v1 新增的postForm、putForm、patchForm,同时开放defaults与interceptors属性。类声明末尾的export interface HttpService extends AxiosInstance {}让HttpService在类型层面与AxiosInstance完全对齐。

四、配置实战:单实例与多实例

组件支持“单客户端”与“多客户端”两种配置形态,统一放在应用配置的axios键下。

1. 单客户端(最简单形态)

// src/config/config.default.ts export default { axios: { default: { baseURL: 'https://api.example.com', timeout: 5000, headers: { common: { Authorization: 'Bearer <token>', }, }, }, }, };

这是 packages/axios/test/index.test.ts 中“单配置”用例的标准写法:配置default后,注入的HttpService会基于该配置发请求。

2. 多客户端(default + clients)

当应用需要同时访问多个服务、且各服务配置差异较大时,使用clients声明多个实例:

export default { axios: { default: { baseURL: 'https://www.abc.com', headers: { common: { Authorization: 'Bearer ...', }, }, }, clients: { default: { baseURL: 'https://www.taobao.com', timeout: 10000, headers: { common: { addHead: 'xiaoqinvar' }, }, }, test: { baseURL: 'https://www.midwayjs.org', timeout: 1000, headers: { common: { Authorization: 'Bearer test...' }, }, }, }, }, };

依据 packages/axios/test/factory.test.ts 的断言,此配置下:

  • 默认实例(clients.default)会合并顶层default的Authorization头与自身timeout、addHead配置;
  • test实例只拥有自己的baseURL、timeout、Authorization,不会继承default中的addHead。

3. 旧版平铺写法(自动兼容)

如果沿用早期版本直接把配置平铺在axios键下:

export default { axios: { baseURL: 'https://api.example.com', timeout: 5000, }, };

importConfigFilter会自动将其归一化为clients.default,组件仍可正常工作。

4. 配置参数速查表

配置键类型默认值说明
axios.defaultCreateAxiosDefaults{}默认客户端的通用配置,会与clients.default合并
axios.clientsRecord<string, CreateAxiosDefaults>无具名客户端配置集合,键名为客户端名称
axios.clients.defaultCreateAxiosDefaults无默认客户端实例的专属配置
axios.tracing.enableboolean未配置时视为启用是否在请求拦截器中注入链路追踪上下文
axios.tracing.injectorfunction无自定义链路载体生成函数,入参含request与custom.clientName

其中baseURL、timeout、headers、代理等字段与原生 axios 的CreateAxiosDefaults完全一致,可直接沿用 axios 的全部配置能力。

五、业务代码中的使用方式

1. 注入并使用默认客户端

import { Controller, Get, Inject } from '@midwayjs/core'; import { HttpService } from '@midwayjs/axios'; @Controller('/') export class HomeController { @Inject() httpService: HttpService; @Get('/orgs') async orgs() { const result = await this.httpService.get( 'https://api.github.com/users/octocat/orgs' ); return result.data; } }

HttpService为单例,但通过@Inject()注入后,在请求作用域(Request Scope)中取到的仍然是同一个单例实例(packages/axios/test/index.test.ts 验证了应用上下文与请求上下文获取结果一致),因此可以安全地在控制器、服务、组件中共享。

2. 按需获取具名客户端

如果业务需要直接使用工厂获取指定客户端,可以注入HttpServiceFactory:

import { Inject, Provide } from '@midwayjs/core'; import { HttpServiceFactory } from '@midwayjs/axios'; @Provide() export class UserService { @Inject() serviceFactory: HttpServiceFactory; async callTestClient() { const client = this.serviceFactory.get('test'); return client.get('https://www.midwayjs.org/...'); } }

3. 类型能力

组件在 packages/axios/src/interface.ts 中重新导出AxiosRequestConfig与AxiosResponse,并修正了响应对象中config.headers的类型,配合HttpService extends AxiosInstance的接口声明,开发者可以获得与原生 axios 一致的泛型推导体验:

const resp = await this.httpService.get<OrgItem[]>('/users/octocat/orgs'); // resp.data 的类型为 OrgItem[]

六、测试如何验证组件行为

仓库中的测试直接印证了 CHANGELOG 中各项修复的实际效果,可作为回归验证的参考:

  • packages/axios/test/factory.test.ts:验证工厂单例与多配置合并行为,包括default与clients的合并断言,以及使用错误的client字段会抛出异常;
  • packages/axios/test/index.test.ts:验证HttpService的单例语义、全部代理方法(getUri、request、get、post、postForm等 12 个方法)、defaults/interceptors透传,以及基于 nock 的真实请求收发。

两份测试均使用createLightApp加载组件(imports: [require(join(__dirname, '../src'))]),并通过globalConfig注入测试配置,展示了组件在轻量应用(Light App)中的接入方式。

七、版本与依赖约束

  • 当前仓库中 packages/axios/package.json 声明组件版本为4.2.3,依赖axios: 1.18.0,运行环境要求node >= 20;
  • CHANGELOG 记录的时间线止于 2022-10-29 的 v3.7.0,后续发版以“版本同步”为主,组件自身的破坏性变更集中在 axios v1 升级(v3.6.1)之前;
  • 组件入口为dist/index.js(构建产物),源码位于src/目录,外部使用只需依赖@midwayjs/axios一个包即可,无需直接依赖 axios(但如需使用Axios导出或自定义拦截器类型,可保持类型对齐)。

从 v2.12.1 的“新增 http client 组件”到 v3.6.1 的“升级 axios v1”,@midwayjs/axios 用不到一年的时间完成了从 0 到生产可用的演进:统一的配置模型、多实例工厂、自动的链路追踪注入、与 axios 1.x 对齐的类型系统,以及严格的测试保障。理解这份 CHANGELOG 与源码的对应关系,既能帮助你在升级版本时评估影响面,也能让你在业务中把组件的多客户端与追踪能力用到极致。

  • 后端
  • 微服务
  • 云原生

【免费下载链接】midway

🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈

项目地址:https://gitcode.com/gh_mirrors/mi/midway
点击查看免费下载
上一篇:OpenMed 临床保真隐私处理指南:基于 ClinicalPrivacyProcessor 的批量化去标识化与临床内容保留
下一篇:Graph of Thoughts部署最佳实践:生产环境配置与优化

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询