news 2026/9/6 20:20:27

axios 拦截器(Interceptors)指南:请求/响应拦截、执行顺序与同步模式的源码级解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
axios 拦截器(Interceptors)指南:请求/响应拦截、执行顺序与同步模式的源码级解析

axios 拦截器(Interceptors)指南:请求/响应拦截、执行顺序与同步模式的源码级解析

【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios

拦截器是 axios 中用于拦截并修改 HTTP 请求与响应的核心机制,作用类似 Express.js 的中间件:请求拦截器在请求发出前运行,响应拦截器在响应返回后(或出错时)运行,常用于日志记录、修改请求头、统一处理响应数据与错误。读完本文,你将掌握拦截器的注册、移除、synchronousrunWhen两个高级选项的用法,以及请求/响应拦截器"相反的"执行顺序(LIFO 与 FIFO),并能从 axios 源码层面理解拦截链是如何构建和执行的。

一、基本用法:注册请求与响应拦截器

axios 实例提供interceptors.requestinterceptors.response两个入口,均通过use(fulfilled, rejected, options)注册拦截器:

// 注册请求拦截器 axios.interceptors.request.use( function (config) { // 请求发出前对 config 做任何处理 return config; }, function (error) { // 处理请求阶段的错误 return Promise.reject(error); } ); // 注册响应拦截器 axios.interceptors.response.use( function (response) { // 任何 2xx 状态码都会触发该函数 return response; }, function (error) { // 任何非 2xx 状态码都会触发该函数 return Promise.reject(error); } );

要点说明:

  • 请求拦截器的fulfilled收到的是配置对象config,必须返回(可能被修改过的)config;
  • 响应拦截器的fulfilled只在状态码落在2xx 区间时触发,其余状态码走rejected
  • 全局axios与通过axios.create()创建的实例各自拥有独立的拦截器集合,互不影响——从源码结构看,这一点由 Axios 构造函数 直接体现:每个实例在构造时各自new InterceptorManager()分别创建interceptors.requestinterceptors.response两个管理器。

二、移除拦截器:ejectclear

移除单个拦截器

use()的返回值是一个自增 ID,将其传给eject(id)即可精确移除对应拦截器:

// 移除请求拦截器 const myInterceptor = axios.interceptors.request.use(function () { /*...*/ }); axios.interceptors.request.eject(myInterceptor); // 移除响应拦截器 const myInterceptor = axios.interceptors.response.use(function () { /*...*/ }); axios.interceptors.response.eject(myInterceptor);

清空所有拦截器

调用interceptors.request.clear()/interceptors.response.clear()可移除全部拦截器,常用于测试隔离或实例重置:

const instance = axios.create(); instance.interceptors.request.use(function () { /*...*/ }); instance.interceptors.request.clear(); // 移除所有请求拦截器 instance.interceptors.response.use(function () { /*...*/ }); instance.interceptors.response.clear(); // 移除所有响应拦截器

源码视角:eject的"墓碑"机制与 ID 失效规则

拦截器的增删逻辑集中在 InterceptorManager。use()从内部计数器nextId++分配 ID 并pushhandlers数组(use 实现);eject(id)并不立即收缩数组,而是把对应位置置为null(eject 实现):

  • 只有当被移除的是末尾元素时才调用trimHandlers收缩数组,中间的空位(源码注释称"tombstone")由forEach遍历时跳过null来规避;
  • 遍历(iterationDepth > 0)期间不会压缩数组,避免在迭代长度快照过程中复用索引,遍历结束后才统一trimHandlers并重新同步内部handlerEntries(forEach 实现);
  • 如果用户代码把handlers数组整体替换(它是公开属性),旧 ID 会被判定为"已失效",再次eject时静默忽略,不会误删新注册的拦截器。

这些边界行为的回归测试见 InterceptorManager 单元测试,其中覆盖了"1 万次 use/eject 后不累积墓碑"、"eject 后 ID 不复用"、"handlers 被替换后旧 ID 失效"等场景。

三、同步与异步:synchronous选项

请求拦截器默认被当作异步处理:axios 内部会为其创建一条 Promise 链,即使你的拦截器是纯同步代码,请求也会被推到调用栈末尾执行。当主线程被阻塞时,这可能引入不必要的延迟。

如果确认请求拦截器是同步的,可以在第三个参数(options 对象)中传入{ synchronous: true },让 axios 同步执行拦截器代码,避免延迟:

axios.interceptors.request.use( function (config) { config.headers.test = "I am only a header!"; return config; }, null, { synchronous: true } );

同步模式下的执行链

