VWED_server/middlewares/error_handlers.py
2025-07-14 10:29:37 +08:00

130 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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)