- 后端
- 微服务
- 云原生
【免费下载链接】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. 🌈
@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.1 | 2021-08-01 | Features | 新增 http client 组件(对应议题 #1098),组件自此诞生 |
| v2.12.4 | 2021-08-13 | Bug Fixes | 修复 POST 请求数据丢失问题(#1225) |
| v3.0.0-beta.14 | 2022-01-04 | Bug Fixes | axios 依赖升级到 ^0.24.0(#1506) |
| v3.0.0-beta.17 | 2022-01-18 | Bug Fixes | axios 依赖升级到 ^0.25.0(#1596) |
| v3.0.0-beta.3 | 2021-11-18 | Features | 增加组件与框架的配置定义(#1367) |
| v3.4.12 | 2022-08-20 | Bug Fixes | service factory 的client与clients配置合并(#2248) |
| v3.5.0 | 2022-08-29 | Bug Fixes | 修复 @midway/axios 多配置实例相关问题(#2273) |
| v3.6.0 | 2022-10-10 | Features | 框架级 Guard 能力引入(#2345) |
| v3.6.1 | 2022-10-13 | Bug 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.default | CreateAxiosDefaults | {} | 默认客户端的通用配置,会与clients.default合并 |
axios.clients | Record<string, CreateAxiosDefaults> | 无 | 具名客户端配置集合,键名为客户端名称 |
axios.clients.default | CreateAxiosDefaults | 无 | 默认客户端实例的专属配置 |
axios.tracing.enable | boolean | 未配置时视为启用 | 是否在请求拦截器中注入链路追踪上下文 |
axios.tracing.injector | function | 无 | 自定义链路载体生成函数,入参含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. 🌈
相关推荐
OpenWhispr双窗口架构解析:听写悬浮层与控制面板的设计哲学
OpenWhispr双窗口架构解析:听写悬浮层与控制面板的设计哲学 OpenWhispr 是一款隐私优先的跨平台语音转文字听写应用,支持本地模型(Nvidia
后端微服务云原生x402 Axios E2E 客户端实战:基于 @x402/axios 实现多链 HTTP 402 自动支付
x402 Axios E2E 客户端实战:基于 @x402/axios 实现多链 HTTP 402 自动支付 本文以仓库中 e2e/clients/axios/
后端金融科技区块链API设计Axios 教程:Promise 基础的 HTTP 客户端
Axios 教程:Promise 基础的 HTTP 客户端 1. 项目介绍 Axios 是一个流行的基于 Promise 的网络库,可在浏览器和 Node.js
网络后端前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考