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.
 

4.4 KiB

RFC 135: 统一 API 响应格式

状态: 已实施
创建日期: 2026-01-26
作者: Kiro AI
类型: 重构

概述

实施统一的 API 响应格式,确保所有接口返回一致的数据结构,符合 api-design.md 规范要求。

背景

问题

当前所有 API 接口直接返回数据,未使用统一的响应包装格式:

# 当前实现
@router.get("/projects")
async def get_projects(...):
    return {"items": [...], "total": 100}  # 直接返回

规范要求

根据 api-design.md,所有接口应返回统一格式:

{
  "code": 200,
  "message": "Success",
  "data": {
    "items": [...],
    "total": 100
  }
}

解决方案

1. 创建统一响应模型

文件: server/app/schemas/response.py

from typing import Generic, TypeVar, Optional
from pydantic import BaseModel

T = TypeVar('T')

class ApiResponse(BaseModel, Generic[T]):
    """统一 API 响应格式"""
    code: int = 200
    message: str = "Success"
    data: Optional[T] = None

def success_response(data=None, message="Success", code=200):
    """便捷函数:创建成功响应"""
    return {"code": code, "message": message, "data": data}

def error_response(message, code=400, data=None, errors=None):
    """便捷函数:创建错误响应"""
    return {"code": code, "message": message, "data": data, "errors": errors}

2. 修改所有路由

修改前:

@router.get("", response_model=ProjectListResponse)
async def get_projects(...):
    result = await service.get_projects(...)
    return result

修改后:

from app.schemas.response import ApiResponse, success_response

@router.get("", response_model=ApiResponse[ProjectListResponse])
async def get_projects(...):
    result = await service.get_projects(...)
    return success_response(data=result)

实施范围

修改的文件

新增:

  • server/app/schemas/response.py - 统一响应模型

修改:

  • server/app/schemas/__init__.py - 导出响应模型
  • server/app/api/v1/health.py (2 个接口)
  • server/app/api/v1/auth.py (2 个接口)
  • server/app/api/v1/users.py (2 个接口)
  • server/app/api/v1/folders.py (15 个接口)
  • server/app/api/v1/projects.py (20 个接口)
  • server/app/api/v1/ai.py (10 个接口)

总计: 51+ 个接口

修改模式

  1. 添加导入:

    from app.schemas.response import ApiResponse, success_response
    
  2. 更新 response_model:

    response_model=ApiResponse[OriginalSchema]
    
  3. 包装返回值:

    return success_response(data=result)
    

响应格式示例

成功响应

单个对象:

{
  "code": 200,
  "message": "Success",
  "data": {
    "id": "123",
    "name": "项目名称"
  }
}

列表响应:

{
  "code": 200,
  "message": "Success",
  "data": {
    "items": [...],
    "total": 100,
    "page": 1,
    "pageSize": 20,
    "totalPages": 5
  }
}

简单消息:

{
  "code": 200,
  "message": "Success",
  "data": {
    "message": "操作成功"
  }
}

错误响应

{
  "code": 400,
  "message": "验证失败",
  "data": null,
  "errors": [
    {
      "field": "name",
      "message": "名称不能为空"
    }
  ]
}

影响分析

前端影响

修改前:

const response = await api.getProjects()
const projects = response.items  // 直接访问

修改后:

const response = await api.getProjects()
const projects = response.data.items  // 需要通过 data 访问

兼容性

  • 不向后兼容: 前端需要同步更新
  • 类型安全: TypeScript 会在编译时捕获错误
  • 统一处理: 前端可以统一处理响应格式

优势

  1. 一致性: 所有接口返回格式统一
  2. 可扩展: 易于添加全局字段(如 timestamptraceId
  3. 错误处理: 成功和错误响应格式一致
  4. 符合规范: 与 api-design.md 保持一致
  5. 类型安全: 泛型支持确保类型正确

后续工作

  1. 前端适配: 更新前端 API 调用代码
  2. 错误处理: 统一异常处理中间件
  3. 文档更新: 更新 API 文档示例
  4. 测试: 验证所有接口返回格式

参考

  • api-design.md - API 设计规范
  • FastAPI 文档 - Response Model
  • Pydantic 文档 - Generic Models