发布时间:2026/9/4 11:52:17
axios 适配器(Adapters)深度解析:内置适配器的选择机制与自定义适配器实战 axios 适配器Adapters深度解析内置适配器的选择机制与自定义适配器实战【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios适配器Adapter是 axios 请求链路中真正“发出请求”的最后一环它决定了请求在浏览器、Node.js 或边缘运行环境如 Cloudflare Workers、Deno中分别走xhr、http还是fetch通道。本文基于 axios 官方文档docs/es/pages/advanced/adapters.md并结合源码实现讲清三件事内置适配器如何按优先级被自动选择、adapter配置项的所有取值形式以及编写自定义适配器的完整规范——读完你可以为测试、自定义传输层或非标准环境写出一个受 axios 完整生命周期约束的适配器。一、适配器是什么请求管线中的“执行者”axios 将一次请求的生命周期拆分为若干阶段配置合并、请求拦截器、请求转换器transformRequest、适配器发送请求、响应转换器transformResponse、响应拦截器。其中只有适配器阶段与具体运行环境绑定因此 axios 用“可插拔适配器”来屏蔽 XHR、Nodehttp/https、fetch之间的差异。默认情况下axios 使用一个有序优先级列表[xhr, http, fetch]并选择第一个当前环境支持的适配器。这一默认值直接定义在 lib/defaults/index.js#L41adapter: [xhr, http, fetch],实际效果是浏览器中选中xhrXMLHttpRequestNode.js 中选中http底层http/https模块见 lib/adapters/http.js两者都不可用的环境Cloudflare Workers、Deno 等落到fetch。写自定义适配器可以让你完全掌控 axios 如何发起请求、如何处理响应——官方文档给出的典型场景是测试、自定义传输协议、非标准运行环境。二、内置适配器按名称或数组指定2.1 用adapter配置项按名称选择可以通过adapter配置项以名称方式显式选用内置适配器// 使用 fetch 适配器 const instance axios.create({ adapter: fetch }); // 使用 XHR 适配器浏览器默认 const instance axios.create({ adapter: xhr }); // 使用 HTTP 适配器Node.js 默认 const instance axios.create({ adapter: http });三个名称对应的实现分别位于lib/adapters/xhr.js —— 浏览器 XHR 实现lib/adapters/http.js —— Node.js HTTP/HTTPS含 HTTP/2实现lib/adapters/fetch.js —— 基于 Fetch API 的通用实现是 axios 面向现代运行环境的推荐通道。关于fetch适配器的更多细节可参考 docs/pages/advanced/fetch-adapter.md。2.2 传一个适配器名称数组adapter也可以接收名称数组axios 会按顺序取第一个当前环境支持的适配器const instance axios.create({ adapter: [fetch, xhr, http] });这一行为背后的解析逻辑见下一节的源码分析。2.3 传一个函数直接注入自定义适配器除了名称adapter还可以直接是一个函数自定义适配器这是 lib/core/dispatchRequest.js 中解析入口所支持的核心形式const adapter adapters.getAdapter(config.adapter || defaults.adapter, config);见 lib/core/dispatchRequest.js#L52。也就是说请求真正发出前axios 先经过getAdapter把“名称 / 名称数组 / 函数”统一解析成一个可调用的适配器函数。三、源码剖析getAdapter的选择流程与报错语义内置适配器的注册表和解析函数都在 lib/adapters/adapters.js。注册表只有三个键// lib/adapters/adapters.js#L16-L22 const knownAdapters { http: httpAdapter, xhr: xhrAdapter, fetch: { get: fetchAdapter.getFetch, }, };注意一个容易忽略的细节fetch注册的不是一个函数而是{ get: fetchAdapter.getFetch }这样一个带get方法的对象。这为环境探测留了口子——getFetch(config)在运行时检查当前环境是否真的提供可用的fetchlib/adapters/fetch.js#L97-L103typeof fetch function不满足时直接返回false。因此在某些极老的浏览器构建中fetch会被判定为“环境不支持”而自动跳过请求落到xhr上。getAdapter(adapters, config)的完整流程lib/adapters/adapters.js#L65-L115可以概括为归一化把字符串包成单元素数组逐项遍历函数直通若项本身是函数或null/false视为“已解析句柄”函数直接可用名称查找字符串按String(nameOrAdapter).toLowerCase()小写后查knownAdapters查不到立即抛Unknown adapter name环境可用性判定对带get方法的项如fetch调用adapter.get(config)返回false表示“当前环境不支持”继续尝试下一项全部失败收集每一项的拒绝原因并抛出AxiosError(ERR_NOT_SUPPORT)错误信息区分两种情况值为false→ “is not supported by the environment”环境不支持值为null→ “is not available in the build”构建中未包含例如某些精简构建裁剪了该适配器。这个“可用 / 支持”的二分语义有对应的测试用例验证见 tests/unit/adapters/adapters.test.js其中should detect adapter unavailable status断言null项抛出is not available in the buildshould detect adapter unsupported status断言false项抛出is not supported by the environment且code AxiosError.ERR_NOT_SUPPORTshould pick suitable adapter from the list则验证了数组按顺序跳过不可用项、选中第一个可用适配器的行为。从源码结构看adapters.adapters即knownAdapters是模块导出的可变对象理论上可以在运行时向其中注册新的适配器名称测试中正是这样做的adapters.adapters.testadapter adapter之后getAdapter(testAdapter)即可命中。这是 axios 面向测试与扩展预留的扩展点但官方推荐的做法仍是通过adapter配置项传函数。四、自定义适配器接口契约与完整示例4.1 契约入参与出参自定义适配器是一个接受config对象、返回 Promise 的函数Promise 必须 resolve 为一个合法的 axios 响应对象。官方示例完整保留自文档如下import axios from axios; import { settle } from axios/unsafe/core/settle.js; function myAdapter(config) { /** * 到达适配器时 * - config 已与默认值合并完毕 * - 请求转换器transformRequest已执行 * - 请求拦截器已执行 * * 此时适配器负责真正发起请求 * 并返回合法的响应对象。 */ return new Promise((resolve, reject) { // 在这里实现你的自定义请求逻辑。 // 本示例以原生 fetch API 为起点。 fetch(config.url, { method: config.method?.toUpperCase() ?? GET, headers: config.headers?.toJSON() ?? {}, body: config.data, signal: config.signal, }) .then(async (fetchResponse) { const responseData await fetchResponse.text(); const response { data: responseData, status: fetchResponse.status, statusText: fetchResponse.statusText, headers: Object.fromEntries(fetchResponse.headers.entries()), config, request: null, }; // settle 依据 HTTP 状态码决定 resolve 或 reject settle(resolve, reject, response); /** * settle 之后 * - 响应转换器transformResponse将执行 * - 响应拦截器将执行 */ }) .catch(reject); }); } const instance axios.create({ adapter: myAdapter });示例中有几处细节值得展开config.headers?.toJSON()到达适配器时 headers 已是AxiosHeaders实例在 lib/core/dispatchRequest.js#L43 中经AxiosHeaders.from(...)转换传给fetch前需要转成普通对象signal: config.signal把 axios 的取消信号透传给底层传输是自定义适配器支持AbortController取消的关键响应对象必须携带config与request字段settle抛错时会把response.config、response.request挂到AxiosError上见下文缺失会导致错误对象信息不全。request在自定义通道中拿不到原生请求句柄时置null即可响应对象必须包含status与headersdispatchRequest在适配器 resolve 之后还会执行transformResponse并把response.headers重新包装成AxiosHeaderslib/core/dispatchRequest.js#L54-L71所以适配器只负责产出“原始”响应后处理统一交给框架。4.2 为什么必须settle状态码判定归属文档明确提示settle辅助函数对 2xx 状态码 resolve Promise对其余状态码 reject与 axios 的默认行为一致如需自定义状态判定应使用validateStatus配置项。settle的实现只有 10 余行lib/core/settle.js#L14-L26export default function settle(resolve, reject, response) { const validateStatus response.config.validateStatus; if (!response.status || !validateStatus || validateStatus(response.status)) { resolve(response); } else { reject(new AxiosError( Request failed with status code response.status, response.status 400 response.status 500 ? AxiosError.ERR_BAD_REQUEST : AxiosError.ERR_BAD_RESPONSE, response.config, response.request, response )); } }两个关键点它不硬编码 2xx而是读取response.config.validateStatus做判定——由于config已合并默认值默认行为即“2xx 通过”默认validateStatus定义于 lib/defaults/index.js#L161reject 时构造的AxiosError会附带config、request、response错误码在 4xx 与 5xx 之间区分ERR_BAD_REQUEST/ERR_BAD_RESPONSE这正是axios.isAxiosError判断和拦截器中error.response的来源。所以自定义适配器的正确姿势是自己实现传输逻辑但把“状态码 → resolve/reject”的判定权交还给settle或等价地自己调用validateStatus。这样无论用户把validateStatus改成接受 3xx、401 等你的适配器都会自动跟随不会破坏 axios 的错误语义。4.3 请求生命周期中的精确位置文档示例中的注释说明了适配器被调用时的时机。结合 lib/core/dispatchRequest.js 可以确认完整顺序拦截器请求→ dispatchRequest 1. throwIfCancellationRequested(config) // 先检查取消 2. config.headers AxiosHeaders.from(...) // 头部归一化 3. config.data transformData(...transformRequest) // 请求转换 4. adapter adapters.getAdapter(config.adapter || defaults.adapter, config) 5. adapter(config) // ← 你的自定义适配器在这里执行 ↓ resolve 6. throwIfCancellationRequested(config) // 响应前再次检查取消 7. response.data transformData(...transformResponse) // 响应转换 8. response.headers AxiosHeaders.from(...)即写适配器时不需要也不应该重复处理数据序列化、头部归一化、取消检查等框架职责只专注于“把config变成一次真实网络交互并回传原始响应”。五、TypeScript 适配器用泛型保留请求体与查询参数类型TypeScript 场景下自定义适配器可以通过泛型把“请求体类型”和“查询参数类型”一并保留到响应配置上避免any扩散。文档给出的示例完整保留如下import type { AxiosPromise, InternalAxiosRequestConfig, } from axios; interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; } interface SearchResponse { results: string[]; } const searchAdapter ( config: InternalAxiosRequestConfigRequestBody, SearchParams ): AxiosPromiseSearchResponse, RequestBody, SearchParams Promise.resolve({ data: { results: [] }, status: 200, statusText: OK, headers: {}, config, });要点在于三个泛型的对应关系InternalAxiosRequestConfigD, P的D、P分别声明请求体与查询参数类型AxiosPromiseD, R, P中第三个参数R请求类型与第二个参数D请求体必须与入参保持一致这样.then(res res.config)处的类型推断不会丢失信息响应data的类型由SearchResponse约束。该适配器本身返回一个立即 resolve 的 Promise说明适配器并不要求真实发起网络请求——这也是它天然适合做测试桩stub的原因单测中注入一个返回固定数据的函数适配器即可完全绕开网络层验证业务逻辑。六、速查小结场景写法依据完全交给 axios 自动选择不传adapter默认[xhr, http, fetch]lib/defaults/index.js#L41指定单个内置适配器axios.create({ adapter: fetch })等docs/es/pages/advanced/adapters.md指定候选顺序adapter: [fetch, xhr, http]getAdapter顺序遍历lib/adapters/adapters.js#L65注入自定义函数适配器adapter: myAdapterisResolvedHandle函数直通名称写错抛Unknown adapter namelib/adapters/adapters.js#L83-L85全部不可用抛ERR_NOT_SUPPORT信息区分 unavailable/unsupportedtests/unit/adapters/adapters.test.js自定义适配器状态码判定调用settle(resolve, reject, response)或自行读validateStatuslib/core/settle.js一句话总结内置适配器解决“多环境一致性”自定义适配器解决“传输层可控性”而无论哪条路径getAdapter → adapter(config) → settle → transformResponse → 响应拦截器这条管线保证了自定义通道与内置通道享有完全相同的请求语义、错误语义和取消语义。【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

