当前位置: 首页 > news >正文

Axios拦截器实战:从请求到响应的全流程控制

1. Axios拦截器入门:前端开发的瑞士军刀

第一次接触Axios拦截器是在三年前的一个电商项目里,当时需要给所有请求自动添加Token。当我发现每个API调用都要手动设置header时,整个人都不好了。直到同事扔给我一段拦截器代码,才真正体会到什么叫"一行代码解决所有问题"。

Axios拦截器本质上是个中间件管道,就像快递公司的分拣系统。请求发出前要经过请求拦截器(request interceptor)处理,收到响应后要经过响应拦截器(response interceptor)加工。这个机制让统一处理认证、日志、错误等需求变得异常简单。

来看个最简单的例子:

// 添加请求拦截器 axios.interceptors.request.use(config => { config.headers.Authorization = 'Bearer token123' return config }) // 添加响应拦截器 axios.interceptors.response.use(response => { console.log(`收到来自${response.config.url}的响应`) return response })

这段代码实现了两个最常用功能:

  1. 自动给所有请求添加认证头
  2. 记录所有响应日志

实际项目中,拦截器能做的事情远不止这些。比如我们可以在请求阶段:

  • 统一添加时间戳参数
  • 压缩请求数据
  • 显示全局loading动画

在响应阶段可以:

  • 统一处理错误码
  • 解压响应数据
  • 隐藏loading动画

2. 拦截器核心机制详解

2.1 洋葱模型与执行顺序

很多初学者容易误解拦截器的执行顺序。假设我们注册了多个拦截器:

// 请求拦截器A axios.interceptors.request.use(config => { console.log('请求拦截器A') return config }) // 请求拦截器B axios.interceptors.request.use(config => { console.log('请求拦截器B') return config }) // 响应拦截器X axios.interceptors.response.use(response => { console.log('响应拦截器X') return response }) // 响应拦截器Y axios.interceptors.response.use(response => { console.log('响应拦截器Y') return response })

实际执行顺序是:B → A → 服务器 → X → Y。这个反直觉的顺序源于Axios内部的unshift操作。源码中请求拦截器是通过unshift方法添加到调用链前端的,所以后注册的反而先执行。

2.2 错误处理的最佳实践

拦截器的错误处理经常被忽视。来看个典型的错误示范:

axios.interceptors.response.use(response => { if (response.status !== 200) { throw new Error('请求失败') } return response })

这种写法会导致业务代码的catch无法捕获错误。正确的做法应该是:

