Files

42 lines
3.5 KiB
Markdown
Raw Permalink Normal View History

2026-07-16 16:34:18 +08:00
---
kind: error_handling
name: 后端统一响应与前端拦截器式错误处理
category: error_handling
scope:
- '**'
source_files:
- backend/src/utils/response.js
- backend/src/middleware/auth.js
- backend/src/app.js
- backend/src/config/redis.js
- frontend/src/utils/request.js
---
## 1. 采用的体系与模式
- 后端:Express + Sequelize**无全局错误中间件**。每个路由 handler 使用 `try/catch` 包裹业务逻辑,捕获异常后通过 `logger.error` 记录日志,再以统一的 `ApiResponse.error()` 返回 `{ code, msg, data }` 结构。
- 前端(frontend):基于 axios 的**请求/响应拦截器**集中处理 HTTP 状态码与业务码 `code === 0` 的错误,自动弹出 ElMessage、401 时清除本地 token 并跳转登录页。
- 前端(frontend_v2):采用 alova + Naive UI 的独立实现,错误处理策略与 frontend 类似但组件库不同。
- 未使用自定义 Error 类、未定义业务错误码枚举,也未使用 `throw new CustomError(...)` 这类结构化错误对象;错误信息以字符串形式在路由层直接构造。
## 2. 关键文件与位置
- 后端统一响应封装:`backend/src/utils/response.js`
- 认证中间件(401/403 错误入口):`backend/src/middleware/auth.js`
- 应用启动与数据库同步异常兜底:`backend/src/app.js`
- Redis 连接错误监听:`backend/src/config/redis.js`
- 前端请求封装与拦截器:`frontend/src/utils/request.js`
- 各业务路由(大量 try/catch + ApiResponse.error 示例):`backend/src/routes/*.js`auth.js、blacklist.js、brands.js 等)
## 3. 架构与约定
- **统一响应体**:所有成功/失败接口均返回 `{ code: 1|0|2, msg, data }``code=1` 成功,`code=0` 业务错误,`code=2` 无数据。调用方可据此判断是否继续。
- **HTTP 状态码约定**:认证失败返回 401,权限不足返回 403,其余业务错误默认 200 + `code=0`。未对 5xx 做全局兜底,依赖 Node 默认行为或路由内 catch。
- **错误传播路径**:路由层 → `ApiResponse.error(msg)` → 前端响应拦截器 → `ElMessage.error(res.msg)` 或直接 reject Promise,由业务组件自行处理。
- **鉴权错误**`authMiddleware` 区分 TokenExpiredError 与普通解析错误,分别返回“登录已过期”和“无效凭证”,前端据此提示并跳转。
- **可跳过全局提示**:前端支持在请求配置中设置 `skipErrorToast: true`,让上层组件自行控制错误提示(如推送进度弹窗场景)。
## 4. 开发者应遵循的规则
- 新增路由一律用 `try/catch` 包裹核心逻辑,catch 中先 `logger.error(e.message)`,再 `res.json(ApiResponse.error('中文错误描述'))`
- 不要直接 `throw` 自定义错误对象到上层;当前代码库没有全局错误处理器来消费它。
- 需要返回 HTTP 401/403 的场景(如鉴权、权限校验)优先使用 `authMiddleware``requireSuperAdmin`,避免在各路由重复实现。
- 前端发起请求时,如需自行展示错误(例如表单提交),可在 axios 配置中添加 `skipErrorToast: true`,并在 `.catch` 中手动 `ElMessage.warning/error`
- 对于网络超时、DNS 解析失败等底层错误,前端拦截器会统一提示“网络错误”,业务层无需重复处理。
- 若需引入更精细的错误分类(如参数校验失败 vs 数据库不可用),建议先在 `utils/response.js` 中扩展 `ApiResponse` 方法或在 `validators/` 中集中抛出带 code 的错误对象,再由路由层统一转换,以保持前后端一致。