""" 统一异常处理 捕获所有异常,返回统一格式的错误响应,包含 TraceID。 """ import logging import traceback from typing import Union from fastapi import FastAPI, Request, HTTPException from fastapi.responses import JSONResponse from fastapi.exceptions import RequestValidationError from starlette.exceptions import HTTPException as StarletteHTTPException from .trace import get_trace_id logger = logging.getLogger(__name__) class ErrorCode: """错误码常量""" BAD_REQUEST = "BAD_REQUEST" UNAUTHORIZED = "UNAUTHORIZED" FORBIDDEN = "FORBIDDEN" NOT_FOUND = "NOT_FOUND" VALIDATION_ERROR = "VALIDATION_ERROR" RATE_LIMITED = "RATE_LIMITED" INTERNAL_ERROR = "INTERNAL_ERROR" SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE" GATEWAY_ERROR = "GATEWAY_ERROR" STATUS_TO_ERROR_CODE = { 400: ErrorCode.BAD_REQUEST, 401: ErrorCode.UNAUTHORIZED, 403: ErrorCode.FORBIDDEN, 404: ErrorCode.NOT_FOUND, 422: ErrorCode.VALIDATION_ERROR, 429: ErrorCode.RATE_LIMITED, 500: ErrorCode.INTERNAL_ERROR, 502: ErrorCode.GATEWAY_ERROR, 503: ErrorCode.SERVICE_UNAVAILABLE, } def create_error_response( status_code: int, code: str, message: str, trace_id: str = None, details: dict = None ) -> JSONResponse: """创建统一格式的错误响应""" if trace_id is None: trace_id = get_trace_id() error_body = { "code": code, "message": message, "trace_id": trace_id } if details: error_body["details"] = details return JSONResponse( status_code=status_code, content={"success": False, "error": error_body}, headers={"X-Trace-ID": trace_id} ) async def http_exception_handler(request: Request, exc: Union[HTTPException, StarletteHTTPException]): """处理 HTTP 异常""" trace_id = get_trace_id() status_code = exc.status_code error_code = STATUS_TO_ERROR_CODE.get(status_code, ErrorCode.INTERNAL_ERROR) message = exc.detail if isinstance(exc.detail, str) else str(exc.detail) logger.warning(f"[{trace_id}] HTTP {status_code}: {message}") return create_error_response( status_code=status_code, code=error_code, message=message, trace_id=trace_id ) async def validation_exception_handler(request: Request, exc: RequestValidationError): """处理请求验证错误""" trace_id = get_trace_id() errors = exc.errors() error_messages = [f"{'.'.join(str(l) for l in e['loc'])}: {e['msg']}" for e in errors] logger.warning(f"[{trace_id}] 验证错误: {error_messages}") return create_error_response( status_code=422, code=ErrorCode.VALIDATION_ERROR, message="请求参数验证失败", trace_id=trace_id, details={"validation_errors": error_messages} ) async def generic_exception_handler(request: Request, exc: Exception): """处理所有未捕获的异常""" trace_id = get_trace_id() logger.error(f"[{trace_id}] 未捕获异常: {type(exc).__name__}: {exc}") logger.error(f"[{trace_id}] 堆栈:\n{traceback.format_exc()}") return create_error_response( status_code=500, code=ErrorCode.INTERNAL_ERROR, message="服务器内部错误,请稍后重试", trace_id=trace_id ) def setup_exception_handlers(app: FastAPI): """配置 FastAPI 应用的异常处理器""" app.add_exception_handler(HTTPException, http_exception_handler) app.add_exception_handler(StarletteHTTPException, http_exception_handler) app.add_exception_handler(RequestValidationError, validation_exception_handler) app.add_exception_handler(Exception, generic_exception_handler) logger.info("异常处理器已配置")