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.8 KiB

ADR 011: API 响应格式标准化

状态

已接受 (Accepted) - 2026-02-05

背景

在代码审查过程中,发现 API 响应格式存在不一致的问题:

发现的问题

  1. 文档不一致

    • api-design.md 第 90-96 行定义了完整的响应格式(5 个字段)
    • 但第 617-628 行及其他多处示例缺少 successtimestamp 字段
  2. Schema 定义不完整

    • schemas/common.py 中的 SuccessResponse 只有 3 个字段(code, message, data)
    • 缺少 successtimestamp 字段
    • 导致部分 API(如 credits.py)返回不完整的响应
  3. 前端已兼容

    • client/src/services/api/client.ts 已经支持完整格式
    • 响应拦截器正确处理 5 字段格式(第 48 行)
    • 并兼容旧的 3 字段格式(第 66 行)

正确的响应格式

成功响应

{
  "success": true,
  "code": 200,
  "message": "Success",
  "data": {...},
  "timestamp": "2026-02-05T10:30:00+00:00"
}

错误响应

{
  "success": false,
  "code": 400,
  "message": "错误消息",
  "data": null,
  "timestamp": "2026-02-05T10:30:00+00:00"
}

决策

统一所有 API 响应格式为 5 字段格式

  1. success (boolean) - 请求是否成功
  2. code (number) - 业务状态码
  3. message (string) - 响应消息
  4. data (T | null) - 业务数据
  5. timestamp (string) - ISO 8601 格式的响应时间戳

实施

1. 修复文档

已修复.claude/skills/jointo-tech-stack/references/api-design.md

  • 第 617-628 行:分页响应示例
  • 第 352-366 行:邮箱登录响应
  • 第 380-389 行:发送验证码响应
  • 第 403-418 行:手机号登录响应
  • 第 428-437 行:微信二维码响应
  • 第 446-455 行:微信登录(等待扫码)
  • 第 458-472 行:微信登录(成功)
  • 第 490-497 行:未认证响应
  • 第 499-506 行:权限不足响应
  • 第 660-680 行:项目列表响应
  • 第 700-710 行:创建项目响应
  • 第 733-740 行:删除项目响应
  • 第 788-799 行:积分余额响应
  • 第 809-830 行:积分交易记录响应
  • 第 849-861 行:上传附件响应
  • 第 872-880 行:下载链接响应
  • 第 906-917 行:创建充值订单响应
  • 第 928-940 行:查询订单状态响应

2. 修复 Schema 定义

已修复server/app/schemas/common.py

class SuccessResponse(BaseModel, Generic[T]):
    """统一成功响应格式(完整版本)"""
    success: bool = Field(default=True, description="请求是否成功")
    code: int = Field(default=200, description="业务状态码")
    message: str = Field(default="Success", description="响应消息")
    data: Optional[T] = Field(default=None, description="响应数据")
    timestamp: datetime = Field(
        default_factory=lambda: datetime.now(timezone.utc),
        description="响应时间戳"
    )
    
    @field_serializer('timestamp')
    def serialize_timestamp(self, value: datetime) -> str:
        return value.isoformat()

3. API 路由现状

已确认正确

  • api/v1/projects.py - 使用 success_response()
  • api/v1/folders.py - 使用 success_response()
  • api/v1/auth.py - 使用 success_response()
  • api/v1/credits.py - 使用 SuccessResponse() (已修复 Schema)

4. 前端兼容性

已确认client/src/services/api/client.ts

前端响应拦截器已正确处理:

  • 优先检查新格式(5 字段,第 48 行)
  • 兼容旧格式(3 字段,第 66 行)
  • 正确处理错误响应(第 96 行)

影响

正面影响

  1. 统一性:所有 API 响应格式一致,前端处理更简单
  2. 可追踪性timestamp 字段便于排查问题
  3. 明确性success 字段明确表示请求成功/失败
  4. 兼容性:前端已兼容新旧格式,无破坏性变更

需要注意

  1. 新 API 开发:必须使用完整的 5 字段格式
  2. 旧 API 迁移:逐步迁移使用不完整格式的旧接口
  3. 测试用例:更新测试用例验证响应格式

实施指南

后端开发

推荐方式 1:使用便捷函数

from app.schemas.response import success_response

@router.get("/example")
async def example():
    return success_response(data={"key": "value"})

推荐方式 2:使用 Pydantic 模型(推荐用于有明确类型的场景)

from app.schemas.common import SuccessResponse

@router.get("/example", response_model=SuccessResponse[MyResponse])
async def example():
    return SuccessResponse(data=my_data)

禁止方式:手动构建不完整的字典

# ❌ 错误:缺少 success 和 timestamp
return {"code": 200, "message": "Success", "data": data}

前端开发

前端无需修改,响应拦截器已正确处理:

// 自动提取 data 字段
const projects = await projectApi.getAll(); // 直接得到 data 内容

// 错误自动抛出异常
try {
  await api.call();
} catch (error) {
  console.error(error.message); // 来自 response.message
}

检查清单

创建新 API 时必须确认:

  • 使用 success_response()SuccessResponse 返回
  • response_model 使用 ApiResponse[T]SuccessResponse[T]
  • 响应包含 5 个必需字段:success, code, message, data, timestamp
  • 错误响应也遵循相同格式
  • 更新 API 文档示例

相关文档

  • .claude/skills/jointo-tech-stack/references/api-design.md - API 设计规范
  • server/app/schemas/response.py - 响应格式定义
  • server/app/schemas/common.py - 通用响应模型
  • client/src/services/api/client.ts - 前端响应拦截器

历史

  • 2026-02-05: 创建 ADR,修复文档和 Schema 定义