在 Axios._request 中,所有请求拦截器都会先被标记synchronous,只有当所有注册的请求拦截器都声明了synchronous: true时,synchronousRequestInterceptors才为true,此时 axios 走同步分支:

  • 依次同步调用每个拦截器的onFulfilled,直接把返回值作为下一个拦截器的输入newConfig
  • 全部执行完毕后同步调用dispatchRequest发出请求,再按顺序挂接响应拦截器链(L250-L255)。

只要有一个请求拦截器未声明同步(或需要runWhen过滤后仍留有一个非同步拦截器),整条链就退回promise.then(...)的 Promise 链式执行模式(L196-L208)。

同步拦截器中的错误处理

当同步请求拦截器抛出错误时,行为规则是(与 Axios.js 同步分支 的实现一一对应):

  1. axios 调用与该拦截器配对onRejected处理器,并停止执行后续请求拦截器
  2. onRejected正常返回——包括返回undefined或一个已解决的 Promise——该错误被视为"已处理",axios 将用最后一个有效的配置照常发出请求;注意:处理器的返回值不会替换配置对象;
  3. 若要阻止请求发出,要么不提供onRejected,要么让onRejected抛出错误 / 返回被拒绝的 Promise——此时错误转为终止性错误,继续流向响应侧的拦截器链(源码中即promise = Promise.reject(error)后进入响应拦截器链);
  4. 特例:若onRejected返回一个 thenable,源码会Promise.resolve(rejectedResult).then(...)后仍继续dispatchRequest,即"等待异步善后完成后照发请求"。

示例一:校验类拦截器——缺Authorization头则拒绝,且错误继续向上传播:

axios.interceptors.request.use( function validate(config) { if (!config.headers.has("Authorization")) { throw new Error("Authorization is required"); } return config; }, function rejectInvalidRequest(error) { return Promise.reject(error); }, { synchronous: true } );

示例二:只记录日志、不阻断请求的拦截器——处理器正常返回即可保持"继续发送"的语义:

