我如何编写符合 ASGI 的中间件(同时保持与框架无关)?

How do I write an ASGI compliant middleware (while staying framework-agnostic)?

我们目前正在维护用多个 HTTP 框架(Flask、aiohttp 和 FastAPI)编写的代码。重写它们使它们都使用相同的框架目前是不可行的。我想在这些应用程序之间共享一些代码,它们非常适合中间件(日志配置、监控、身份验证……)。

最初的实现是通过子类化 Flask 完成的,并且在所有基于 Flask 的应用程序中都运行良好。但是在aiohttp或者FastAPI中是不可用的。

创建一个与框架无关的实现是可行的(理论上),今天早上我采用了一个更简单的案例并成功地将它转换为 WSGI 中间件并且能够将它集成到 Flask 应用程序中。

但是 ASGI 给我带来了一些麻烦,因为 纯 ASGI 中间件的文档不多。所有示例都显示了如何为 他们的 框架编写中间件。

official ASGI docs 在那个话题上也真的很“瘦”。据我所知,它应该看起来像这样(附带问题:“传递给构造函数的第二个参数是什么?”):

class MyMiddleware:
    def __init__(self, app, something) -> None:
        self.app = app
        self.something = something  # <- what is this second argument?

    async def __call__(self, scope, receive, send):
        print("Hello from the Middleware")
        await self.app(scope, receive, send)

我以 Starlette TimingMiddleware 为灵感,但我无法将其与 aiohttp 整合。可能是因为它们的实现方式略有不同。

考虑到 ASGI 规范中有一段中间件,并且 aiohttpStarlette 都实现了该规范,难道不应该有一种方法可以编写在两个?

如果是,我错过了什么?

不可能让相同的中间件支持所有三个框架。