""" 自定义异常 + 全局异常处理器 统一输出格式: { "code": int, "data": Any, "message": str } """ from __future__ import annotations from typing import Any from fastapi import FastAPI, Request from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse from starlette.exceptions import HTTPException as StarletteHTTPException # ── 自定义业务异常 ───────────────────────────────────────── class BizException(Exception): """通用业务异常,code 对应 HTTP 状态码""" def __init__(self, code: int = 400, message: str = "业务异常", data: Any = None): self.code = code self.message = message self.data = data class UnauthorizedException(BizException): def __init__(self, message: str = "未登录或 Token 已失效"): super().__init__(code=401, message=message) class ForbiddenException(BizException): def __init__(self, message: str = "无权访问"): super().__init__(code=403, message=message) class NotFoundException(BizException): def __init__(self, message: str = "资源不存在"): super().__init__(code=404, message=message) # ── 统一响应构造 ────────────────────────────────────────── def _make_response(code: int, message: str, data: Any = None) -> JSONResponse: return JSONResponse( status_code=code, content={"code": code, "data": data, "message": message}, ) # ── 注册全局异常处理器 ──────────────────────────────────── def register_exception_handlers(app: FastAPI) -> None: @app.exception_handler(BizException) async def biz_exception_handler(_req: Request, exc: BizException) -> JSONResponse: return _make_response(exc.code, exc.message, exc.data) @app.exception_handler(StarletteHTTPException) async def http_exception_handler( _req: Request, exc: StarletteHTTPException ) -> JSONResponse: return _make_response(exc.status_code, str(exc.detail)) @app.exception_handler(RequestValidationError) async def validation_exception_handler( _req: Request, exc: RequestValidationError ) -> JSONResponse: # 把 Pydantic 校验错误打平为可读字符串 errors = [] for e in exc.errors(): loc = " -> ".join(str(x) for x in e.get("loc", [])) errors.append(f"{loc}: {e.get('msg', '')}") return _make_response(422, "请求参数校验失败", errors) @app.exception_handler(Exception) async def global_exception_handler( _req: Request, exc: Exception ) -> JSONResponse: # 兜底:未知异常统一 500 return _make_response(500, f"服务器内部错误: {exc!s}")