2026/9/4 11:52:17

“扛着电动车翻窗”:沙盒游戏交互规则与AI感知的边界拆解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/4 11:52:17

连点器自动化工具:从原理到实战的鼠标键盘模拟指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/4 18:58:16

基于Python的视频物体检测:从理论到实践的全流程指南

一、视频物体检测的技术基础与核心挑战视频物体检测的 core 使命在于, 于相继的帧里, 辨认并且确定目标物体的位置, 它的技术困难程度明显地比静态图像检测要高, 主要面临的挑战涵盖着:对于实时性方面有的要求, 视频帧率一般处在25 - 30fps这个范围, 并且要求检测算法在毫秒级别…

2026/9/4 18:58:16

专业的东莞家电玻璃定制生产厂家

东莞聚集了大量玻璃定制生产企业,行业品类丰富、选择多样。其中,广东鸿盛玻璃科技有限公司(以下简称“鸿盛”)凭借完善的产品体系、标准化品质管控、成熟的深加工工艺和多年行业积淀,在家电玻璃定制赛道形成了自身鲜明…

2026/9/4 18:58:16

[附文稿]产品经理的终结?为什么领英正在将产品经理转变为由人工智能驱动的“全栈构建者”

本次讨论由领英的高管托默主导并重点介绍了人工智能时代下工作技能的剧烈变化, 以及公司为应对挑战而推行的组织重塑策略, 托默指出到2030年完成现有工作所需技能预计将发生70%的变化, 这一变化速度超过组织应对的速度, 这要求企业必须重构产品构建流程以提高迭代能力来保持竞争…

