errors.py 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. from __future__ import annotations
  2. from typing import Any
  3. from fastapi import FastAPI, Request
  4. from fastapi.exceptions import RequestValidationError
  5. from fastapi.responses import JSONResponse
  6. from starlette.exceptions import HTTPException as StarletteHTTPException
  7. class APIError(Exception):
  8. def __init__(
  9. self,
  10. status_code: int,
  11. code: str,
  12. message: str,
  13. detail: Any | None = None,
  14. ) -> None:
  15. super().__init__(message)
  16. self.status_code = status_code
  17. self.code = code
  18. self.message = message
  19. self.detail = detail
  20. def error_payload(code: str, message: str, detail: Any | None = None) -> dict[str, Any]:
  21. payload: dict[str, Any] = {"code": code, "message": message}
  22. if detail is not None:
  23. payload["detail"] = detail
  24. return payload
  25. def install_exception_handlers(app: FastAPI) -> None:
  26. @app.exception_handler(APIError)
  27. async def api_error_handler(_: Request, exc: APIError) -> JSONResponse:
  28. return JSONResponse(
  29. status_code=exc.status_code,
  30. content=error_payload(exc.code, exc.message, exc.detail),
  31. )
  32. @app.exception_handler(RequestValidationError)
  33. async def validation_error_handler(
  34. _: Request, exc: RequestValidationError
  35. ) -> JSONResponse:
  36. return JSONResponse(
  37. status_code=422,
  38. content=error_payload("validation_error", "请求参数校验失败", exc.errors()),
  39. )
  40. @app.exception_handler(StarletteHTTPException)
  41. async def http_error_handler(_: Request, exc: StarletteHTTPException) -> JSONResponse:
  42. detail = exc.detail
  43. if isinstance(detail, dict) and "code" in detail:
  44. return JSONResponse(status_code=exc.status_code, content=detail)
  45. return JSONResponse(
  46. status_code=exc.status_code,
  47. content=error_payload("http_error", str(detail)),
  48. )