# 错误处理架构说明 ## 📐 架构设计 ### 设计原则 1. **关注点分离(Separation of Concerns)** - `main.py`:应用配置和启动 - `core/error_handlers.py`:错误处理逻辑 - 各层职责清晰,互不干扰 2. **单一职责原则(Single Responsibility Principle)** - 每个函数只做一件事 - 易于测试和维护 3. **开放封闭原则(Open-Closed Principle)** - 对扩展开放:可以轻松添加新的错误类型 - 对修改封闭:不需要修改核心代码 4. **安全优先(Security First)** - 不暴露技术细节 - 环境隔离(开发/生产) - 详细日志仅在服务端 ## 🏗️ 架构层次 ``` ┌─────────────────────────────────────────────────────────┐ │ Client (前端) │ └────────────────────┬────────────────────────────────────┘ │ HTTP Request ↓ ┌─────────────────────────────────────────────────────────┐ │ FastAPI Application │ │ ┌───────────────────────────────────────────────────┐ │ │ │ Middleware Stack │ │ │ │ • CORS Middleware │ │ │ │ • Logging Middleware │ │ │ │ • Request ID Middleware │ │ │ └───────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌───────────────────────────────────────────────────┐ │ │ │ Route Handlers │ │ │ │ • /api/v1/projects │ │ │ │ • /api/v1/folders │ │ │ │ • ... │ │ │ └───────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌───────────────────────────────────────────────────┐ │ │ │ Pydantic Validation │ │ │ │ • Schema validation │ │ │ │ • Type checking │ │ │ └───────────────────────────────────────────────────┘ │ │ ↓ │ │ ❌ Exception? │ │ ↓ │ │ ┌───────────────────────────────────────────────────┐ │ │ │ Exception Handlers (error_handlers.py) │ │ │ │ │ │ │ │ ┌──────────────────────────────────────────────┐ │ │ │ │ │ 1. Extract Error Information │ │ │ │ │ │ • Field path │ │ │ │ │ │ • Error type │ │ │ │ │ │ • Context │ │ │ │ │ └──────────────────────────────────────────────┘ │ │ │ │ ↓ │ │ │ │ ┌──────────────────────────────────────────────┐ │ │ │ │ │ 2. Transform to Friendly Message │ │ │ │ │ │ • Hide technical details │ │ │ │ │ │ • Use user-friendly language │ │ │ │ │ │ • Provide suggestions │ │ │ │ │ └──────────────────────────────────────────────┘ │ │ │ │ ↓ │ │ │ │ ┌──────────────────────────────────────────────┐ │ │ │ │ │ 3. Create Error Response │ │ │ │ │ │ • Uniform format │ │ │ │ │ │ • Proper status code │ │ │ │ │ │ • Timestamp │ │ │ │ │ └──────────────────────────────────────────────┘ │ │ │ │ ↓ │ │ │ │ ┌──────────────────────────────────────────────┐ │ │ │ │ │ 4. Log (Development Only) │ │ │ │ │ │ • Detailed error info │ │ │ │ │ │ • Request path │ │ │ │ │ │ • Stack trace │ │ │ │ │ └──────────────────────────────────────────────┘ │ │ │ │ │ │ │ └───────────────────────────────────────────────────┘ │ │ ↓ │ └────────────────────┬────────────────────────────────────┘ │ JSON Response ↓ ┌─────────────────────────────────────────────────────────┐ │ Client (前端) │ │ • Display error message │ │ • Show field-level errors │ │ • User-friendly UI feedback │ └─────────────────────────────────────────────────────────┘ ``` ## 📦 模块职责 ### 1. `app/main.py` - 应用入口 **职责**: - 创建 FastAPI 应用实例 - 配置中间件 - 注册异常处理器 - 注册路由 - 应用生命周期管理 **示例**: ```python from app.core.error_handlers import register_exception_handlers app = FastAPI(...) # 配置中间件 setup_cors(app) app.add_middleware(LoggingMiddleware) app.add_middleware(RequestIdMiddleware) # 注册异常处理器(一行代码) register_exception_handlers(app) # 注册路由 app.include_router(api_router, prefix="/api/v1") ``` --- ### 2. `app/core/error_handlers.py` - 错误处理核心 **职责**: - 定义错误类型映射 - 转换技术错误为友好消息 - 处理各类异常 - 创建统一响应格式 - 提供注册函数 **核心函数**: #### `get_friendly_error_message(error: Dict) -> str` 将 Pydantic 验证错误转换为用户友好的消息 ```python # 输入(Pydantic 错误) { "type": "uuid_parsing", "msg": "Invalid UUID...", "input": "virtual-mine" } # 输出(友好消息) "必须是有效的 UUID 格式,或者不传此字段" ``` #### `extract_field_path(loc: List) -> str` 从错误位置提取字段路径 ```python # 输入 ["body", "user", "profile", "email"] # 输出 "user -> profile -> email" ``` #### `create_error_response(...) -> JSONResponse` 创建统一格式的错误响应 ```python return { "success": False, "code": 422, "message": "参数验证失败", "data": { "errors": [ {"field": "folderId", "message": "..."} ] }, "timestamp": "2026-02-05T10:00:00Z" } ``` #### `validation_exception_handler(...)` 处理 Pydantic 验证错误 **流程**: 1. 遍历所有验证错误 2. 提取字段路径 3. 转换为友好消息 4. 构建错误列表 5. 开发环境记录详细日志 6. 返回统一响应 #### `register_exception_handlers(app)` 注册所有异常处理器 ```python app.add_exception_handler(404, not_found_handler) app.add_exception_handler(HTTPException, http_exception_handler) app.add_exception_handler(RequestValidationError, validation_exception_handler) app.add_exception_handler(Exception, general_exception_handler) ``` --- ### 3. `app/schemas/` - 数据验证 **职责**: - 定义 API Schema - 使用 Pydantic 进行验证 - 提供别名映射(snake_case ↔️ camelCase) **示例**: ```python class ProjectCreate(BaseModel): name: str = Field(..., min_length=1, max_length=255) folder_id: Optional[UUID] = Field(None, alias="folderId") planned_duration: Optional[int] = Field(None, alias="plannedDuration", gt=0) class Config: populate_by_name = True ``` --- ## 🔄 错误处理流程 ### 场景 1: UUID 格式错误 ``` Client Request: POST /api/v1/projects { "name": "项目", "folderId": "virtual-mine" // ❌ 不是 UUID } ↓ Pydantic Validation: ❌ ValidationError: uuid_parsing ↓ validation_exception_handler(): 1. Extract: field="folderId", type="uuid_parsing" 2. Transform: "必须是有效的 UUID 格式,或者不传此字段" 3. Create response ↓ Client Response: { "success": false, "code": 422, "message": "folderId: 必须是有效的 UUID 格式,或者不传此字段", "data": { "errors": [ { "field": "folderId", "message": "必须是有效的 UUID 格式,或者不传此字段" } ] } } ``` ### 场景 2: 多个字段错误 ``` Client Request: POST /api/v1/projects { "name": "", // ❌ 长度不足 "folderId": "invalid", // ❌ 不是 UUID "plannedDuration": 0 // ❌ 必须大于 0 } ↓ Pydantic Validation: ❌ ValidationError: [string_too_short, uuid_parsing, greater_than] ↓ validation_exception_handler(): 1. Process all 3 errors 2. Transform each error 3. Create error list 4. Build main message: "参数验证失败,共 3 个错误" ↓ Client Response: { "success": false, "code": 422, "message": "参数验证失败,共 3 个错误", "data": { "errors": [ {"field": "name", "message": "长度不能少于 1 个字符"}, {"field": "folderId", "message": "必须是有效的 UUID 格式,或者不传此字段"}, {"field": "plannedDuration", "message": "必须大于 0,或者不传此字段"} ] } } ``` --- ## 🔐 安全特性 ### 1. 信息隐藏 **隐藏内容**: - ❌ Pydantic 错误类型(`uuid_parsing`, `greater_than`) - ❌ 框架版本和文档链接 - ❌ 验证规则细节(`ctx`) - ❌ 内部错误堆栈 **保留内容**: - ✅ 字段名称 - ✅ 用户友好的错误说明 - ✅ 修复建议 ### 2. 环境隔离 ```python if settings.DEBUG: # 开发环境:记录详细日志 logger.warning(f"验证错误详情: {exc.errors()}") else: # 生产环境:简洁安全 pass # 返回给客户端的始终是友好消息 return create_error_response(...) ``` ### 3. 日志记录 ```python # 服务端日志(不返回给客户端) logger.error( f"未处理的异常 - Path: {request.url.path} - " f"错误: {exc}", exc_info=True # 包含完整堆栈信息 ) # 客户端响应(隐藏细节) return { "message": "服务器内部错误" # 不暴露异常详情 } ``` --- ## 🧪 测试策略 ### 1. 单元测试 测试各个函数的功能: ```python # tests/unit/test_error_handlers.py def test_extract_field_path(): assert extract_field_path(["body", "name"]) == "name" def test_get_friendly_error_message(): error = {"type": "uuid_parsing"} message = get_friendly_error_message(error) assert "UUID" in message ``` ### 2. 集成测试 测试完整的错误处理流程: ```python # tests/integration/test_project_api.py async def test_create_project_invalid_uuid(): response = await client.post( "/api/v1/projects", json={"name": "test", "folderId": "invalid"} ) assert response.status_code == 422 data = response.json() assert "UUID" in data["message"] ``` ### 3. 异常处理器测试 测试异常处理器的注册和执行: ```python def test_register_exception_handlers(): app = FastAPI() register_exception_handlers(app) assert 404 in app.exception_handlers assert HTTPException in app.exception_handlers ``` --- ## 📈 扩展指南 ### 添加新的错误类型 1. **在 `ERROR_TYPE_MESSAGES` 中添加映射** ```python ERROR_TYPE_MESSAGES = { "uuid_parsing": "必须是有效的 UUID 格式,或者不传此字段", "email_invalid": "邮箱格式不正确", # ✅ 新增 } ``` 2. **在 `get_friendly_error_message` 中处理特殊逻辑** ```python def get_friendly_error_message(error: Dict[str, Any]) -> str: error_type = error.get("type", "") # 特殊处理 if error_type == "email_invalid": return "请输入有效的邮箱地址,如:user@example.com" # ... 其他逻辑 ``` ### 添加自定义异常处理器 ```python # 定义自定义异常 class BusinessException(Exception): def __init__(self, message: str, code: int = 400): self.message = message self.code = code # 添加处理器 async def business_exception_handler(request: Request, exc: BusinessException): return create_error_response( status_code=exc.code, message=exc.message ) # 注册 def register_exception_handlers(app): # ... 其他处理器 ... app.add_exception_handler(BusinessException, business_exception_handler) ``` --- ## 📊 性能优化 ### 1. 错误消息缓存 ```python from functools import lru_cache @lru_cache(maxsize=128) def get_error_message_template(error_type: str) -> str: """缓存常用错误消息模板""" return ERROR_TYPE_MESSAGES.get(error_type, "参数验证失败") ``` ### 2. 延迟日志记录 ```python if settings.DEBUG and logger.isEnabledFor(logging.WARNING): # 只在需要时才格式化日志字符串 logger.warning("验证错误详情: %s", exc.errors()) ``` --- ## ✅ 总结 ### 架构优势 | 维度 | 优势 | |------|------| | **可维护性** | 代码集中,易于修改 | | **可测试性** | 独立模块,易于测试 | | **可扩展性** | 开放封闭,易于扩展 | | **安全性** | 隐藏细节,保护系统 | | **用户体验** | 友好提示,清晰易懂 | | **性能** | 高效处理,低开销 | ### 设计模式 1. **策略模式**:不同错误类型使用不同的转换策略 2. **工厂模式**:`create_error_response` 创建统一格式响应 3. **外观模式**:`register_exception_handlers` 简化注册过程 4. **模板方法模式**:统一的错误处理流程 ### 最佳实践 ✅ 单一职责 ✅ 依赖注入 ✅ 环境隔离 ✅ 安全优先 ✅ 用户友好 ✅ 可测试性 ✅ 可扩展性 这是一个生产级的错误处理架构!