














这是通过全局 HTTP 异常处理器 + 统一响应封装实现的。
main.py
configure_api_responses(app)
请求不存在的路径时,FastAPI 底层的 Starlette 路由会抛出 HTTPException,其中:
status_code = 404
detail = "Not Found"
注册的处理器接住这个异常:
@app.exception_handler(HTTPException)
async def http_error(request: Request, exc: HTTPException):
return error_response(
exc.status_code, {"detail": exc.detail}, exc.headers
)
这里导入的是 starlette.exceptions.HTTPException,所以也能捕获框架产生的路由异常。
[responses.py:22]的 error_response():
ERROR_CODES 将 404 映射为业务码 4004。detail 作为 message。{"detail": "Not Found"} 放入 data。ApiResponse 序列化,返回 JSONResponse。因此响应体是:
{
"code": 4004,
"message": "Not Found",
"data": {"detail": "Not Found"}
}
注意:目前实际返回的 HTTP 状态码是 200,因为 error_response() 中写的是 status_code=200;404 被转换成了响应体里的业务码 4004。
main.py
def create_app(
settings: Settings | None = None,
extractor=None,
rag_answer_service=None,
) -> FastAPI:
settings = settings or Settings()
settings.ensure_directories()
settings.configure_logging(component="api")
repository = create_document_repository(settings)
if extractor is None:
from vipsoft_agent.infrastructure.llm.qwen import QwenRequirementExtractor
extractor = QwenRequirementExtractor(settings)
app = FastAPI(
title="VipSoftAgent",
version="0.1.0",
description="VipSoftAgent Service",
lifespan=lifespan,
redirect_slashes=False,
docs_url=None,
redoc_url=None
)
configure_api_responses(app) # 配置异常处理器
.....
return app
def run() -> None:
import uvicorn
uvicorn.run("vipsoft_agent.main:create_app", factory=True, host="0.0.0.0", port=8000)
responses.py
def configure_api_responses(app: FastAPI) -> None:
@app.exception_handler(HTTPException)
async def http_error(request: Request, exc: HTTPException):
return error_response(exc.status_code, {"detail": exc.detail}, exc.headers)
@app.exception_handler(RequestValidationError)
async def validation_error(request: Request, exc: RequestValidationError):
return error_response(422, {"detail": exc.errors()})
# Keep unexpected errors inside CORS so allowed callers can read the envelope.
@app.middleware("http")
async def unexpected_error(request: Request, call_next):
try:
return await call_next(request)
except Exception:
logger.exception("Unhandled API error method=%s path=%s",
request.method, request.url.path)
return error_response(500, {"detail": "Internal server error"})
def openapi():
if app.openapi_schema is None:
schema = get_openapi(
title=app.title, version=app.version,
description=app.description, routes=app.routes,
)
for path in schema["paths"].values():
for operation in path.values():
if isinstance(operation, dict) and "responses" in operation:
operation["responses"].pop("422", None)
app.openapi_schema = schema
return app.openapi_schema
app.openapi = openapi
def error_response(status: int, data: dict, headers=None) -> JSONResponse:
detail = data.get("detail")
if isinstance(detail, str):
message = detail
elif isinstance(detail, list) and detail and isinstance(detail[0], dict):
message = str(detail[0].get("msg", "Request failed"))
else:
message = HTTPStatus(status).phrase
return JSONResponse(
status_code=200,
content=ApiResponse(
code=ERROR_CODES.get(status, status * 10),
message=message,
data=jsonable_encoder(data),
).model_dump(mode="json"),
headers=headers,
)
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。