请求拦截、响应拦截、业务错误统一处理

📅 2026/8/9 3:45:11
请求拦截、响应拦截、业务错误统一处理
在 Ant Design Pro 后台项目中plugin‑request是框架内置对 Axios 的高层封装帮助我们统一处理请求头、业务错误、HTTP 状态码异常、消息提示避免每个接口写重复的 try‑catch 和弹框逻辑。很多同学直接复制默认配置却不理解内部执行流程遇到 token 失效、后端返回格式变更、401 未登录、业务自定义弹窗等问题无从下手。本文完整解析这份官方模板代码讲清楚每一段代码的作用、执行顺序、常见踩坑点与扩展思路。完整代码就是项目src/app.ts的request配置也就是我们贴出的源码。整体架构总览RequestConfig分为三大模块errorConfig业务错误处理器包含errorThrower抛出业务异常、errorHandler捕获并处理异常专门处理后端业务码不是 HTTP 状态码。requestInterceptors 请求拦截器请求发出之前执行统一追加 token、userId 等公共请求头。responseInterceptors 响应拦截器接口拿到后端返回 response 之后执行在交给业务页面之前做统一处理。重要区分HTTP 错误401/403/404/500Axios 层面抛出错误。业务错误HTTP 200但是success:false属于业务逻辑失败由plugin‑request的errorConfig接管。一、类型与枚举定义解析import type { RequestOptions } from /plugin-request/request; import type { RequestConfig } from umijs/max; import { message, notification } from antd; // 错误处理方案 错误类型 enum ErrorShowType { SILENT 0, // 静默不提示任何消息 WARN_MESSAGE 1, // warning 警告提示 ERROR_MESSAGE 2, // error 错误提示 NOTIFICATION 3, // notification通知框 REDIRECT 9, // 需要跳转例如登录失效跳转登录页 } // 和后端约定返回的数据格式 interface ResponseStructure { success: boolean; data: any; errorCode?: number; errorMessage?: string; showType?: ErrorShowType; }解读ErrorShowType后端控制前端提示行为。后端返回showType前端根据这个枚举决定用什么组件展示错误。好处后端接口可以精细化控制报错表现。比如有些错误不需要弹窗静默记录日志有些重要错误用通知栏。ResponseStructure前后端契约。约定接口 HTTP 状态码永远 200业务成功失败由success布尔字段控制。success:true业务正常取 datasuccess:false业务失败读取errorCode、errorMessage、showType。⚠️坑点如果你们后端不返回这套字段整套业务错误处理会失效需要修改字段映射。二、errorConfig 业务错误处理核心errorConfig: { //错误抛出 errorThrower: (res) { const { success, data, errorCode, errorMessage, showType } res as unknown as ResponseStructure; if (!success) { const error: any new Error(errorMessage); error.name BizError; error.info { errorCode, errorMessage, showType, data }; throw error; // 抛出自制的业务错误 } }, //错误接收及处理 errorHandler: (error: any, opts: any) { if (opts?.skipErrorHandler) throw error; //我们 errorThrower抛出的业务错误 if (error.name BizError) { const errorInfo: ResponseStructure | undefined error.info; if (errorInfo) { const { errorMessage, errorCode } errorInfo; switch (errorInfo.showType) { case ErrorShowType.SILENT: // do nothing break; case ErrorShowType.WARN_MESSAGE: message.warning(errorMessage); break; case ErrorShowType.ERROR_MESSAGE: message.error(errorMessage); break; case ErrorShowType.NOTIFICATION: notification.open({ description: errorMessage, message: errorCode, }); break; case ErrorShowType.REDIRECT: // TODO: redirect break; default: message.error(errorMessage); } } } else if (error.response) { //Axios HTTP错误服务器返回非2xx状态码404 500 401 message.error(Response status:${error.response.status}); } else if (error.request) { //请求发出去但是完全没有收到后端响应断网、跨域、后端服务挂掉 message.error(None response! Please retry.); } else { // 创建请求阶段出错配置错误 message.error(Request error, please retry.); } }, },2.1 errorThrower 做了什么执行时机接口 HTTP 200 拿到响应后。判断success false业务失败创建 JS Error 对象自定义nameBizError挂载info属性存放后端完整业务信息手动 throw error。关键点虽然 HTTP 请求成功但是业务失败主动抛出异常业务代码中 await request () 会进入 catch不会走 then。 如果不抛异常即使 successfalse业务代码依然会走到.then每个接口都要手动判断 success非常麻烦。2.2 errorHandler 错误捕获分发errorThrower抛出异常后会进入errorHandler统一做消息提示。分三大错误分支error.name BizError业务层错误来自上面errorThrower抛出读取后端返回的showType按枚举展示不同 antd 提示组件。opts?.skipErrorHandler调用接口时手动配置{skipErrorHandler:true}可以跳过全局错误提示业务自己处理错误。error.responseAxios HTTP 错误请求发送成功服务器返回状态码但是不在 2xx 区间如 401、403、500。error.request无响应错误请求已经发出但是没有收到任何返回。典型场景断网、后端服务宕机、跨域拦截。else 其他错误请求构建阶段异常。遗留 TODOREDIRECT枚举官方模板没有实现一般用来处理 token 过期跳转登录页。常见坑点后端返回success:false但是页面没有弹报错大概率后端字段名不是success / errorMessage。想某个接口不使用全局提示调用时传入{skipErrorHandler:true}自己在 catch 处理。error.info是自定义挂载属性TS 不会识别源码用any绕过类型。三、requestInterceptors 请求拦截器requestInterceptors: [ (config: RequestOptions) { // 从本地存储获取认证信息 const token localStorage.getItem(token); const userId localStorage.getItem(userId); // 构建公共请求头 const authHeaders: Recordstring, string {}; if (token) { authHeaders[token] token; } if (userId) { authHeaders[id] userId; } return { ...config, headers: { ...config.headers, ...authHeaders, }, }; }, ],执行时机请求发送到服务器之前。从localStorage读取 token、userId合并原有接口配置的 headers追加自定义鉴权头返回新的 config 对象plugin‑request 拿着这个配置发起网络请求。⚠️坑点注意合并顺序...config.headers写前面业务接口的 header 优先级高于全局拦截器业务接口可以覆盖全局 token。不要直接修改原 config必须返回新对象。很多项目 token 放Authorization请求头而这份代码使用自定义token请求头需要和后端对齐。localStorage在 SSR 环境会报错Umi Max 默认客户端渲染后台项目无问题。四、responseInterceptors 响应拦截器responseInterceptors: [ (response) { // 拦截响应数据进行个性化处理 const { data } response as unknown as ResponseStructure; if (data?.success false) { message.error(请求失败); } return response; }, ],执行时机拿到后端 response 之后在 errorThrower 之前执行。重大注意点很多人踩坑这里有一段冗余逻辑if(data?.success false) message.error(请求失败)因为后面errorThrower已经捕获successfalse并且弹窗如果后端返回业务错误这里会先弹一次 “请求失败”之后 errorHandler 又弹一次后端的 errorMessage造成双重提示✅优化建议可以直接删除这段判断否则出现重复报错弹窗。responseInterceptors: [ (response) { return response; } ]responseInterceptors 的用途可以在这里做 response.data 的预处理例如统一剥壳、打印日志。不要在这里做业务错误弹窗交给 errorConfig 处理。五、完整执行时序图非常重要1.业务代码调用 request(/api/xxx) ↓ 2.执行 requestInterceptors 请求拦截器 → 追加token头 ↓ 3.Axios发起http请求 ↓ 4.后端返回response ↓ 5.执行 responseInterceptors 响应拦截器 ↓ 6.进入 errorThrower判断 successfalse则抛出BizError ↓ 7.抛出异常进入 errorHandler根据错误类型统一弹消息 ↓ 如果没有异常业务代码进入 then有异常业务代码进入catch重点responseInterceptors在errorThrower前面执行。六、高频业务扩展改造方案改造 1401 未登录跳转到登录页完善 REDIRECT 枚举在errorHandler中HTTP 错误分支捕获error.response.status 401清除 token跳转登录。else if (error.response) { if(error.response.status 401){ localStorage.removeItem(token); localStorage.removeItem(userId); // umi 跳转 history.push(/login); message.error(登录已过期请重新登录); }else{ message.error(Response status:${error.response.status}); } }改造 2后端字段不匹配比如后端叫code代替success修改errorThrower内部判断逻辑把success改为code 200。改造 3某个接口跳过全局错误提示//业务调用示例 const res await request(/api/demo,{skipErrorHandler:true}).catch(err{ //自己单独处理错误 })改造 4移除 responseInterceptors 重复弹窗代码上文提到默认模板存在重复弹窗 bug生产环境建议删除。七、总结plugin‑request把错误分为业务错误和HTTP 网络错误两套体系errorThrower把业务失败主动抛出异常让业务代码统一走 catch。请求拦截器统一注入 token注意 headers 合并顺序业务接口 header 优先级更高。响应拦截器执行时机早于业务错误处理默认模板存在重复弹窗 bug需要手动修复。ErrorShowType实现后端驱动前端报错 UI是这套配置的设计亮点。skipErrorHandler提供局部关闭全局提示的能力适合需要自定义错误逻辑的接口。