我用 AI 重写了项目的请求层,从 800 行"面条代码"变成 3 层洋葱模型

接手一个 3 年老项目,打开 api/axios.ts 看到 800 行代码------拦截器里塞了鉴权、品牌切换、错误提示、日志、重试......每次改一个逻辑都怕影响别的。

我把这个文件丢给 AI,让它帮我重新设计架构。最终方案:洋葱模型,每个关注点一个中间件,互不干扰。

原始状态

scss 复制代码
axios.ts (800行)
├── 请求拦截器 (200行)
│   ├── 鉴权 token 注入
│   ├── brandId 注入
│   ├── 环境判断
│   ├── 请求日志
│   └── 特殊 URL 处理
├── 响应拦截器 (400行)
│   ├── 登录过期处理
│   ├── 权限不足处理
│   ├── 网络错误处理
│   ├── 业务错误码映射
│   ├── 错误弹窗逻辑
│   └── 重试逻辑
└── 工具函数 (200行)

改一行都心惊肉跳。

AI 给出的分层设计

yaml 复制代码
请求生命周期(洋葱模型):

        请求方向 →
┌─────────────────────────────────────────────┐
│  Layer 1: Auth(鉴权)                       │
│  ┌───────────────────────────────────────┐  │
│  │  Layer 2: BrandId(品牌注入)          │  │
│  │  ┌─────────────────────────────────┐  │  │
│  │  │  Layer 3: ErrorHandler(错误)   │  │  │
│  │  │  ┌───────────────────────────┐  │  │  │
│  │  │  │  实际网络请求              │  │  │  │
│  │  │  └───────────────────────────┘  │  │  │
│  │  └─────────────────────────────────┘  │  │
│  └───────────────────────────────────────┘  │
└─────────────────────────────────────────────┘
        ← 响应方向

每一层只管一件事,新增需求加一层,删除需求去掉一层,不影响其他层。

关键代码:拦截器拆分

改造前(混在一起):

typescript 复制代码
axios.interceptors.request.use((config) => {
  // 50行鉴权逻辑
  const token = getToken()
  if (token) config.headers.Authorization = token
  
  // 30行品牌逻辑
  const brandId = getBrandId()
  if (brandId) config.headers['X-Brand-Id'] = brandId
  
  // 20行环境逻辑
  if (process.env.API_ENV === 'test') { ... }
  
  // ...更多
  return config
})

改造后(各管各的):

typescript 复制代码
// interceptors/auth.ts
export const authInterceptor = {
  request: (config: AxiosRequestConfig) => {
    const token = getToken()
    if (token) {
      config.headers.Authorization = token
    }
    return config
  },
  responseError: (error: AxiosError) => {
    if (error.response?.status === 401) {
      handleLogout()
    }
    return Promise.reject(error)
  },
}

// interceptors/brandId.ts
export const brandIdInterceptor = {
  request: (config: AxiosRequestConfig) => {
    const brandId = getBrandId()
    if (brandId) {
      config.headers['X-Brand-Id'] = brandId
    }
    return config
  },
}

// interceptors/errorHandler.ts
export const errorHandlerInterceptor = {
  responseError: (error: AxiosError) => {
    const { status, data } = error.response || {}
    const msg = ERROR_CODE_MAP[data?.code] || '网络异常'
    message.error(msg)
    return Promise.reject(error)
  },
}

注册:

typescript 复制代码
// api/axios.ts(现在只有 30 行)
const instance = axios.create({ baseURL: PROXY_URL, timeout: 15000 })

const interceptors = [
  authInterceptor,
  brandIdInterceptor,
  errorHandlerInterceptor,
]

interceptors.forEach(({ request, response, responseError }) => {
  if (request) instance.interceptors.request.use(request)
  if (response) instance.interceptors.response.use(response)
  if (responseError) instance.interceptors.response.use(undefined, responseError)
})

export default instance

AI 在这个过程中做了什么

arduino 复制代码
我的输入:
├── "这是我的 axios.ts,帮我重构"
├── "要求:每个关注点分离,可独立测试"
└── "保持对外 API 不变,调用方零改动"

AI 的输出:
├── 架构建议(洋葱模型 vs 管道模式 vs 装饰器模式)
├── 每个拦截器的独立文件
├── 注册逻辑
└── 一个我没想到的点:拦截器执行顺序说明

拦截器顺序的坑

AI 特别提醒了一个 axios 的行为:

ini 复制代码
请求拦截器:后注册的先执行(栈结构)
响应拦截器:先注册的先执行(队列结构)

所以注册顺序要反着想:
interceptors = [auth, brandId, errorHandler]

实际请求执行顺序:errorHandler → brandId → auth → [网络] → auth → brandId → errorHandler

这个如果不注意,鉴权失败时的错误处理顺序会出问题。AI 在设计阶段就帮我规避了。

对外接口不变

改造前怎么用的,改造后还是怎么用:

typescript 复制代码
// 业务层调用------零改动
import request from '@/api/axios'

export const getStoreList = (params: StoreListParams) =>
  request.get('/store/list', { params })

这就是重构的核心原则:内部随便改,外部无感知

收益

arduino 复制代码
改造前:
├── 800 行单文件,改一行可能影响全部
├── 无法单独测试某个拦截逻辑
├── 新人看完需要 2h 才能理解
└── 加新功能(如请求加密)需要在 3 个地方改

改造后:
├── 主文件 30 行,每个拦截器 30-50 行
├── 每个拦截器可独立 mock 测试
├── 新人 10min 理解整体架构
└── 加新功能 = 加一个文件 + 注册一行

什么时候该重构请求层

复制代码
值得重构:
├── 拦截器超过 100 行
├── 多个不相关逻辑写在同一个拦截器
├── 改一个功能总担心影响其他
└── 新人入职搞不懂请求层做了什么

不需要的:
├── 项目小(拦截器 <50 行)
├── 逻辑简单(只有 token 注入 + 错误提示)
└── 没人维护的遗留项目

💬 你们项目的请求层是什么状态?有没有类似的"上帝文件"想重构但不敢动?


🔗 完整 Skills 源码已开源github.com/sleepyccat/...,欢迎 Star ⭐ 和 PR。

相关推荐
fatcoder1 小时前
玩转Docker 06 — 容器网络
前端·后端·docker
默_笙1 小时前
🛬 前端路由的"高级玩法":懒加载、404、鉴权路由,一个都不能少(下篇)
前端·javascript
用户921080262861 小时前
1. Cesium 在 Vue 项目中的简单初始化配置
前端
YIAN1 小时前
从 Hash 底层原理到 React Router v6 实战:我学会了什么?
前端·react.js·vue-router
lv__pf2 小时前
Spring配置类解析 【TL spring 11】
java·前端·spring
AI分享猿2 小时前
UI设计Prompt系列(十三):响应式布局需求怎么写——让设计Prompt更接近前端实现
前端
ClouGence2 小时前
Selenium 写不动了?这个工具录一次就能跑 Web 自动化
前端·selenium·测试
tedcloud1232 小时前
book-to-skill 怎么部署?把技术书和文档转换成可复用的 AI Skill
运维·服务器·人工智能·开源·ai编程
hunterandroid2 小时前
[鸿蒙从零到一] HarmonyOS 地图、定位与传感器能力实战:从位置获取到运动感知
前端