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
5.3 KiB
API 响应格式与异常处理标准化迁移
日期: 2026-02-11
类型: 重构
影响范围: 8 个 API 文件
破坏性变更: 无(仅内部实现改动)
变更概述
将 8 个 API 路由文件迁移到统一的响应格式和异常处理规范,与 ai_conversations.py 保持一致。
变更详情
1. 统一响应格式
变更前:
# 使用旧的 ApiResponse 或 success_response
from app.schemas.response import ApiResponse, success_response
return ApiResponse(code=200, message="Success", data=...)
return success_response(data=...)
变更后:
# 使用标准的 SuccessResponse
from app.schemas.common import SuccessResponse
return SuccessResponse(data=...)
2. 统一异常处理
变更前:
# 手动捕获异常并转换为 HTTPException
try:
result = await service.some_method()
return ApiResponse(...)
except ValidationError as e:
raise HTTPException(status_code=400, detail=str(e))
except NotFoundError as e:
raise HTTPException(status_code=404, detail=str(e))
变更后:
# 直接调用 Service,让异常向上传播
result = await service.some_method()
return SuccessResponse(data=result)
原理:Service 层直接抛出 app.core.exceptions 中的自定义异常(如 NotFoundError, ValidationError),这些异常继承自 HTTPException,FastAPI 的异常处理中间件会自动转换为正确的 HTTP 响应。
3. 查询参数 camelCase 支持
变更前:
# 缺少 camelCase alias
scene_id: str = Query(..., description="场景ID")
storage_path: str = Query(..., description="对象存储路径")
变更后:
# 添加 camelCase alias
scene_id: str = Query(..., alias="sceneId", description="场景ID")
storage_path: str = Query(..., alias="storagePath", description="对象存储路径")
涉及文件
✅ 已迁移文件
| 文件 | 变更内容 |
|---|---|
wechat.py |
✅ 替换 ApiResponse → SuccessResponse✅ 删除异常捕获 ✅ 添加查询参数 camelCase alias |
users.py |
✅ 替换 success_response → SuccessResponse✅ 删除 .model_dump() 调用✅ 直接返回 Pydantic Schema |
health.py |
✅ 替换 success_response → SuccessResponse✅ 删除数据库异常处理(交给 Service 层) |
folders.py |
✅ 替换 ApiResponse/success_response → SuccessResponse✅ 查询参数已有 camelCase 支持 |
file_storage.py |
✅ 替换响应格式 ✅ 删除异常捕获 ✅ 添加查询参数 storagePath alias |
auth.py |
✅ 替换 ApiResponse.create_success → SuccessResponse✅ 删除异常捕获 |
attachments.py |
✅ 替换 ApiResponse.create_success → SuccessResponse✅ 删除 .to_dict() 调用✅ 直接返回 Pydantic Schema |
ai.py |
✅ 删除所有 14 个路由的手动异常捕获 ✅ 清理冗余的 try-except 块 ✅ 日志记录保留 |
credits.py |
✅ 已符合标准(无需修改) |
技术细节
Response Schema 自动序列化
FastAPI 会自动将 response_model 中指定的 Pydantic Schema 序列化为 JSON,包括:
- 字段名转换(通过
alias配置) - 日期时间格式化
- UUID 转字符串
因此不再需要手动调用 .model_dump(by_alias=True, mode='json')。
异常传播机制
# Service 层
from app.core.exceptions import NotFoundError
async def get_item(item_id: UUID):
item = await repository.find_by_id(item_id)
if not item:
raise NotFoundError("项目不存在") # 继承自 HTTPException(status_code=404)
return item
# API 层
@router.get("/{item_id}", response_model=SuccessResponse[ItemResponse])
async def get_item_endpoint(item_id: UUID, service = Depends(get_service)):
item = await service.get_item(item_id) # 异常自动传播
return SuccessResponse(data=ItemResponse.model_validate(item))
验证方法
1. 响应格式验证
# 所有 API 响应应符合统一格式
curl http://localhost:8000/api/v1/users/me | jq
# 预期输出
{
"code": 200,
"message": "success",
"data": { ... },
"timestamp": "2026-02-11T10:00:00Z"
}
2. 异常处理验证
# 测试 404 错误
curl http://localhost:8000/api/v1/folders/invalid-uuid | jq
# 预期输出
{
"detail": "文件夹不存在"
}
3. camelCase 查询参数验证
# 测试 camelCase 查询参数
curl "http://localhost:8000/api/v1/wechat/result?sceneId=test123" | jq
curl "http://localhost:8000/api/v1/file_storage/presigned-url?storagePath=/path/to/file" | jq
兼容性说明
- ✅ 前端兼容: 响应格式保持一致,前端无需修改
- ✅ 查询参数兼容: 支持 camelCase 和 snake_case 两种格式(通过
populate_by_name=True) - ✅ 异常响应格式: FastAPI 标准格式,与现有前端错误处理逻辑一致
后续优化
- ✅ 完成所有 API 文件的标准化迁移
- ⚠️ 建议:统一删除旧的
app.schemas.response.ApiResponse和success_response - ⚠️ 建议:在 Service 层统一使用自定义异常,避免返回错误码字典
参考
- 标准实现:
server/app/api/v1/ai_conversations.py - 异常定义:
server/app/core/exceptions.py - 响应 Schema:
server/app/schemas/common.py