You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

5.3 KiB

统一错误响应格式实现

日期: 2026-01-27
类型: 功能增强
影响范围: 全局 API 错误处理

变更概述

实现全局异常处理器,确保所有 API 错误响应符合统一的 ApiResponse 格式规范。

问题背景

在实现统一响应格式(RFC-135)后,发现错误响应仍使用 FastAPI 默认格式:

{
  "detail": "错误信息"
}

这与成功响应的 ApiResponse 格式不一致,导致前端需要处理两种不同的响应结构。

解决方案

1. 添加全局异常处理器

app/main.py 中添加三个异常处理器:

HTTPException 处理器

@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "success": False,
            "code": exc.status_code,
            "message": exc.detail,
            "data": None,
            "timestamp": datetime.now(timezone.utc).isoformat()
        }
    )

RequestValidationError 处理器

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    error_msg = "请求参数验证失败"
    if exc.errors():
        first_error = exc.errors()[0]
        field = " -> ".join(str(loc) for loc in first_error.get("loc", []))
        msg = first_error.get("msg", "")
        error_msg = f"{field}: {msg}"
    
    return JSONResponse(
        status_code=422,
        content={
            "success": False,
            "code": 422,
            "message": error_msg,
            "data": {"errors": exc.errors()},
            "timestamp": datetime.now(timezone.utc).isoformat()
        }
    )

通用异常处理器

@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception):
    logger.error(f"未处理的异常: {exc}", exc_info=True)
    
    return JSONResponse(
        status_code=500,
        content={
            "success": False,
            "code": 500,
            "message": "服务器内部错误" if not settings.DEBUG else str(exc),
            "data": None,
            "timestamp": datetime.now(timezone.utc).isoformat()
        }
    )

404 错误处理器

@app.exception_handler(404)
async def not_found_handler(request: Request, exc):
    return JSONResponse(
        status_code=404,
        content={
            "success": False,
            "code": 404,
            "message": "请求的资源不存在",
            "data": None,
            "timestamp": datetime.now(timezone.utc).isoformat()
        }
    )

测试验证

1. 业务逻辑错误(400)

curl -X POST "http://localhost:6170/api/v1/auth/login/phone" \
  -H "Content-Type: application/json" \
  -d '{"phone":"18046315592","countryCode":"+86","code":"000000"}'

响应:

{
  "success": false,
  "code": 400,
  "message": "验证码错误",
  "data": null,
  "timestamp": "2026-01-27T08:23:00.310624+00:00"
}

2. 参数验证错误(422)

curl -X POST "http://localhost:6170/api/v1/auth/login/phone" \
  -H "Content-Type: application/json" \
  -d '{"phone":"18046315592"}'

响应:

{
  "success": false,
  "code": 422,
  "message": "body -> code: Field required",
  "data": {
    "errors": [...]
  },
  "timestamp": "2026-01-27T08:23:12.388333+00:00"
}

3. 资源不存在(404)

curl -X GET "http://localhost:6170/api/v1/nonexistent"

响应:

{
  "success": false,
  "code": 404,
  "message": "请求的资源不存在",
  "data": null,
  "timestamp": "2026-01-27T08:24:07.692476+00:00"
}

4. 限流错误(429)

curl -X POST "http://localhost:6170/api/v1/auth/sms/send" \
  -H "Content-Type: application/json" \
  -d '{"phone":"15980906230","countryCode":"+86","purpose":"login"}'

响应:

{
  "success": false,
  "code": 429,
  "message": "今日发送次数已达上限",
  "data": null,
  "timestamp": "2026-01-27T08:21:32.130535+00:00"
}

影响范围

受影响的异常类型

  • ValidationError (400)
  • AuthenticationError (401)
  • PermissionError (403)
  • NotFoundError (404)
  • InsufficientCreditsError (402)
  • RateLimitError (429)
  • RequestValidationError (422)
  • 所有未捕获的异常 (500)

前端适配

前端现在可以统一处理所有 API 响应:

interface ApiResponse<T> {
  success: boolean;
  code: number;
  message: string;
  data: T | null;
  timestamp: string;
}

// 统一错误处理
if (!response.success) {
  showError(response.message);
}

技术细节

异常处理优先级

  1. 特定状态码处理器(如 404)
  2. HTTPException 处理器
  3. RequestValidationError 处理器
  4. 通用 Exception 处理器

日志记录

  • 500 错误会记录完整堆栈信息
  • DEBUG 模式下返回详细错误信息
  • 生产环境返回通用错误提示

相关文档

部署说明

无需数据库迁移,重启应用即可生效。

后续优化

  1. 添加错误码枚举,细化错误类型
  2. 支持国际化错误消息
  3. 添加错误追踪 ID(trace_id)