跳转到主要内容

为aiohttp管理会话的websockets

项目描述

https://travis-ci.org/dfee/aiohttp_session_ws.svg?branch=master https://coveralls.io/repos/github/dfee/aiohttp_session_ws/badge.svg?branch=master

简单来说:将您的websockets与用户的会话关联起来,并在您认为合适的时候关闭这些连接。

例如,假设您正在使用 aiohttp_security,并且用户选择登录或登出。使用 aiohttp_session_ws,您可以断开与他们的会话关联的打开的websocket订阅,并强制他们重新连接并重新授权他们的websocket订阅。

demo/demo.gif

基本示例

此示例中的代码片段取自本存储库的 demo 目录。

async def handle_root(request):
    return web.Response(text='Hello world', content_type="text/html")

async def handle_reset(request):
    session_ws_id = await get_session_ws_id(request)
    response = web.Response(
        text=f"Reset called on session {session_ws_id}!",
        content_type="text/plain",
    )
    await schedule_close_all_session_ws(request, response)
    await new_session_ws_id(request)
    return response

async def handle_websocket(request):
    async with session_ws(request) as wsr:
        connected_at = datetime.now()
        session_ws_id = await get_session_ws_id(request)
        while True:
            await wsr.send_str(
                f"Websocket associated with session [{session_ws_id}] "
                f"connected for {(datetime.now() - connected_at).seconds}"
            )
            await asyncio.sleep(1)
        return wsr

def make_app():
    app = web.Application(
        middlewares=[
            aiohttp_session.session_middleware(
                aiohttp_session.SimpleCookieStorage()
            ),
            session_ws_middleware,
        ]
    )
    app.router.add_get("/", handle_root)
    app.router.add_get("/reset", handle_reset)
    app.router.add_get("/ws", handle_websocket)

    setup_session_websockets(app, SessionWSRegistry())
    return app

使用 demo 文件夹中的代码,该文件夹包括一个简单的模板,用于在您的网页浏览器中与websocket交互。

叙事API

此软件包设计得简单易用。此轻量级文档并不试图取代阅读代码的需求,因此鼓励您亲自去阅读代码。

有几个组件,但如果(以及何时)您需要做更复杂的事情,您可以通过子类化来做到这一点。

SessionWSRegistry

这是 aiohttp_session_ws 的核心。

它的构建值得注意

SessionWSRegistry(self, *, id_factory, session_key)

id_factory 生成一个会话范围内的id,用于关联websockets。默认的id_factory返回UUID4,但您可以提供自己的可调用对象(也支持异步可调用对象)。id_factory 的函数签名是

id_factory(request: aiohttp.web.Request) -> typing.Hashable

基本上,返回可以作为字典键的值(字符串、整数等)。

session_key 是会话中映射到会话级 WebSocket 标识符的键的名称。默认情况下,它是合理的 aiohttp_session_ws_id

辅助工具

创建 SessionWSRegistry 之后,您不需要直接与之交互,但要知道它在您的 aiohttp.web.Application 中可用(如这样访问:app['aiohttp_session_ws_registry'])。

该对象的友好全局操作符为

  • get_session_ws_id(request)

  • new_session_ws_id(request)

  • delete_session_ws_id(request)

  • ensure_session_ws_id(request)

  • schedule_close_all_session_ws(request, response)

这些方法可以直接从 aiohttp_session_ws 导入。

注意,schedule_close_all_session_ws 接受一个响应对象。这允许我们通过 aiohttp.web.Response.force_close 终止响应的 keep-alive 状态。这意味着一旦您的用户完成接收响应,他们未完成的 WebSocket 将关闭。

这也意味着如果您有重新连接 WebSocket 的用户,您可能需要遵循以下模式

async def handle_logout(request):
    response = web.HTTPFound('/')
    await schedule_close_all_session_ws(request, response)
    await aiohttp_session.new_session(request)
    await new_session_ws_id(request)
    return response

session_ws

要跟踪 WebSocket,您将使用异步上下文管理器 session_ws。此上下文管理器升级请求,并提供其 aiothttp.web.WebSocketResponse 对应物。这样使用

async def handle_websocket(request):
    async with session_ws(request) as wsr:
        async for msg in wsr:
            await wsr.send_str(f'Heard: {ws.data}')
        return wsr

就是这样。很简单,对吧?如果您想向 aiohttp.web.WebSocketResponse 提供初始化选项(例如,支持的 WebSocket 协议),请将这些选项作为命名参数传递给 session_ws

async def handle_websocket(request):
    async with session_ws(request, protocols=('graphql-ws',)) as wsr:
        async for msg in wsr:
            await wsr.send_str(f'Heard: {ws.data}')
        return wsr

如下面的 注意 中所述,在尝试 WebSocket 连接之前,确保您的用户有一个 session_ws id 是很重要的(提示:Safari)。

使用 session_ws_middleware 自动将键添加到您的会话中。它应该位于 aiohttp_session.session_middleware 的调用栈中

web.Application(
    middlewares=[
        aiohttp_session.session_middleware(
            aiohttp_session.SimpleCookieStorage()
        ),
        session_ws_middleware,
    ]
)

最后,要设置所有这些,您将想要使用 setup 方法(鼓励您将其导入为 setup_session_ws)。

基本用法如下

web.Application(
    middlewares=[
        aiohttp_session.session_middleware(
            aiohttp_session.SimpleCookieStorage()
        ),
        session_ws_middleware,
    ]
)
setup(app, SessionWSRegistry())  # <------
# etc...
return app

注意

虽然 session_ws 在连接时生成 aiohttp_session_ws_id(如果不存在),但某些浏览器不尊重 WebSocket 升级时的 Set-Cookie(例如 Safari)。

因此,在尝试 WebSocket 连接之前,确保用户的会话中存在 aiohttp_session_ws_id 是最好的(如果使用 aiohttp_session.SimpleCookieStorageaiohttp_session.EncryptedCookieStorage)。

如果您使用更高级的存储会话引用的会话 cookie(并在服务器端存储实际值,如 aiohttp_session.RedisStorage),则 aiohttp_session_ws_id 在 cookie 中设置的时间并不重要,但仍然很重要,即用户在连接尝试之前必须有一个会话 cookie。

如果您想在会话中将 session-ws-id(通常是 aiohttp_session_ws_id)放在其他地方,或者从请求中派生它,您可以这样做。只需将 SessionWSRegistry 子类化,并修改 get_idset_iddelete_id 方法。

如果您有一个Web服务器集群,您需要将SessionWSRegistry子类化,并修改registerunregister函数,以便在消息代理上监听(例如,使用aioredis及其pubsub功能)。

项目详情


下载文件

下载您平台上的文件。如果您不确定选择哪一个,请了解有关安装包的更多信息。

源分发

aiohttp_session_ws-1.1.1.tar.gz (7.2 kB 查看散列)

上传时间

构建分发

aiohttp_session_ws-1.1.1-py3-none-any.whl (13.2 kB 查看散列)

上传时间 Python 3