Merge branch 'refactor/backend-architecture' into development
This commit is contained in:
99
api/app/core/errors.py
Normal file
99
api/app/core/errors.py
Normal file
@@ -0,0 +1,99 @@
|
||||
"""
|
||||
全局异常体系。
|
||||
|
||||
只统一*错误*响应格式 {error_code, message, request_id}。
|
||||
成功响应直接返回 Pydantic model / FileResponse / 原始结构,不强制包装。
|
||||
"""
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class AppError(Exception):
|
||||
"""Base application exception."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str = "内部服务器错误",
|
||||
*,
|
||||
status_code: int = 500,
|
||||
error_code: str = "INTERNAL_ERROR",
|
||||
):
|
||||
self.message = message
|
||||
self.status_code = status_code
|
||||
self.error_code = error_code
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class NotFoundError(AppError):
|
||||
def __init__(self, message: str = "资源不存在"):
|
||||
super().__init__(message, status_code=404, error_code="NOT_FOUND")
|
||||
|
||||
|
||||
class AuthenticationError(AppError):
|
||||
def __init__(self, message: str = "认证失败"):
|
||||
super().__init__(message, status_code=401, error_code="AUTHENTICATION_FAILED")
|
||||
|
||||
|
||||
class AuthorizationError(AppError):
|
||||
def __init__(self, message: str = "权限不足"):
|
||||
super().__init__(message, status_code=403, error_code="FORBIDDEN")
|
||||
|
||||
|
||||
class ValidationError(AppError):
|
||||
def __init__(self, message: str = "请求参数无效"):
|
||||
super().__init__(message, status_code=422, error_code="VALIDATION_ERROR")
|
||||
|
||||
|
||||
class ProviderError(AppError):
|
||||
def __init__(self, message: str = "外部服务异常", *, provider: str = ""):
|
||||
super().__init__(message, status_code=502, error_code="PROVIDER_ERROR")
|
||||
self.provider = provider
|
||||
|
||||
|
||||
class QuotaExceededError(AppError):
|
||||
def __init__(self, message: str = "配额已用尽"):
|
||||
super().__init__(message, status_code=429, error_code="QUOTA_EXCEEDED")
|
||||
|
||||
|
||||
# ── Exception handler registration ──────────────────────────
|
||||
|
||||
|
||||
def _get_request_id(request: Request) -> str:
|
||||
return getattr(request.state, "request_id", "-")
|
||||
|
||||
|
||||
def register_exception_handlers(app: FastAPI) -> None:
|
||||
"""Register global exception handlers on the FastAPI app."""
|
||||
|
||||
@app.exception_handler(AppError)
|
||||
async def app_error_handler(request: Request, exc: AppError):
|
||||
request_id = _get_request_id(request)
|
||||
logger.warning(
|
||||
"AppError: error_code={} message={} request_id={}",
|
||||
exc.error_code,
|
||||
exc.message,
|
||||
request_id,
|
||||
)
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content={
|
||||
"error_code": exc.error_code,
|
||||
"message": exc.message,
|
||||
"request_id": request_id,
|
||||
},
|
||||
)
|
||||
|
||||
@app.exception_handler(Exception)
|
||||
async def unhandled_error_handler(request: Request, exc: Exception):
|
||||
request_id = _get_request_id(request)
|
||||
logger.exception("Unhandled exception: request_id={}", request_id)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={
|
||||
"error_code": "INTERNAL_ERROR",
|
||||
"message": "服务器内部错误",
|
||||
"request_id": request_id,
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user