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不为空,说明认证成功了!"}
二,测试效果:

这个主要是在控制台打印:
保存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 是可以通过传参完美解决的,不需要依赖第三方库。
浙公网安备 33010602011771号