You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI中如何让中间件在认证依赖后仅运行于已认证端点?

解决方案

针对你的需求,有两种可行的实现方式,分别适配不同场景:


方案一:将统计逻辑转为依赖(推荐)

既然你已经通过路由级依赖auth_check保护了端点,直接把统计逻辑做成依赖附加到受保护路由上是最符合FastAPI设计的方式。这种方式天然保证统计逻辑在认证完成后执行,且仅作用于受保护端点,还能直接获取认证后的用户元数据。

步骤1:改写统计逻辑为依赖函数

from fastapi import Request, Depends, BackgroundTasks
from sqlalchemy.orm import Session
from your_db_module import get_db  # 替换为你实际的get_db导入路径

async def record_api_usage_dependency(
    request: Request,
    background_tasks: BackgroundTasks,
    db: Session = Depends(get_db),
    current_user = Depends(auth_check)  # 直接依赖auth_check,自动获取认证后的用户数据
):
    # 这里可以直接使用current_user的元数据(比如用户ID、角色等)
    background_tasks.add_task(record_api_usage_to_db, db, request, current_user)

步骤2:将统计依赖添加到受保护路由

app.include_router(
    admin_router,
    tags=["admin"],
    prefix="/admin",
    dependencies=[Depends(auth_check), Depends(record_api_usage_dependency)],
)

方案二:利用request.state和路由信息改造中间件

如果必须使用中间件,可通过以下方式实现:

步骤1:在auth_check中存储认证元数据

修改你的认证依赖,将用户信息存入request.state,供后续中间件获取:

async def auth_check(request: Request, db: Session = Depends(get_db)):
    # 你的认证逻辑(比如解析Token、查询用户)
    user = get_current_user_from_token(db, request.headers.get("Authorization"))
    # 将用户信息存入request.state
    request.state.current_user = user
    return user

步骤2:改造中间件逻辑

去掉中间件中的Depends(BaseHTTPMiddleware不支持依赖注入),手动处理数据库会话和后台任务,并通过路由信息过滤受保护端点:

from sqlalchemy.orm import Session
from your_db_module import get_db
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.background import BackgroundTasks

async def record_api_usage_to_db_middleware(request: Request, call_next):
    # 先执行路由处理,确保auth_check已完成
    response = await call_next(request)
    
    # 1. 判断当前路由是否为受保护端点(两种方式选其一)
    # 方式A:通过URL前缀判断(简单直接)
    if request.url.path.startswith("/admin"):
        # 2. 检查是否已完成认证
        if hasattr(request.state, "current_user"):
            # 手动获取数据库会话
            db = next(get_db())
            # 创建后台任务并添加到响应中
            background_tasks = BackgroundTasks()
            background_tasks.add_task(
                record_api_usage_to_db, 
                db, 
                request, 
                request.state.current_user  # 传入认证后的用户元数据
            )
            response.background = background_tasks
    
    # 方式B:通过路由依赖精准判断(更灵活)
    # route = request.scope.get("route")
    # if route and is_route_protected(route):
    #     # 执行统计逻辑
    
    return response

# 可选:实现精准判断路由是否受保护的辅助函数
def is_route_protected(route):
    # 检查当前路由的直接依赖
    for dep in getattr(route, "dependencies", []):
        if callable(dep.dependency) and dep.dependency.__name__ == auth_check.__name__:
            return True
    # 递归检查子路由(如果是APIRouter)
    if hasattr(route, "routes"):
        for sub_route in route.routes:
            if is_route_protected(sub_route):
                return True
    return False

步骤3:在main.py中添加中间件

from middlewares import record_api_usage_to_db_middleware
from fastapi import FastAPI
from starlette.middleware.base import BaseHTTPMiddleware

app = FastAPI()
app.add_middleware(BaseHTTPMiddleware, dispatch=record_api_usage_to_db_middleware)

内容的提问来源于stack exchange,提问作者nb123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 06:02:51