1. 对 axios 封装时 HTTP 状态码归类处理的完整实践
在现代化前端项目中,axios 作为最主流的 HTTP 客户端库,其二次封装质量直接影响项目的开发效率和可维护性。我曾在多个大型项目中负责前端架构设计,发现很多团队对 axios 的封装都停留在基础层面,特别是对 HTTP 状态码的处理往往不够系统化。本文将分享我在企业级项目中总结的完整状态码处理方案。
1.1 为什么需要状态码归类处理?
想象一下这样的场景:你的项目中有上百个 API 请求,每个请求都需要处理 401 未授权、403 禁止访问、500 服务器错误等常见状态码。如果每个请求都单独写一遍状态码判断逻辑,不仅代码冗余,而且当需要调整处理逻辑时(比如 401 从跳转登录页改为静默刷新 token),就需要修改所有相关文件——这简直是维护噩梦。
通过响应拦截器进行统一的状态码归类处理,可以实现:
- 逻辑复用:相同状态码的处理逻辑只写一次
- 集中管理:所有异常处理策略在一个文件中维护
- 业务解耦:业务组件无需关心 HTTP 层细节
- 一致体验:相同错误在全应用有相同的处理方式
1.2 HTTP 状态码分类体系解析
HTTP 状态码虽然数量众多,但可以按照以下维度进行分类处理:
1.2.1 按状态码类别
| 状态码范围 | 类别 | 典型场景 | 前端处理原则 |
|---|---|---|---|
| 2xx | 成功 | 200 OK, 201 Created | 正常返回数据 |
| 3xx | 重定向 | 301 Moved, 304 Not Modified | 浏览器自动处理/特殊处理 |
| 4xx | 客户端错误 | 400 Bad Request, 404 Not Found | 提示用户/修正请求 |
| 5xx | 服务器错误 | 500 Internal Error | 提示稍后重试/记录日志 |
1.2.2 按业务场景
在实际项目中,我们更关注状态码对应的具体业务含义:
javascript复制const BUSINESS_ERROR_CODES = {
UNAUTHORIZED: [401, 403], // 认证/授权问题
CLIENT_ERROR: [400, 404], // 客户端请求问题
SERVER_ERROR: [500, 502, 503], // 服务端问题
NETWORK_ERROR: ['ECONNABORTED', 'ETIMEDOUT'] // 网络问题
}
1.3 响应拦截器实现方案
下面是一个完整的响应拦截器实现示例,包含状态码归类处理的核心逻辑:
javascript复制// axios 实例配置
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 10000
})
// 响应拦截器
service.interceptors.response.use(
response => {
// 2xx 范围内的状态码都会触发该函数
return handleSuccessResponse(response)
},
error => {
// 超出 2xx 范围的状态码都会触发该函数
return handleErrorResponse(error)
}
)
// 成功响应处理
function handleSuccessResponse(response) {
// 可在此处处理业务自定义状态码(如后端返回的 code 字段)
const res = response.data
if (res.code !== 200) {
return Promise.reject(new Error(res.message || 'Error'))
}
return res
}
// 错误响应处理
function handleErrorResponse(error) {
if (error.response) {
// 请求已发出且服务器响应了状态码
const status = error.response.status
const message = error.response.data?.message || error.message
switch (status) {
case 400:
console.error('请求参数错误:', message)
break
case 401:
// token 过期处理
if (!isRefreshing) {
return handleTokenExpired(error)
}
break
case 403:
// 无权限处理
router.push('/403')
break
case 404:
console.error('请求资源不存在:', message)
break
case 500:
// 服务器错误
notifyServerError()
break
default:
console.error('未知错误:', message)
}
} else if (error.request) {
// 请求已发出但没有收到响应
console.error('无响应:', error.message)
} else {
// 发送请求时异常
console.error('请求错误:', error.message)
}
// 返回统一错误对象
return Promise.reject(error)
}
1.4 高级处理场景
1.4.1 Token 过期自动刷新
401 状态码的典型处理场景是 token 过期自动刷新:
javascript复制let isRefreshing = false
let requests = []
function handleTokenExpired(error) {
const config = error.config
if (!isRefreshing) {
isRefreshing = true
return refreshToken()
.then(res => {
const newToken = res.token
setToken(newToken)
requests.forEach(cb => cb(newToken))
requests = []
return service(config)
})
.catch(() => {
clearToken()
router.push('/login')
})
.finally(() => {
isRefreshing = false
})
} else {
// 正在刷新 token,将请求存入队列
return new Promise(resolve => {
requests.push(token => {
config.headers['Authorization'] = `Bearer ${token}`
resolve(service(config))
})
})
}
}
1.4.2 错误重试机制
对于网络不稳定或服务端临时错误,可以实现自动重试:
javascript复制function retryRequest(error) {
const config = error.config
config.__retryCount = config.__retryCount || 0
if (config.__retryCount >= MAX_RETRY) {
return Promise.reject(error)
}
config.__retryCount += 1
// 使用指数退避算法
const delay = Math.pow(2, config.__retryCount) * 1000
return new Promise(resolve => {
setTimeout(() => resolve(service(config)), delay)
})
}
1.5 最佳实践与注意事项
- 错误边界处理:始终在拦截器中返回 Promise.reject,确保错误能被业务代码捕获
- 避免副作用:拦截器中不要直接修改全局状态(如直接跳转路由),应该通过返回特定错误让调用方决定如何处理
- 日志记录:关键错误应该记录到监控系统
- 用户提示:通用错误提示应该在拦截器中统一处理,特殊错误才由业务方处理
- 性能考虑:拦截器中不要做耗时操作,避免阻塞请求队列
2. Webpack 工程化配置中的状态码处理集成
在前端工程化体系中,HTTP 状态码处理不仅限于 axios 封装,还需要与构建工具和开发环境深度集成。
2.1 开发环境模拟
利用 webpack-dev-server 的 proxy 和 mock 功能,可以模拟各种状态码:
javascript复制// vue.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
bypass: function(req, res, proxyOptions) {
// 模拟 401 错误
if (req.url.includes('/unauthorized')) {
res.statusCode = 401
return res.json({ message: 'Unauthorized' })
}
// 模拟 500 错误
if (req.url.includes('/server-error')) {
res.statusCode = 500
return res.json({ message: 'Internal Server Error' })
}
}
}
}
}
}
2.2 构建时环境变量注入
通过 webpack.DefinePlugin 注入不同的状态码处理策略:
javascript复制new webpack.DefinePlugin({
'process.env': {
ERROR_HANDLING_STRATEGY: JSON.stringify(
process.env.NODE_ENV === 'production' ? 'strict' : 'relaxed'
)
}
})
然后在拦截器中使用:
javascript复制if (process.env.ERROR_HANDLING_STRATEGY === 'strict') {
// 生产环境严格模式
} else {
// 开发环境宽松模式
}
3. 企业级项目中的状态码监控体系
完善的 HTTP 状态码监控是保障应用稳定性的重要环节。
3.1 监控指标设计
| 指标名称 | 计算方式 | 告警阈值 |
|---|---|---|
| 5xx 错误率 | 5xx 数量 / 总请求数 × 100% | > 1% 持续5分钟 |
| 401 频次 | 单位时间内 401 次数 | > 10次/分钟 |
| 平均错误恢复时间 | 从错误发生到恢复的平均时间 | > 5分钟 |
3.2 实现方案
在响应拦截器中收集指标数据:
javascript复制function collectMetrics(error) {
const status = error.response?.status || 'network_error'
metrics.increment(`http.${status}`)
if (status >= 500) {
sentry.captureException(error)
}
}
4. 与 Vue 生态的深度集成
在 Vue 项目中,我们可以将 HTTP 状态码处理与 Vue 生态深度集成。
4.1 全局错误处理
javascript复制// main.js
Vue.config.errorHandler = function(err, vm, info) {
if (err.isAxiosError) {
handleHttpError(err)
}
}
app.config.globalProperties.$http = service
4.2 组合式 API 封装
javascript复制// useHttp.js
export function useHttp() {
const handleError = (error) => {
const status = error.response?.status
if (status === 401) {
useAuthStore().logout()
}
// ...
}
const get = (url, config) => {
return service.get(url, config).catch(handleError)
}
return { get, post }
}
在实际项目中,我发现将状态码处理逻辑分层设计最为有效:基础层处理技术性错误(网络问题、格式错误),业务层处理领域错误(订单不存在、库存不足),用户层处理展示逻辑(提示文案、跳转逻辑)。这种分层架构既保证了代码的复用性,又保持了足够的灵活性。