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

如何在Starlette中间件中获取请求的路径模板?

问题解答

1. 什么是scope?

scope是FastAPI/Starlette中存储请求上下文元数据的字典,包含请求方法、客户端地址、URL路径等核心信息,它贯穿整个请求处理流程,不同阶段会逐步填充更多属性。

2. 为什么中间件里scope没有route属性?

你使用的BaseHTTPMiddleware属于全局前置中间件,它的执行时机在FastAPI完成路由匹配之前。路由匹配是FastAPI根据请求URL找到对应端点函数的过程,只有匹配完成后,scope才会被注入route属性(包含原始路由路径、端点信息等)。而端点函数是在路由匹配完成后执行的,所以能拿到route。

3. 解决方法

方法一:使用路由级中间件(路由匹配后执行)

如果日志需求针对特定路由,可使用Starlette的路由级中间件,此时scope已包含route属性:

from starlette.middleware.base import RequestResponseEndpoint
from fastapi import Request, APIRouter

router = APIRouter()

async def route_log_middleware(request: Request, call_next: RequestResponseEndpoint):
    # 路由已匹配,直接获取原始路由路径
    route_path = request.scope["route"].path
    print(f"原始路由路径: {route_path}")
    response = await call_next(request)
    return response

router.middleware("http")(route_log_middleware)

@router.get("/success/{id}", status_code=201)
def success(id: str):
    return {"id": id}

# 挂载到主应用
app.include_router(router)

方法二:在BaseHTTPMiddleware中通过反向路由生成原始路径

如果必须用全局BaseHTTPMiddleware,可通过端点函数名称,用app.url_path_for反向生成原始路由路径:

from starlette.middleware.base import BaseHTTPMiddleware
from fastapi import Request

class RequestInfo(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        response = await call_next(request)
        # 从request.state获取提前传递的端点名称
        if hasattr(request.state, "endpoint_name"):
            # 生成带占位符的原始路由路径
            raw_path = request.app.url_path_for(request.state.endpoint_name, id="*")
            print(f"原始路由路径格式: {raw_path}")
        return response

# 在端点函数中传递端点名称到request.state
@app.get("/success/{id}", status_code=201)
def success(request: Request, id: str):
    request.state.endpoint_name = "success"
    return {"id": id}

方法三:预加载路由规则,手动匹配请求路径

应用启动时加载所有路由的路径规则,在中间件中手动匹配当前请求路径,得到原始路由格式:

from starlette.middleware.base import BaseHTTPMiddleware
from fastapi import Request
from starlette.routing import Route

class RequestInfo(BaseHTTPMiddleware):
    def __init__(self, app):
        super().__init__(app)
        # 预加载所有路由的路径规则
        self.route_paths = [route.path for route in app.routes if isinstance(route, Route)]
    
    async def dispatch(self, request: Request, call_next):
        current_path = request.url.path
        # 简单匹配示例(复杂路径需用正则精确匹配)
        for raw_path in self.route_paths:
            prefix = raw_path.split("{")[0]
            if current_path.startswith(prefix):
                print(f"匹配到的原始路由路径: {raw_path}")
                break
        response = await call_next(request)
        return response

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 07:43:16