我正在使用aiohttp
设置aiohttp_session
服务器以将数据存储到EncryptedCookieStorage
中。我用它来存储7天有效令牌,到期日期和刷新令牌。
无论客户端访问哪个端点,我都希望检查令牌(存储在会话中)是否需要刷新。中间件的选择非常明显。
问题是,当我打电话给await aiohttp_session.get_session(request)
时,我得到一个不错的RuntimeError
要求我将aiohttp_session
中间件设置为aiohttp.web.Application
。我的猜测是,我的自定义中间件被称为 before ,用于处理会话加载,因此该会话尚无法访问。我已经搜索了一些有关中间件的“优先”系统,但没有发现任何东西。
我的服务器设置在main.py
文件中,例如:
def main():
app = web.Application()
middleware.setup(app)
session_key = base64.urlsafe_b64decode(fernet.Fernet.generate_key())
aiohttp_session.setup(app, EncryptedCookieStorage(session_key))
# I have tried swapping the two setup functions
web.run_app(app)
if __name__ == '__main__':
main()
middleware.setup()
在单独的程序包中,在__init__.py
中:
# For each python file in the package, add it's middleware function to the app middlewares
def setup(app):
for filename in listdir('middleware'):
if filename[-2:] == 'py' and filename[:2] != '__':
module = __import__('rpdashboard.middleware.' + filename[:-3], fromlist=['middleware'])
app.middlewares.append(module.middleware)
最后,我要加入会话的中间件是:
@web.middleware
async def refresh_token_middleware(request, handler):
session = await get_session(request)
if session.get('token'):
pass # To be implemented ...
return await handler(request)
middleware = refresh_token_middleware
执行问题在这里:
# From aiohttp_session
async def get_session(request):
session = request.get(SESSION_KEY)
if session is None:
storage = request.get(STORAGE_KEY)
if storage is None:
# This is raised
raise RuntimeError(
"Install aiohttp_session middleware "
"in your aiohttp.web.Application")
正如我之前说的,似乎该会话不是要在中间件中访问的,并且尚未加载。那么,如何防止我的自定义中间件在会话加载一个中间件之前运行?还是自己直接手动运行aiohttp_session
中间件?甚至有可能吗?
答案 0 :(得分:1)
您自己更改顺序。代码应该是这样
def main():
app = web.Application()
session_key = base64.urlsafe_b64decode(fernet.Fernet.generate_key())
aiohttp_session.setup(app, EncryptedCookieStorage(session_key))
# I have tried swapping the two setup functions
middleware.setup(app)
web.run_app(app)
如果您查看aiohttp_session.setup
的代码
https://github.com/aio-libs/aiohttp-session/blob/master/aiohttp_session/init.py
def setup(app, storage):
"""Setup the library in aiohttp fashion."""
app.middlewares.append(session_middleware(storage))
如您所见,中间件已添加到此功能中。在middleware.setup(app)
之前添加中间件会使会话仍然无法用于请求
答案 1 :(得分:1)
是的,以正确的顺序添加到应用程序 的中间件组件可以访问由会话中间件设置的会话存储。
aiohttp
文档涵盖了Middlewares section中中间件组件的优先级顺序:
在内部,通过将中间件链反向应用到原始处理程序来构建单个请求处理程序,并由
RequestHandler
作为常规的处理程序进行调用。
接下来,他们使用一个示例来演示这意味着什么。总之,他们使用两个中间件组件来报告其进入和退出,并按以下顺序将它们添加到app.middlewares
列表中:
... middlewares=[middleware1, middleware2]
此命令产生以下输出:
Middleware 1 called Middleware 2 called Handler function called Middleware 2 finished Middleware 1 finished
因此,传入请求以将它们添加到app.middlewares
列表中的相同顺序沿着不同的中间件传递。
接下来,aiohttp_session
还在API entry for aiohttp_session.setup()
中记录了他们如何添加会话中间件:
此功能是以下操作的快捷方式:
app.middlewares.append(session_middleware(storage))
因此,将它们的中间件组件添加到列表的末尾。上面的值表示所有需要访问会话的内容都必须在此中间件组件之后 。
会话中间件所做的全部工作就是使用aiohttp_session.STORAGE_KEY
键将存储添加到请求中;这使会话可用于其后的任何其他中间件组件。您的中间件组件不需要做任何特殊的事情,除了在会话中间件之后添加外,还可以将存储对象添加到请求中。请求对象是designed to share data between components this way。
您的代码将所有中间件组件放在会话中间件组件之前:
middleware.setup(app)
# ...
aiohttp_session.setup(app, EncryptedCookieStorage(session_key))
这给您订购了[..., refresh_token_middleware, ..., session_middleware]
,并且中间件无法访问任何会话信息。
因此您必须调换订单;先致电aiohttp_session.setup()
,然后再添加您自己的组件:
aiohttp_session.setup(app, EncryptedCookieStorage(session_key))
middleware.setup(app)
如果访问会话存储仍然有问题,则表示中间的中间件组件之一正在再次删除会话存储信息。
您可以在不同位置使用以下中间件工厂来报告存在的会话存储,以帮助您调试此问题:
from aiohttp import web
from aiohttp_session import STORAGE_KEY
COUNTER_KEY = "__debug_session_storage_counter__"
_label = {
False: "\x1b[31;1mMISSING\x1b[0m",
True: "\x1b[32;1mPRESENT\x1b[0m",
}
def debug_session_storage(app):
pre = nxt = ""
if app.middlewares:
previous = app.middlewares[-1]
name = getattr(previous, "__qualname__", repr(previous))
pre = f" {name} ->"
nxt = f" {name} <-"
@web.middleware
async def middleware(request, handler):
counter = request.get(COUNTER_KEY, -1) + 1
request[COUNTER_KEY] = counter
found = STORAGE_KEY in request
indent = " " * counter
print(f"{indent}-{pre} probe#{counter} - storage: {_label[found]}")
try:
return await handler(request)
finally:
print(f"{indent}-{nxt} probe#{counter} - done")
app.middlewares.append(middleware)
如果在添加的每个中间件之间插入此代码,则应该能够确定会话存储是否丢失以及丢失的位置:
def setup(app):
# start with a probe
debug_session_storage(app)
for filename in listdir('middleware'):
if filename[-2:] == 'py' and filename[:2] != '__':
module = __import__('rpdashboard.middleware.' + filename[:-3], fromlist=['middleware'])
app.middlewares.append(module.middleware)
# Add debug probe after every component
debug_session_storage(app)
这应该告诉你