Files
012-kaopeilian/docs/规划/全链路联调/异常处理规范.md
111 998211c483 feat: 初始化考培练系统项目
- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
2026-01-24 19:33:28 +08:00

194 lines
4.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 异常处理规范
> 最后更新2025-12-25
> 本文档定义前后端统一的异常处理策略
---
## 一、设计原则
### 1.1 核心目标
1. **用户友好**:错误信息对用户清晰易懂
2. **调试便捷**:保留足够的日志信息用于排查问题
3. **一致性**:前后端采用统一的错误响应格式
### 1.2 HTTP 状态码策略
| 场景 | HTTP状态码 | 业务码 | 说明 |
|------|-----------|--------|------|
| 登录失败(密码错误) | 200 | 400 | 便于前端友好提示 |
| Token无效/过期 | 401 | - | 触发前端自动登出 |
| 权限不足 | 403 | - | 标准HTTP语义 |
| 资源不存在 | 404 | - | 标准HTTP语义 |
| 服务器错误 | 500 | - | 标准HTTP语义 |
---
## 二、后端异常处理
### 2.1 统一响应格式
```python
# app/schemas/base.py
class ResponseModel(BaseModel):
code: int = 200 # 业务状态码
message: str = "success" # 业务消息
data: Any = None # 响应数据
```
### 2.2 登录异常处理
**设计决策**:登录失败返回 HTTP 200 + 业务错误码
```python
# 正确做法
@router.post("/login")
async def login(login_data: LoginRequest):
try:
user, token = await auth_service.login(...)
return ResponseModel(data={...})
except UnauthorizedError as e:
# 记录日志
logger.warning("login_failed", username=login_data.username)
# 返回 HTTP 200 + 业务失败码
return ResponseModel(
code=400,
message="用户名或密码错误",
data=None,
)
```
**原因说明**
- 避免浏览器弹出 HTTP 401 认证对话框
- 前端可以统一处理业务错误,展示友好提示
- 区分"未登录"(401)和"登录失败"(200+400)的语义
### 2.3 全局异常处理
```python
# app/main.py
@app.exception_handler(Exception)
async def global_exception_handler(request, exc):
logger.error(f"未处理的异常: {exc}", exc_info=True)
return JSONResponse(
status_code=500,
content={
"code": 500,
"message": "内部服务器错误",
"detail": str(exc) if settings.DEBUG else None,
},
)
```
---
## 三、前端异常处理
### 3.1 HTTP 错误拦截
```typescript
// src/api/request.ts
} catch (error) {
const errorInfo = handleHttpError(error)
// 401 统一处理:清理认证状态并重定向
try {
const status = (errorInfo as any)?.status || (error as any)?.status
if (status === 401) {
console.warn('[Auth] Token过期或无效正在清理认证状态', { url, status })
localStorage.removeItem('access_token')
localStorage.removeItem('refresh_token')
localStorage.removeItem('current_user')
if (!location.pathname.startsWith('/login')) {
console.info('[Auth] 重定向到登录页')
location.href = '/login'
}
}
} catch (authError) {
// 认证处理过程中的异常需要记录,但不影响主流程
console.error('[Auth] 处理401错误时发生异常:', authError)
}
throw errorInfo
}
```
### 3.2 日志规范
| 级别 | 使用场景 | 示例 |
|------|---------|------|
| `console.error` | 程序错误、异常 | 网络错误、解析失败 |
| `console.warn` | 预期内的失败 | Token过期、密码错误 |
| `console.info` | 关键操作记录 | 登录成功、页面跳转 |
| `console.log` | 开发调试(生产禁用) | 变量打印 |
### 3.3 错误信息展示
```typescript
// 业务错误code !== 200
if (response.code !== 200) {
ElMessage.error(response.message || '操作失败')
}
// HTTP 错误
catch (error) {
ElMessage.error(error.message || '网络请求失败')
}
```
---
## 四、最佳实践
### 4.1 DO推荐
- ✅ 使用统一的 ResponseModel 格式
- ✅ 异常处理中添加日志记录
- ✅ 区分用户提示信息和调试信息
- ✅ 401 错误自动清理认证状态
### 4.2 DON'T避免
- ❌ 静默吞掉异常 `catch (_) {}`
- ❌ 在用户提示中暴露技术细节
- ❌ 忘记处理边界情况(网络超时等)
- ❌ 生产环境使用 console.log
---
## 五、错误码对照表
| 业务码 | 含义 | 前端处理 |
|--------|------|---------|
| 200 | 成功 | 正常流程 |
| 400 | 业务失败(如密码错误) | 显示 message |
| 401 | 未认证 | 跳转登录页 |
| 403 | 无权限 | 显示无权限提示 |
| 404 | 资源不存在 | 显示不存在提示 |
| 500 | 服务器错误 | 显示通用错误 |
---
## 六、变更记录
| 日期 | 内容 | 作者 |
|------|------|------|
| 2025-12-25 | 初始版本,明确登录异常处理策略 | AI Assistant |