Python 可以在中间件中访问会话(从aiohttp_会话)吗?

Python 可以在中间件中访问会话(从aiohttp_会话)吗?,python,middleware,aiohttp,Python,Middleware,Aiohttp,我正在设置一个aiohttp服务器,使用aiohttp\u会话将数据存储到EncryptedCookies存储中。我使用它来存储一个7天的有效令牌,以及到期日期和刷新令牌。 无论客户端访问哪个端点,我都希望检查令牌(存储在会话中)是否需要刷新。中间件的选择非常明显 问题是,当我调用wait aiohttp\u session.get\u session(request)时,我收到一个很好的运行时错误要求我设置aiohttp\u session中间件到aiohttp.web.Application

我正在设置一个
aiohttp
服务器,使用
aiohttp\u会话
将数据存储到
EncryptedCookies存储中。我使用它来存储一个7天的有效令牌,以及到期日期和刷新令牌。
无论客户端访问哪个端点,我都希望检查令牌(存储在会话中)是否需要刷新。中间件的选择非常明显

问题是,当我调用
wait aiohttp\u session.get\u session(request)
时,我收到一个很好的
运行时错误
要求我设置
aiohttp\u session
中间件到
aiohttp.web.Application
。我的猜测是,我的自定义中间件在处理会话加载的中间件之前被调用,因此会话还不能访问。我搜索了一些关于中间产品的“优先权”系统,但没有找到任何东西

我的服务器设置在
main.py
文件中,如下所示:

def main():
app=web.Application()
中间件安装(应用程序)
会话\u key=base64.urlsafe\u b64解码(fernet.fernet.generate\u key())
aiohttp_会话设置(应用程序,加密CookieStorage(会话密钥))
#我已尝试交换两个设置功能
web.run_应用程序(应用程序)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
main()
其中
middleware.setup()
在一个单独的包中,在
\uuuuu init\uuuuuuuuuu.py
中:

#对于包中的每个python文件,将其中间件功能添加到应用程序中间件中
def设置(应用程序):
对于listdir(“中间件”)中的文件名:
如果文件名[-2::][='py'和文件名[:2]!='\uuuuuu':
模块=\uuuu导入('rpdashboard.middleware.+filename[:-3],fromlist=['middleware']))
app.middleware.append(模块.中间件)
最后,我想让会话进入的中间件是:

@web.com
异步def刷新令牌中间件(请求、处理程序):
会话=等待获取会话(请求)
if session.get('token'):
通过#待实施。。。
返回等待处理程序(请求)
中间件=刷新\u令牌\u中间件
这里的执行问题是:

#来自aiohttp#U会话
异步def get_会话(请求):
会话=请求.get(会话密钥)
如果会话为“无”:
存储=请求.get(存储\u键)
如果存储为无:
#这是提出的
引发运行时错误(
“安装aiohttp_会话中间件”
“在您的aiohttp.web.Application中”)

正如我前面所说的,会话似乎不应该在中间件中访问,而且还没有加载。那么,如何防止在会话加载中间件之前运行自定义中间件呢?或者干脆自己手动运行
aiohttp\u会话
中间件?有可能吗?

您自己可以更改订单。代码应该是这样的

def main():
app=web.Application()
会话\u key=base64.urlsafe\u b64解码(fernet.fernet.generate\u key())
aiohttp_会话设置(应用程序,加密CookieStorage(会话密钥))
#我已尝试交换两个设置功能
中间件安装(应用程序)
web.run_应用程序(应用程序)
如果查看
aiohttp\u会话设置的代码

def设置(应用程序、存储):
“”“以aiohttp方式设置库。”“”
app.middleware.append(会话\中间件(存储))

如您所见,中间件添加到此函数中。在
中间件之前添加中间件。设置(应用)
会使会话仍然无法用于请求

是的,按正确顺序添加到应用的中间件组件可以访问会话中间件设置的会话存储

aiohttp
文档涵盖了中间件组件的优先级顺序:

在内部,通过将中间件链以相反顺序应用于原始处理程序来构造单个请求处理程序,
RequestHandler
将其作为常规处理程序调用

接下来,他们用一个例子来说明这意味着什么。总之,他们使用两个中间件组件来报告其入口和出口,并按以下顺序将它们添加到
app.middleware
列表中:

此排序产生以下输出:

调用中间件1
调用中间件2
调用的处理程序函数
中间件2已完成
中间件1已完成
因此,传入的请求按照添加到
app.middleware
列表中的相同顺序沿着不同的中间件传递

接下来,
aiohttp_session
还记录了他们如何添加会话中间件,在:

该功能是以下各项的快捷方式:

app.middlewares.append(session_middleware(storage))
因此,他们的中间件组件被添加到列表的末尾。根据上面的说明,这意味着需要访问会话的任何内容都必须位于该中间件组件之后

会话中间件所做的只是将存储添加到
aiohttp\u session.storage\u键下的请求中;这使得会话可用于任何后续中间件组件。您的中间件组件不需要做任何特殊的事情,只需要在会话中间件之后添加,并将添加到请求中的存储对象留在原位。请求对象为

您的代码将所有中间件组件置于会话中间件组件之前:

middleware.setup(app)
# ...
aiohttp_session.setup(app, EncryptedCookieStorage(session_key))
这为您提供了
[…,刷新令牌中间件,…,会话中间件]
的顺序,并且您的中间件无法访问任何会话信息

所以你必须交换订单;首先调用
aiohttp\u session.setup()
,然后添加您自己的组件:

aiohttp_session.setup(app, EncryptedCookieStorage(session_key))
middleware.setup(app)
如果在访问会话存储时仍然存在问题,则意味着其中一个中间件组件正在再次删除会话存储信息

您可以使用以下命令
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)