1. 新增TraceId中间件实现全链路请求追踪 2. 新增在途请求计数器与中间件支持优雅关停 3. 重构全局异常处理器,统一三模块异常响应格式 4. 优化渠道查询接口,新增多维度过滤与模糊匹配 5. 重构channels模块数据库会话管理,避免事务冲突 6. 新增目录搜索建议与目录导出功能
123 lines
3.8 KiB
Python
123 lines
3.8 KiB
Python
"""统一错误响应构建器。
|
||
|
||
定义 ``UnifiedError`` 协议(三模块异常基类都实现此协议),
|
||
提供 ``build_error_response()`` 和 ``build_error_response_from_exc()``
|
||
统一构建错误响应,确保所有全局异常处理器输出格式一致。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Any, Protocol
|
||
|
||
from fastapi.responses import JSONResponse
|
||
from yuxi.utils.trace_context import get_trace_id
|
||
|
||
|
||
class UnifiedError(Protocol):
|
||
"""统一异常协议。
|
||
|
||
三模块异常基类(ChannelError / ExternalSystemError / SchedulerError)
|
||
都实现此协议,允许统一 handler 处理。
|
||
|
||
字段:
|
||
status_code: HTTP 状态码。
|
||
error_code: 稳定错误码(字符串,如 "VALIDATION_ERROR")。
|
||
message: 人类可读错误信息。
|
||
details: 业务字段字典(如 references, retry_after 等)。
|
||
trace_id: 链路追踪 ID。
|
||
"""
|
||
|
||
status_code: int
|
||
error_code: str
|
||
message: str
|
||
details: dict[str, Any]
|
||
trace_id: str | None
|
||
|
||
|
||
def _extract_retry_after(exc: UnifiedError) -> dict[str, str]:
|
||
"""统一提取 Retry-After 响应头。
|
||
|
||
429/503 且 details 含 retry_after 时设置。
|
||
503 且无 retry_after 时设置默认 60 秒(ChannelDegradedError 等降级场景)。
|
||
channels 的 RateLimitError 已将 retry_after 写入 details(通过 to_dict())。
|
||
external_systems 的 RateLimitExceededError 已将 retry_after 写入 details。
|
||
|
||
Args:
|
||
exc: 满足 UnifiedError 协议的异常对象。
|
||
|
||
Returns:
|
||
含 Retry-After 头的字典(无需要时为空字典)。
|
||
"""
|
||
headers: dict[str, str] = {}
|
||
if exc.status_code in (429, 503):
|
||
retry_after = exc.details.get("retry_after")
|
||
if retry_after is not None:
|
||
headers["Retry-After"] = str(retry_after)
|
||
elif exc.status_code == 503:
|
||
# 503 降级场景无明确 retry_after 时设置默认 60 秒
|
||
headers["Retry-After"] = "60"
|
||
return headers
|
||
|
||
|
||
def build_error_response(
|
||
status_code: int,
|
||
code: str,
|
||
message: str,
|
||
details: dict[str, Any] | None = None,
|
||
trace_id: str | None = None,
|
||
headers: dict[str, str] | None = None,
|
||
) -> JSONResponse:
|
||
"""构建统一错误响应。
|
||
|
||
所有全局异常处理器调用此函数,确保响应格式一致。
|
||
trace_id 优先使用传入值,其次从 ContextVar 获取。
|
||
|
||
Args:
|
||
status_code: HTTP 状态码。
|
||
code: 稳定错误码。
|
||
message: 人类可读错误信息。
|
||
details: 业务字段字典。
|
||
trace_id: 链路追踪 ID(为 None 时从 ContextVar 获取)。
|
||
headers: 额外响应头(如 Retry-After)。
|
||
|
||
Returns:
|
||
统一格式的 JSONResponse。
|
||
"""
|
||
resolved_trace_id = trace_id or get_trace_id()
|
||
return JSONResponse(
|
||
status_code=status_code,
|
||
content={
|
||
"success": False,
|
||
"error": {
|
||
"code": code,
|
||
"message": message,
|
||
"trace_id": resolved_trace_id,
|
||
"details": details or {},
|
||
},
|
||
},
|
||
headers=headers or {},
|
||
)
|
||
|
||
|
||
def build_error_response_from_exc(exc: UnifiedError) -> JSONResponse:
|
||
"""从统一异常对象构建错误响应(统一入口)。
|
||
|
||
所有满足 UnifiedError 协议的异常都通过此函数构建响应,
|
||
确保 handler 逻辑完全一致,不再有 isinstance 特判。
|
||
|
||
Args:
|
||
exc: 满足 UnifiedError 协议的异常对象。
|
||
|
||
Returns:
|
||
统一格式的 JSONResponse。
|
||
"""
|
||
headers = _extract_retry_after(exc)
|
||
return build_error_response(
|
||
status_code=exc.status_code,
|
||
code=exc.error_code,
|
||
message=exc.message,
|
||
details=exc.details,
|
||
trace_id=exc.trace_id,
|
||
headers=headers,
|
||
)
|