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

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

4.6 KiB
Raw Blame History

异常处理规范

最后更新2025-12-25 本文档定义前后端统一的异常处理策略


一、设计原则

1.1 核心目标

  1. 用户友好:错误信息对用户清晰易懂
  2. 调试便捷:保留足够的日志信息用于排查问题
  3. 一致性:前后端采用统一的错误响应格式

1.2 HTTP 状态码策略

场景 HTTP状态码 业务码 说明
登录失败(密码错误) 200 400 便于前端友好提示
Token无效/过期 401 - 触发前端自动登出
权限不足 403 - 标准HTTP语义
资源不存在 404 - 标准HTTP语义
服务器错误 500 - 标准HTTP语义

二、后端异常处理

2.1 统一响应格式

# app/schemas/base.py
class ResponseModel(BaseModel):
    code: int = 200          # 业务状态码
    message: str = "success" # 业务消息
    data: Any = None         # 响应数据

2.2 登录异常处理

设计决策:登录失败返回 HTTP 200 + 业务错误码

# 正确做法
@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 全局异常处理

# 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 错误拦截

// 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 错误信息展示

// 业务错误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