# API 响应格式与异常处理标准化迁移
**日期**: 2026-02-11
**类型**: 重构
**影响范围**: 8 个 API 文件
**破坏性变更**: 无(仅内部实现改动)
## 变更概述
将 8 个 API 路由文件迁移到统一的响应格式和异常处理规范,与 `ai_conversations.py` 保持一致。
## 变更详情
### 1. 统一响应格式
**变更前**:
```python
# 使用旧的 ApiResponse 或 success_response
from app.schemas.response import ApiResponse, success_response
return ApiResponse(code=200, message="Success", data=...)
return success_response(data=...)
```
**变更后**:
```python
# 使用标准的 SuccessResponse
from app.schemas.common import SuccessResponse
return SuccessResponse(data=...)
```
### 2. 统一异常处理
**变更前**:
```python
# 手动捕获异常并转换为 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))
```
**变更后**:
```python
# 直接调用 Service,让异常向上传播
result = await service.some_method()
return SuccessResponse(data=result)
```
**原理**:Service 层直接抛出 `app.core.exceptions` 中的自定义异常(如 `NotFoundError`, `ValidationError`),这些异常继承自 `HTTPException`,FastAPI 的异常处理中间件会自动转换为正确的 HTTP 响应。
### 3. 查询参数 camelCase 支持
**变更前**:
```python
# 缺少 camelCase alias
scene_id: str = Query(..., description="场景ID")
storage_path: str = Query(..., description="对象存储路径")
```
**变更后**:
```python
# 添加 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')`。
### 异常传播机制
```python
# 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. 响应格式验证
```bash
# 所有 API 响应应符合统一格式
curl http://localhost:8000/api/v1/users/me | jq
# 预期输出
{
"code": 200,
"message": "success",
"data": { ... },
"timestamp": "2026-02-11T10:00:00Z"
}
```
### 2. 异常处理验证
```bash
# 测试 404 错误
curl http://localhost:8000/api/v1/folders/invalid-uuid | jq
# 预期输出
{
"detail": "文件夹不存在"
}
```
### 3. camelCase 查询参数验证
```bash
# 测试 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 标准格式,与现有前端错误处理逻辑一致
## 后续优化
1. ✅ 完成所有 API 文件的标准化迁移
2. ⚠️ 建议:统一删除旧的 `app.schemas.response.ApiResponse` 和 `success_response`
3. ⚠️ 建议:在 Service 层统一使用自定义异常,避免返回错误码字典
## 参考
- 标准实现:`server/app/api/v1/ai_conversations.py`
- 异常定义:`server/app/core/exceptions.py`
- 响应 Schema:`server/app/schemas/common.py`