在 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/500,Axios 层面抛出错误。
- 业务错误: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:业务正常,取 data;success: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 对象,自定义
name='BizError',挂载info属性存放后端完整业务信息; - 手动 throw error。
关键点:虽然 HTTP 请求成功,但是业务失败,主动抛出异常,业务代码中 await request () 会进入 catch,不会走 then 。 如果不抛异常,即使 success=false,业务代码依然会走到
.then,每个接口都要手动判断 success,非常麻烦。
2.2 errorHandler 错误捕获分发
errorThrower抛出异常后,会进入 errorHandler,统一做消息提示。分三大错误分支:
error.name === 'BizError':业务层错误 来自上面errorThrower抛出,读取后端返回的showType,按枚举展示不同 antd 提示组件。opts?.skipErrorHandler:调用接口时手动配置{skipErrorHandler:true},可以跳过全局错误提示,业务自己处理错误。error.response:Axios HTTP 错误 请求发送成功,服务器返回状态码,但是不在 2xx 区间,如 401、403、500。error.request:无响应错误 请求已经发出,但是没有收到任何返回。典型场景:断网、后端服务宕机、跨域拦截。- else 其他错误:请求构建阶段异常。
遗留 TODO:
REDIRECT枚举,官方模板没有实现,一般用来处理 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: Record<string, 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已经捕获success=false并且弹窗;如果后端返回业务错误,这里会先弹一次 "请求失败!",之后 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:判断 success,false则抛出BizError
↓
7.抛出异常,进入 errorHandler,根据错误类型统一弹消息
↓
👉 如果没有异常:业务代码进入 then;有异常业务代码进入catch
重点:
responseInterceptors在errorThrower前面执行。
六、高频业务扩展改造方案
改造 1:401 未登录,跳转到登录页(完善 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提供局部关闭全局提示的能力,适合需要自定义错误逻辑的接口。