#!/usr/bin/env python # -*- coding: utf-8 -*- """ 错误处理中间件模块 提供全局异常处理和错误响应格式化 """ from fastapi import Request, HTTPException from fastapi.responses import JSONResponse from fastapi.exceptions import RequestValidationError import traceback from utils.logger import get_logger from routes.common_api import format_response from config.error_messages import VALIDATION_ERROR_MESSAGES, HTTP_ERROR_MESSAGES from config.settings import settings # 设置日志 logger = get_logger("middleware.error_handlers") async def validation_exception_handler(request: Request, exc: RequestValidationError): """ 处理验证错误,将错误消息转换为中文,并提供更友好的错误提示 包括显示具体缺失的字段名称 """ errors = exc.errors() error_details = [] missing_fields = [] for error in errors: error_type = error.get("type", "") loc = error.get("loc", []) # 获取完整的字段路径,排除body/query等 if len(loc) > 1 and loc[0] in ["body", "query", "path", "header"]: field_path = ".".join(str(item) for item in loc[1:]) else: field_path = ".".join(str(item) for item in loc) # 获取中文错误消息 message = VALIDATION_ERROR_MESSAGES.get(error_type, error.get("msg", "验证错误")) # 替换消息中的参数 context = error.get("ctx", {}) for key, value in context.items(): message = message.replace(f"{{{key}}}", str(value)) # 收集缺失字段 if error_type == "missing" or error_type == "value_error.missing": missing_fields.append(field_path) error_details.append({ "field": field_path, "message": message, "type": error_type }) # 构建友好的错误响应 if missing_fields: missing_fields_str = ", ".join(missing_fields) error_message = f"缺少必填字段: {missing_fields_str}" elif error_details: # 提取第一个错误的字段和消息 first_error = error_details[0] error_message = f"参数 '{first_error['field']}' 验证失败: {first_error['message']}" else: error_message = "参数验证失败" # 记录参数验证错误,以便同步到主系统 logger.warning(f"参数验证失败: {error_message} - 请求路径: {request.url.path}") return JSONResponse( status_code=400, content={ "code": 400, "message": error_message, "data": error_details if len(error_details) > 1 else None } ) async def http_exception_handler(request: Request, exc: HTTPException): """处理HTTP异常,转换为统一的响应格式""" status_code = exc.status_code # 获取错误消息,优先使用自定义消息,否则使用配置中的错误消息 message = exc.detail if isinstance(message, str) and message == "Not Found": message = HTTP_ERROR_MESSAGES.get(status_code, message) # 记录HTTP错误日志,以便同步到主系统 if status_code >= 500: # 5xx错误记录为ERROR级别 logger.error(f"HTTP {status_code} 错误: {message} - 请求路径: {request.url.path}") elif status_code >= 400: # 4xx错误记录为WARNING级别 logger.warning(f"HTTP {status_code} 警告: {message} - 请求路径: {request.url.path}") return JSONResponse( status_code=status_code, content=format_response( code=status_code, message=message, data=None ) ) async def global_exception_handler(request: Request, exc: Exception): """处理所有未捕获的异常""" logger.error(f"未捕获异常: {str(exc)}") logger.error(traceback.format_exc()) return JSONResponse( status_code=500, content=format_response( code=500, message="服务器内部错误,请联系管理员", data=None if not settings.DEBUG else str(exc) ) ) def register_exception_handlers(app): """ 注册所有异常处理器到FastAPI应用 Args: app: FastAPI应用实例 """ app.exception_handler(RequestValidationError)(validation_exception_handler) app.exception_handler(HTTPException)(http_exception_handler) app.exception_handler(Exception)(global_exception_handler)