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 的中间件请求拦截器在请求发出前运行响应拦截器在响应返回后或出错时运行常用于日志记录、修改请求头、统一处理响应数据与错误。读完本文你将掌握拦截器的注册、移除、synchronous与runWhen两个高级选项的用法以及请求/响应拦截器相反的执行顺序LIFO 与 FIFO并能从 axios 源码层面理解拦截链是如何构建和执行的。一、基本用法注册请求与响应拦截器axios 实例提供interceptors.request与interceptors.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.request和interceptors.response两个管理器。二、移除拦截器eject与clear移除单个拦截器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 并push到handlers数组use 实现eject(id)并不立即收缩数组而是把对应位置置为nulleject 实现只有当被移除的是末尾元素时才调用trimHandlers收缩数组中间的空位源码注释称tombstone由forEach遍历时跳过null来规避遍历iterationDepth 0期间不会压缩数组避免在迭代长度快照过程中复用索引遍历结束后才统一trimHandlers并重新同步内部handlerEntriesforEach 实现如果用户代码把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 同步分支 的实现一一对应axios 调用与该拦截器配对的onRejected处理器并停止执行后续请求拦截器若onRejected正常返回——包括返回undefined或一个已解决的 Promise——该错误被视为已处理axios 将用最后一个有效的配置照常发出请求注意处理器的返回值不会替换配置对象若要阻止请求发出要么不提供onRejected要么让onRejected抛出错误 / 返回被拒绝的 Promise——此时错误转为终止性错误继续流向响应侧的拦截器链源码中即promise Promise.reject(error)后进入响应拦截器链特例若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.jsthis.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源码视角unshift与push这个洋葱式顺序直接来自 Axios.js 的建链逻辑请求拦截器通过requestInterceptorChain.unshift(...)头插响应拦截器通过responseInterceptorChain.push(...)尾插于是请求侧先注册的反而排在链的尾部。一个重要的版本行为提示该处实际受transitional.legacyInterceptorReqResOrdering开关控制——在 transitional 默认值 中它默认为true即上文文档描述的 LIFO 行为若显式关闭该开关请求拦截器改为pushFIFO。因此升级或配置 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_requesttransitional 默认值含legacyInterceptorReqResOrderingtransitional.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),仅供参考