VWED_server/middlewares/error_handlers.py

130 lines
4.4 KiB
Python
Raw Normal View History

2025-07-14 10:29:37 +08:00
#!/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)