|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
import logging
|
|
|
|
import time
|
|
|
|
import time
|
|
|
|
import uuid
|
|
|
|
import uuid
|
|
|
|
|
|
|
|
from http import HTTPStatus
|
|
|
|
from collections.abc import Iterable, Mapping
|
|
|
|
from collections.abc import Iterable, Mapping
|
|
|
|
from contextvars import ContextVar
|
|
|
|
from contextvars import ContextVar
|
|
|
|
from contextlib import asynccontextmanager
|
|
|
|
from contextlib import asynccontextmanager
|
|
|
|
@ -25,6 +26,7 @@ from fastapi.routing import APIRoute
|
|
|
|
from fastapi.routing import request_response
|
|
|
|
from fastapi.routing import request_response
|
|
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from pydantic import BaseModel
|
|
|
|
from pydantic import BaseModel
|
|
|
|
|
|
|
|
from starlette.exceptions import HTTPException as StarletteHTTPException
|
|
|
|
from starlette.responses import Response
|
|
|
|
from starlette.responses import Response
|
|
|
|
from sqlalchemy.exc import SQLAlchemyError
|
|
|
|
from sqlalchemy.exc import SQLAlchemyError
|
|
|
|
|
|
|
|
|
|
|
|
@ -188,6 +190,16 @@ def init_error_handlers(app: FastAPI) -> None:
|
|
|
|
content=fail("参数验证错误", code=422, data=exc.errors()),
|
|
|
|
content=fail("参数验证错误", code=422, data=exc.errors()),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.exception_handler(StarletteHTTPException)
|
|
|
|
|
|
|
|
async def handle_http_error(request: Request, exc: StarletteHTTPException):
|
|
|
|
|
|
|
|
request.state.response_code = exc.status_code
|
|
|
|
|
|
|
|
message, data = _http_error_payload(exc)
|
|
|
|
|
|
|
|
return JSONResponse(
|
|
|
|
|
|
|
|
status_code=request.app.state.config.response_envelope_http_status,
|
|
|
|
|
|
|
|
content=fail(message, code=exc.status_code, data=data),
|
|
|
|
|
|
|
|
headers=exc.headers,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@app.exception_handler(SQLAlchemyError)
|
|
|
|
@app.exception_handler(SQLAlchemyError)
|
|
|
|
async def handle_db_error(request: Request, exc: SQLAlchemyError):
|
|
|
|
async def handle_db_error(request: Request, exc: SQLAlchemyError):
|
|
|
|
request.state.response_code = 500
|
|
|
|
request.state.response_code = 500
|
|
|
|
@ -207,6 +219,27 @@ def init_error_handlers(app: FastAPI) -> None:
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _http_error_payload(exc: StarletteHTTPException) -> tuple[str, Any]:
|
|
|
|
|
|
|
|
detail = exc.detail
|
|
|
|
|
|
|
|
if isinstance(detail, str):
|
|
|
|
|
|
|
|
return detail, None
|
|
|
|
|
|
|
|
if detail is None:
|
|
|
|
|
|
|
|
return _http_status_phrase(exc.status_code), None
|
|
|
|
|
|
|
|
if isinstance(detail, Mapping):
|
|
|
|
|
|
|
|
for key in ("message", "detail", "error"):
|
|
|
|
|
|
|
|
value = detail.get(key)
|
|
|
|
|
|
|
|
if isinstance(value, str) and value:
|
|
|
|
|
|
|
|
return value, detail
|
|
|
|
|
|
|
|
return _http_status_phrase(exc.status_code), detail
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _http_status_phrase(status_code: int) -> str:
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
return HTTPStatus(status_code).phrase
|
|
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
|
|
return "HTTP Error"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def to_plain_data(value: Any) -> Any:
|
|
|
|
def to_plain_data(value: Any) -> Any:
|
|
|
|
if is_dataclass(value):
|
|
|
|
if is_dataclass(value):
|
|
|
|
return asdict(value)
|
|
|
|
return asdict(value)
|
|
|
|
|