fastapi: 用starlette-context保存request对象,使不需要专递参数也可以从各处访问request

一,代码:

plugin:

from starlette.requests import Request
from starlette_context.plugins import Plugin

class PythonRequestPlugin(Plugin):
    # 1. 定义在 context 中读取时的 key 名,比如 context["request"]
    key = "request"

    # 2. 覆盖这个异步方法,直接把传入的 request 对象返回
    async def process_request(self, request: Request):
        print("保存request")
        return request

注册中间件:注意这一部分要放在所有middleware的最前面

# 注册中间件,并传入我们的自定义插件
api_app.add_middleware(
    RawContextMiddleware,
    #ContextMiddleware,
    plugins=[
        PythonRequestPlugin()  # 实例化我们自己的插件
    ]
)

api_app.add_middleware(
    ContextMiddleware,
    plugins=(AuthorizationPlugin(),)
)

实际访问

@router.get("/info2")
async def read_users_info2():
    """受保护的路由,只有携带有效 Token 才能访问"""

    # user = get_current_user_by_context()
    if context.exists():
        # 从上下文中安全取出 request 对象
        request = context.get("request")
        print("request:", request)

        query_params = dict(request.query_params)
        print("query_params:", query_params)

        print(f"当前请求路径是: {request.url.path}")
        
    return {"message": "如果username不为空,说明认证成功了!"}

二,测试效果:

image

这个主要是在控制台打印:

保存request
request: <starlette.middleware.base._CachedRequest object at 0x772f6b5ff010>
query_params: {'name': 'liu'}
当前请求路径是: /api/account/info2

三,说明:

1. 异步(Async)与同步(Sync)函数的陷阱
FastAPI 支持普通的 def 函数和 async def 函数:

如果你在 async def 中使用,或者在 FastAPI 默认线程池执行的普通 def 路由中,contextvars 都能正常工作。

但是,如果你在代码中自己开了新线程(例如使用 threading.Thread 或某些不支持上下文复制的外部线程池),新线程中将无法获取到这个 context,会抛出异常或返回 None。

2. 丧失了 FastAPI 依赖注入的优势
FastAPI 的 Depends(依赖注入)非常强大,它能清晰地展示出代码的依赖关系,且非常利于编写单元测试(Pytest)。

如果你使用全局 context,你的 Service 层会隐式依赖 HTTP 请求环境。

后果:当你脱离 Web 环境(例如跑一个定时任务脚本、或者写单元测试时),直接调用 my_service_layer() 就会因为没有 HTTP 请求而报错,你必须在测试中手动去 mock 这个上下文。

3. 替代方案:FastAPI 原生 BackgroundTasks
如果你只是想在后台任务中用 request,FastAPI 的 BackgroundTasks 是可以通过传参完美解决的,不需要依赖第三方库。

posted @ 2026-06-29 20:09  刘宏缔的架构森林  阅读(3)  评论(0)    收藏  举报