axios.interceptors.request.use( function prepare(config) { throw new Error("Optional preparation failed"); }, function logPreparationFailure(error) { console.warn(error); // 正常返回:axios 将使用最后一个有效配置发出请求 }, { synchronous: true } );

四、条件执行:runWhen选项

如果拦截器只需在满足特定运行时条件时才执行,可以在 options 中传入runWhen函数。当且仅当runWhen返回false时,该拦截器被跳过runWhen会以当前请求的配置对象为参数被调用(注意:你也可以通过this绑定额外参数)。这对"仅在特定场景下运行的异步拦截器"特别有用,因为它能在请求阶段就把它排除出执行链,而不是等执行时再判断。

function onGetCall(config) { return config.method === "get"; } axios.interceptors.request.use( function (config) { config.headers.test = "special get headers"; return config; }, null, { runWhen: onGetCall } );

源码中该过滤发生在构建拦截链的最早阶段(Axios.js):

this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) { return; // 该拦截器不进入本次请求的执行链 } // ... });

两个细节值得注意:

  • runWhen只作用于请求拦截器(响应拦截器链没有runWhen过滤);
  • 即使某拦截器声明了synchronous: true,若它被runWhen跳过,则不参与"全部同步"的判定,同步分支依然可以成立。

五、执行顺序:请求 LIFO,响应 FIFO

警告:请求与响应拦截器的执行顺序是相反的。

  • 请求拦截器按注册顺序的逆序执行(LIFO — 后注册先执行):最后注册的请求拦截器最先运行;
  • 响应拦截器按注册顺序执行(FIFO — 先注册先执行):最先注册的响应拦截器最先运行。

下面这个例子展示了三个请求拦截器 + 三个响应拦截器时的完整执行顺序:

const instance = axios.create(); const interceptor = (id) => (base) => { console.log(id); return base; }; instance.interceptors.request.use(interceptor("Request Interceptor 1")); instance.interceptors.request.use(interceptor("Request Interceptor 2")); instance.interceptors.request.use(interceptor("Request Interceptor 3")); instance.interceptors.response.use(interceptor("Response Interceptor 1")); instance.interceptors.response.use(interceptor("Response Interceptor 2")); instance.interceptors.response.use(interceptor("Response Interceptor 3")); // 控制台输出: // Request Interceptor 3 // Request Interceptor 2 // Request Interceptor 1 // [HTTP 请求发出] // Response Interceptor 1 // Response Interceptor 2 // Response Interceptor 3

源码视角:unshiftpush

这个"洋葱式"顺序直接来自 Axios.js 的建链逻辑:请求拦截器通过requestInterceptorChain.unshift(...)头插,响应拦截器通过responseInterceptorChain.push(...)尾插,于是请求侧先注册的反而排在链的尾部。

一个重要的版本行为提示:该处实际受transitional.legacyInterceptorReqResOrdering开关控制——在 transitional 默认值 中它默认为true(即上文文档描述的 LIFO 行为);若显式关闭该开关,请求拦截器改为push(FIFO)。因此升级或配置 transitional 选项时,务必确认自己的拦截器依赖的是哪种顺序。

六、多个拦截器的链式语义

同一请求/响应可以注册多个拦截器,它们在一条链上按如下规则流转:

  • 每个拦截器接收前一个拦截器的返回值,只有最后一个拦截器的结果会被返回给请求方;
  • 请求拦截器逆序(LIFO)执行,响应拦截器正序(FIFO)执行;
  • 当某个fulfilled处理器抛出异常时:
    • 下一个fulfilled处理器不再被调用
    • 紧接着调用下一个拦截器的rejected处理器(而非同级的 rejected);
    • 一旦错误被捕获(rejected处理器正常返回),链继续按 Promise 链的常规语义向后流转——下一个fulfilled重新参与调用。

这套语义与原生 Promise 的.then(onFulfilled, onRejected)链完全一致:axios 在异步模式下正是把每个拦截器展开为[fulfilled, rejected, fulfilled, rejected, ...]的扁平数组,然后循环promise = promise.then(chain[i++], chain[i++])(Axios.js)。理解这一点,你就能准确预测复杂拦截链中错误到底会被哪个处理器接住。

七、小结与延伸阅读

  • 注册:use(fulfilled, rejected, options),返回值是可用于eject的 ID;
  • 移除:eject(id)精确移除,clear()全量清空;
  • 性能敏感且拦截器为同步代码时,用{ synchronous: true }走同步分支(需全部请求拦截器都声明才生效),并理解同步分支下"错误被处理后照发请求"的规则;
  • 条件执行用{ runWhen(config) },返回false即跳过;
  • 顺序:请求 LIFO、响应 FIFO,受transitional.legacyInterceptorReqResOrdering(默认true)影响。

进一步阅读与验证依据:

  • 拦截器管理器的完整实现:InterceptorManager.js
  • 拦截链的构建与同步/异步分支:Axios.js_request
  • transitional 默认值(含legacyInterceptorReqResOrdering):transitional.js
  • 拦截器管理器的回归测试(墓碑、ID 失效、迭代中 eject/clear 等):InterceptorManager.test.js
  • 相关文档:请求配置、错误处理、创建实例

【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 20:16:48

OCRmyPDF 实战手册:从基础 OCR 到 v17 新特性的完整操作指南

OCRmyPDF 实战手册:从基础 OCR 到 v17 新特性的完整操作指南 【免费下载链接】OCRmyPDF OCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to be searched 项目地址: https://gitcode.com/GitHub_Trending/oc/OCRmyPDF 本文为 OCRmyPDF…

作者头像 李华
网站建设 2026/9/6 20:14:51

LPDDR4与LPDDR3差异详解:从JEDEC标准到工程实践

简介:JEDEC JESD209-4/3是LPDDR4与LPDDR3的官方规范基础,这份精解面向硬件工程师、嵌入式开发者和存储从业者,以问答形式剖析LP4与LP4X差异、Apple M1性能来源、LPDDR4是否有ECC、LVSTL模型意义、16bit per channel成因、Pad Order内涵、eMCP…

作者头像 李华
网站建设 2026/9/6 20:12:14

C语言课后习题这样练:从看得懂到写得出的实战方法

简介:面向系统学习C语言的初学者、自学者及备考者,《C语言程序设计现代方法(第2版)》全部课后习题参考答案以PDF文档形式整理,可用于课后自查、编程练习与查漏补缺。内容按教材章节展开,覆盖第二章数据类型…

作者头像 李华
网站建设 2026/9/6 20:08:39

NI LabVIEW FPGA开发环境搭建:版本兼容与NI-RIO驱动配置实战

简介:面向使用NI CompactRIO嵌入式测控平台的FPGA开发者,这份PDF系统梳理了搭建LabVIEW开发环境所需的完整软件组合:NI LabVIEW开发系统、LabVIEW Real-Time模块、LabVIEW FPGA模块以及NI-RIO驱动。文档先简述CompactRIO由实时处理器、FPGA和…

作者头像 李华
网站建设 2026/9/6 20:08:14

数控机床故障诊断与维修:从报警代码到PLC逻辑的实战指南

简介:数控机床故障诊断与维修复习资料是一份面向数控设备维修人员、机电专业学生及工程现场技术人员的备考与知识梳理文档。资料围绕机床维护与排障的31个高频考点展开,系统覆盖了CNC系统无报警故障排查、电源电压波动允许范围、自诊断功能的开机自检与实…

作者头像 李华