feat(backend): 重构 HTTP 错误处理为 RFC7807 标准并优化多个 service

This commit is contained in:
qzl
2026-03-27 14:04:49 +08:00
parent 471488f5f7
commit b1f0eb8921
25 changed files with 1324 additions and 316 deletions
+37 -1
View File
@@ -1,7 +1,7 @@
from __future__ import annotations
from contextlib import asynccontextmanager
from typing import AsyncGenerator
from typing import Any, AsyncGenerator
from fastapi import FastAPI, HTTPException, Request
from fastapi.exceptions import RequestValidationError
@@ -11,6 +11,7 @@ from pydantic import BaseModel
from starlette.exceptions import HTTPException as StarletteHTTPException
from core.config.settings import config
from core.http.errors import ApiProblemError
from core.http.response import build_problem_details
from core.logging import configure_logging, get_logger, log_service_banner
from services.base import close_registered_services, initialize_registered_services
@@ -79,6 +80,19 @@ def _build_http_error_response(
) -> JSONResponse:
instance = request.url.path
detail_text = detail if isinstance(detail, str) else "Request failed"
error_code: str | None = None
error_params: dict[str, Any] | None = None
if isinstance(detail, dict):
raw_detail = detail.get("detail")
raw_code = detail.get("code")
raw_params = detail.get("params")
if isinstance(raw_detail, str) and raw_detail.strip():
detail_text = raw_detail
if isinstance(raw_code, str) and raw_code.strip():
error_code = raw_code
if isinstance(raw_params, dict):
error_params = raw_params
logger.warning(
"HTTP error",
status_code=status_code,
@@ -91,6 +105,8 @@ def _build_http_error_response(
status_code=status_code,
detail=detail_text,
instance=instance,
code=error_code,
params=error_params,
)
return JSONResponse(
status_code=status_code,
@@ -170,3 +186,23 @@ async def unhandled_exception_handler(
content=problem.model_dump(),
media_type="application/problem+json",
)
@app.exception_handler(ApiProblemError)
async def api_problem_exception_handler(
request: Request,
exc: ApiProblemError,
) -> JSONResponse:
instance = request.url.path
problem = build_problem_details(
status_code=exc.status_code,
detail=exc.detail,
instance=instance,
code=exc.code,
params=exc.params,
)
return JSONResponse(
status_code=exc.status_code,
content=problem.model_dump(),
media_type="application/problem+json",
)