2026/9/4 18:58:16

Grok Build 开源:基于技能架构的 AI 编码代理实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/4 18:53:16

芯片内部去耦技术:解决高速电路EMC问题的核心原理与实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/3 18:28:26

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出,第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器,出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台,直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/9/3 14:29:47

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流:为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历:明明传感器本身性能很好,信号输出却一塌糊涂——噪声大、漂移明显、重复性差,怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/9/3 14:30:35

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起,其实就是嵌入式开发里最常遇到的一类需求:用一块不算贵的 MCU,同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控,主频…

2026/9/4 0:00:58

STM32H743 SPI从机DMA双缓冲通信实战

简介:本资源是面向嵌入式开发工程师与STM32进阶学习者的SPI DMA双机通信从机端完整实现方案,聚焦STM32H743高性能Cortex-M7单片机在工业控制与高速数据交互场景下的从机通信开发痛点。压缩包含1355个文件,主体为599个C源码与321个头文件&…

2026/9/4 0:00:58

CPU开盖降温教程:20元成本让温度直降30度的原理与实践

最近很多朋友都在抱怨,自己的电脑一到夏天就变成"烤箱",玩游戏时CPU温度动不动就飙到90度以上,风扇噪音堪比直升机。更让人头疼的是,明明配置不错,却因为高温降频导致性能大打折扣。如果你也遇到了类似问题&…

2026/9/4 0:00:58

ArkTS 表单工程:场地预约页的三态场次 Grid 与校验

ArkTS 表单工程:场地预约页的三态场次 Grid 与校验 App 14「运动场地预约」场地 Tab(Func1Tab),是整 App 交互最丰富的页面——场地横向切换 三色图例 渐变预约预览卡 快捷模板 今日场次 Grid(可选/已选/已满三态&…

2026/9/3 20:43:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/3 17:51:43

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/3 21:06:57

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…