axios.interceptors.response.use( response => response, error => { if (error.response.status === 401) { router.push('/login') } return Promise.reject(error) // 关键!让错误继续传递 } )

在业务代码中就可以正常捕获了:

axios.get('/api').catch(err => { console.log('这里能捕获到拦截器reject的错误') })

3. 企业级实战案例

3.1 自动化Token刷新方案

在需要长期维持登录状态的系统中,Token过期处理是个经典难题。通过拦截器可以优雅地解决:

let isRefreshing = false let requestsQueue = [] axios.interceptors.response.use(null, async error => { if (error.config && error.response?.status === 401) { if (!isRefreshing) { isRefreshing = true try { const newToken = await refreshToken() localStorage.setItem('token', newToken) requestsQueue.forEach(cb => cb(newToken)) return axios(error.config) } finally { isRefreshing = false requestsQueue = [] } } return new Promise(resolve => { requestsQueue.push(token => { error.config.headers.Authorization = `Bearer ${token}` resolve(axios(error.config)) }) }) } return Promise.reject(error) })

这个方案实现了:

  1. 检测到401错误自动刷新Token
  2. 避免并发刷新
  3. 排队等待中的请求在刷新后自动重试

3.2 性能监控与埋点

拦截器也是实现监控的绝佳位置:

const metrics = { requestCount: 0, successCount: 0, errorCount: 0, totalTime: 0 } axios.interceptors.request.use(config => { config.metadata = { startTime: Date.now() } metrics.requestCount++ return config }) axios.interceptors.response.use( response => { const duration = Date.now() - response.config.metadata.startTime metrics.totalTime += duration metrics.successCount++ logToServer('api-success', { url: response.config.url, duration }) return response }, error => { metrics.errorCount++ logToServer('api-error', { url: error.config.url, status: error.response?.status }) return Promise.reject(error) } )

4. 高级技巧与原理剖析

4.1 动态禁用拦截器

有时候我们需要临时跳过某些拦截器。Axios提供了eject方法:

const requestId = axios.interceptors.request.use(config => { config.headers['X-Debug'] = true return config }) // 需要跳过时 axios.interceptors.request.eject(requestId)

更优雅的做法是通过自定义配置:

axios.interceptors.request.use(config => { if (config.skipInterceptor) return config // 正常处理逻辑 return config }) // 使用方式 axios.get('/api', { skipInterceptor: true })

4.2 拦截器与CancelToken的结合

在文件上传等场景中,可能需要取消请求:

const source = axios.CancelToken.source() axios.interceptors.request.use(config => { if (config.cancelToken) { config.onCancel = () => { source.cancel('请求被拦截器取消') } } return config }) // 取消请求 source.cancel()

4.3 源码级执行流程

Axios的拦截器实现非常精妙,核心在于Promise链的构建:

  1. 初始化chain数组,包含默认的dispatchRequest
  2. 通过unshift添加所有请求拦截器
  3. 通过push添加所有响应拦截器
  4. 使用while循环构建Promise链
// 简化版实现 let chain = [dispatchRequest, null] let promise = Promise.resolve(config) // 添加请求拦截器 this.interceptors.request.forEach(interceptor => { chain.unshift(interceptor.fulfilled, interceptor.rejected) }) // 添加响应拦截器 this.interceptors.response.forEach(interceptor => { chain.push(interceptor.fulfilled, interceptor.rejected) }) while(chain.length) { promise = promise.then(chain.shift(), chain.shift()) }

这种实现方式保证了:

  • 请求拦截器先进后出(FILO)
  • 响应拦截器先进先出(FIFO)
  • 统一的错误处理机制

5. 常见问题排查指南

5.1 拦截器导致无限循环

曾经在项目中遇到过刷新Token的死循环问题:

axios.interceptors.response.use(null, async error => { if (error.response.status === 401) { const newToken = await refreshToken() // 这里用的也是axios // ... } })

解决方案是创建独立的axios实例用于Token刷新:

const authHttp = axios.create() axios.interceptors.response.use(null, async error => { if (error.response.status === 401) { const newToken = await authHttp.post('/refresh') // ... } })

5.2 响应数据被多次处理

有时候会发现响应数据被多次修改,这通常是因为:

  1. 重复注册了相同的拦截器
  2. 拦截器没有正确返回处理后的数据

正确的做法应该是:

axios.interceptors.response.use(response => { // 修改响应数据 response.data = transform(response.data) // 必须返回整个response对象 return response })

5.3 TypeScript类型增强

在TS项目中,可以通过声明合并来扩展Axios类型:

declare module 'axios' { interface AxiosRequestConfig { skipInterceptor?: boolean metadata?: { startTime?: number } } }

这样在使用自定义配置时就能获得类型提示了。

http://www.jsqmd.com/news/594554/

相关文章:

  • STM32分散加载机制与内存管理详解
  • 避开STM32定时器PWM的那些坑:从CubeMX配置到代码调试的避坑指南
  • SecGPT-14B API保护:防止OpenClaw任务过度消耗模型资源
  • 2007 Text 1
  • OpenClaw安全防护指南:Qwen3-32B私有镜像权限控制策略
  • SEO标题优化与内容营销的关系是什么
  • ESM3 vs AlphaFold3:不需要MSA的蛋白质预测新选择(含本地部署性能测试)
  • SEO_如何制定高效的SEO内容策略?分步指南
  • BH1750光传感器原理、I²C驱动与六种测量模式详解
  • 光刻胶选型避坑指南:从正胶负胶到配套试剂的全流程解析
  • RK3568实战:用QEMU在x86电脑上模拟构建和调试ARM64 Ubuntu 22.04根文件系统
  • OpenClaw场景词典:Qwen3.5-9B在20个日常任务中的实测表现
  • OpenClaw技能开发指南:为百川2-13B-4bits模型编写自定义技能
  • WSL2多版本Ubuntu共存与切换实战指南
  • ADI SC589官方资源挖宝指南:如何高效获取SDK/原理图/PCB设计文件
  • 避坑指南:鸿蒙3.0+Flutter开发BLE应用时,权限、后台保活与多设备管理的那些坑
  • C++的std--ranges算法自定义投影函数与成员指针在代码简洁性上的优势
  • SpringBoot源码企业公司ERP进销存管理系统JavaWeb项目前后端分离Vue实现方案
  • 【RV1106】基于LVGL的ST7735S驱动移植与图像显示实战
  • Unity/Unreal开发者必看:用四元数彻底告别万向死锁,让你的3D角色旋转丝滑起来
  • 无线工程师必备:用Wireshark解码802.11ac VHT Capabilities字段全攻略(含160MHz配置示例)
  • OpenClaw多模型混搭:Qwen2.5-VL-7B与文本模型协同工作流
  • Java集成LibreOffice实现高效Office文档批量转PDF方案
  • OpenClaw本地知识库构建:Qwen2.5-VL-7B处理扫描版PDF与图片资料
  • 从GCC到Nginx:一文搞定Linux开发环境搭建(附1.13.7版本编译避坑指南)
  • 嵌入式摇杆输入处理库:ADC滤波与按钮去抖设计
  • 电子工程师必备英语技能与实战指南
  • UE5 UMG坐标转换实战:用SlateBlueprintLibrary搞定UI拖拽与点击检测
  • TrueLicense实战避坑指南:从KeyTool生成密钥到SpringBoot拦截器校验的完整流程(附常见错误排查)
  • 2-3 上下文管理:让AI真正“看懂“你的项目