FastAPI 保存将在端点中可用的上下文

sas*_*ero 6 python middleware fastapi

我的目标是从请求中提取一些信息并将其放入某些全局上下文中。我尝试的第一件事是执行此操作的中间件函数

user_ctx: ContextVar[typing.Optional[UUID]] = ContextVar('user_ctx', default=None)

def save_user_ctx(request: Request):
    uid = ... # logic that extracts user uid from request header
    user_ctx.set(uid)
Run Code Online (Sandbox Code Playgroud)

例如,我将其添加到另一个中间件函数中my_middleware

所以它确实设置了上下文,但据我所知,中间件在另一个线程中工作,所以如果我尝试类似的事情

@router.post('/some_url', response_model=SomeResponseModel)
def some_url(data: SomeRequestModel, auth_checker = Depends(my_middleware)):
    user_ctx.get() # >> None
Run Code Online (Sandbox Code Playgroud)

auth_checker拥有所有身份验证信息,因此中间件可以正常工作。但uid = user_ctx.get()归来None

这只是一个例子。请

我想要实现的是将请求中的一些上下文存储在自动服务端点的线程中。我的意思是不写ctx.set(smth)为每个端点的第一行。

我也尝试过写装饰器

def save_request_ctx(func):
    @wraps(func)
    def wrap(*args, **kwargs):
        ctx.set(...)
        return func(*args, **kwargs)
    return wrap
Run Code Online (Sandbox Code Playgroud)

但是,如果端点没有request: Request作为参数,它就会得到 - 它不会出现在 或argskwargs。因此,如果您知道如何强制每个端点作为request参数,而不需要在每个端点函数中手动添加它 - 它也将被接受作为解决方案。

如果我对我想要的东西的解释很复杂,我会尽力澄清它。

Tom*_*cik 4

您可能想使用 mystarlette-context来实现这一点。

为了实现你想要的,最好是子类化ContextMiddleware并覆盖该set_context方法。返回的内容将在您可以导入到任何地方的对象中可用context

示例代码


from starlette_context.middleware import ContextMiddleware


class YourMiddleware(ContextMiddleware):
    async def set_context(self, request: Request) -> dict:
        return {"from_middleware": True}
Run Code Online (Sandbox Code Playgroud)

注册此中间件后,您可以在视图/记录器中导入

from starlette_context import context
Run Code Online (Sandbox Code Playgroud)

并使用context["from_middleware"].

请注意,我为最常见的用例准备了“插件”。如果您想在应用程序中的任何位置访问请求/相关 ID,只需使用以下命令初始化中间件即可

from starlette_context import context, plugins
from starlette_context.middleware import ContextMiddleware

middleware = [
    Middleware(
        ContextMiddleware,
        plugins=(plugins.RequestIdPlugin(), plugins.CorrelationIdPlugin()),
    )
]

app = Starlette(debug=True, middleware=middleware)
Run Code Online (Sandbox Code Playgroud)

FastAPI 使用 Starlette 接口,所以你应该可以开始了。

关于

但如果端点没有 request: Request 作为参数,它就不会出现在 args 或 kwargs 中。因此,如果您知道如何强制每个端点将请求作为参数,而不需要手动将其添加到每个端点函数中,那么它也将被接受为解决方案。

我无法回答这个问题,因为我从未尝试过使用没有请求参数的视图。

编辑:我添加RawContextMiddleware不需要请求对象。