Files
group_xinghuo_jinrong/app/utils/response.py
T
zhanghongyu_0626 6d780d45c3 feat(auth): Enhance JWT handling and error management in authentication flow
- Updated the login endpoint to utilize shared JWT issuer/audience settings, improving consistency across modules.
- Introduced error handling for unknown accounts during token issuance, raising an UnauthorizedError when necessary.
- Enhanced traceability by adding trace and request IDs to responses, ensuring better tracking of requests.
- Refactored exception handling in middleware to properly bubble up application-specific errors, preventing them from being swallowed.
- Added new utility functions for generating trace headers to improve debugging capabilities.

This update strengthens the authentication process and enhances error visibility, contributing to a more robust and maintainable codebase.
2026-09-08 19:55:08 +08:00

60 lines
2.7 KiB
Python

"""统一 API 响应外壳(Wave 0 ok/fail + 风控模块 error_body/register_error_handlers)。"""
from __future__ import annotations
from typing import Any
from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException as StarletteHTTPException
from app.model.schemas import ApiResponse
from app.utils.exceptions import ApiError, PermissionDenied
from app.utils.trace import current_request_id, current_trace, new_request_id, new_trace
_HTTP_ERROR_CODES = {404: "NOT_FOUND", 405: "METHOD_NOT_ALLOWED"}
def ok(data: Any, trace_id: str, message: str = "ok") -> ApiResponse:
return ApiResponse(code=0, message=message, data=data, trace_id=trace_id)
def fail(code: int, message: str, trace_id: str, *, data: Any = None) -> ApiResponse:
return ApiResponse(code=code, message=message, data=data, trace_id=trace_id)
def error_body(error_code: str, message: str) -> dict[str, str]:
tid = current_trace() or new_trace()
rid = current_request_id() or new_request_id()
return {"error_code": error_code, "message": message, "trace_id": tid, "request_id": rid}
def register_error_handlers(app: FastAPI) -> None:
@app.exception_handler(ApiError)
async def _api_error_handler(request: Request, exc: ApiError) -> JSONResponse:
return JSONResponse(status_code=exc.status_code, content=error_body(exc.error_code, exc.message))
@app.exception_handler(PermissionDenied)
async def _permission_denied_handler(request: Request, exc: PermissionDenied) -> JSONResponse:
return JSONResponse(status_code=403, content=error_body(exc.code, str(exc)))
@app.exception_handler(StarletteHTTPException)
async def _http_exception_handler(request: Request, exc: StarletteHTTPException) -> JSONResponse:
code = _HTTP_ERROR_CODES.get(exc.status_code, "HTTP_ERROR")
return JSONResponse(status_code=exc.status_code, content=error_body(code, str(exc.detail)))
@app.exception_handler(RequestValidationError)
async def _validation_error_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
return JSONResponse(
status_code=422,
content=error_body("REQUEST_VALIDATION_FAILED", "request validation failed"),
)
def trace_headers(request: Request) -> dict[str, str]:
"""从 request.state 取 trace/request 头(异常路径 contextvar 可能已复位)。"""
trace_id = getattr(request.state, "trace_id", None) or current_trace() or new_trace()
request_id = getattr(request.state, "request_id", None) or current_request_id() or new_request_id()
return {"X-Trace-Id": trace_id, "X-Request-Id": request_id}