Files
xiaoxia-saas/apps/api/app/middleware/exceptions.py
T
Xiaoxia AI 4495d0c53d
Deploy / Deploy Staging (push) Failing after 6s
Deploy / Deploy Production (push) Has been skipped
Tests / test (push) Failing after 30s
Tests / lint (push) Failing after 27s
feat(middleware): add comprehensive error handling and logging
- Add APIException with custom error codes
- Implement global exception handlers (API/HTTP/Validation/General)
- Add RequestLoggingMiddleware with response time tracking
- Add RateLimitMiddleware (in-memory rate limiting)
- Integrate all middleware into main app
- Return consistent JSON error responses
- Add X-Process-Time and X-RateLimit headers

Phase 4 Task 33/68 completed
2026-06-17 08:14:47 +08:00

136 lines
3.6 KiB
Python

"""
全局异常处理和错误响应
"""
from fastapi import Request, status
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException
import traceback
import logging
logger = logging.getLogger(__name__)
class APIException(Exception):
"""API 异常基类"""
def __init__(
self,
message: str,
status_code: int = status.HTTP_400_BAD_REQUEST,
error_code: str = "API_ERROR",
):
self.message = message
self.status_code = status_code
self.error_code = error_code
super().__init__(message)
class AuthenticationError(APIException):
"""认证错误"""
def __init__(self, message: str = "Authentication failed"):
super().__init__(
message=message,
status_code=status.HTTP_401_UNAUTHORIZED,
error_code="AUTH_ERROR",
)
class PermissionDeniedError(APIException):
"""权限拒绝"""
def __init__(self, message: str = "Permission denied"):
super().__init__(
message=message,
status_code=status.HTTP_403_FORBIDDEN,
error_code="PERMISSION_DENIED",
)
class ResourceNotFoundError(APIException):
"""资源不存在"""
def __init__(self, resource: str = "Resource"):
super().__init__(
message=f"{resource} not found",
status_code=status.HTTP_404_NOT_FOUND,
error_code="NOT_FOUND",
)
class ValidationError(APIException):
"""验证错误"""
def __init__(self, message: str):
super().__init__(
message=message,
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
error_code="VALIDATION_ERROR",
)
async def api_exception_handler(request: Request, exc: APIException):
"""API 异常处理"""
return JSONResponse(
status_code=exc.status_code,
content={
"error": {
"code": exc.error_code,
"message": exc.message,
}
},
)
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
"""HTTP 异常处理"""
return JSONResponse(
status_code=exc.status_code,
content={
"error": {
"code": f"HTTP_{exc.status_code}",
"message": exc.detail,
}
},
)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
"""请求验证异常处理"""
errors = []
for error in exc.errors():
errors.append({
"field": ".".join(str(loc) for loc in error["loc"]),
"message": error["msg"],
"type": error["type"],
})
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content={
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": errors,
}
},
)
async def general_exception_handler(request: Request, exc: Exception):
"""通用异常处理"""
logger.error(f"Unhandled exception: {exc}", exc_info=True)
# 生产环境不返回详细错误信息
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={
"error": {
"code": "INTERNAL_ERROR",
"message": "An internal error occurred",
# "detail": str(exc), # 仅在开发环境启用
}
},
)