如何在 Django API GET 请求中与 websocket 通信?

Posted

技术标签:

【中文标题】如何在 Django API GET 请求中与 websocket 通信?【英文标题】:How do I communicate with a websocket within a Django API GET request? 【发布时间】:2020-05-08 04:27:17 【问题描述】:

我正在尝试从 Django REST API 方法中获取来自 websocket 服务器(使用 websocketsasyncio 实现)的响应。结构如下:

Django 应用

(This does not work, but illustrates the idea)

class AnAPI(views.APIView):

    async def get(self, request):
        try:
            timeout = 5
            try:
                ws_conn = await asyncio.wait_for(websockets.connect(WS_STRING), timeout)
            except ConnectionTimeoutError as e:
                 <.....>

            await ws_conn.send(...)
            response = await ws_conn.recv()
            ws_conn.close()
            return Response(...)
        except Exception as e:
            print(e)
            return Response(...)

WS 服务器

ws_server = websockets.serve(...)
asyncio.get_event_loop().run_until_complete(ws_server)
asyncio.get_event_loop().run_forever()

显然,这使得 Django GET 方法返回 &lt;class 'coroutine'&gt;

AssertionError: Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` to be returned from the view, but received a `<class 'coroutine'>`

任何指针将不胜感激!


编辑:感谢所有回答的人!我一直在寻找一个轻量级的解决方案,因为这可能是 Django 应用程序需要与 WS 服务器交互的唯一地方。我最终采用了@Joran 的解决方案,但将所有内容打包到一个辅助函数中,如下所示:

class AnAPI(views.APIView):

    def get(self, request):
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        result = loop.run_until_complete(my_async_helper_function())
        return Response(...)


async def my_async_helper_function():

    try:
            timeout = 5
            try:
                ws_conn = await asyncio.wait_for(websockets.connect(WS_STRING), timeout)
            except ConnectionTimeoutError as e:
                 <.....>

            await ws_conn.send(...)
            response = await ws_conn.recv()
            await ws_conn.close()
            return ...
        except Exception as e:
            print(e)
            await ws_conn.close()
            return ...


【问题讨论】:

我认为您不能返回异步端点,可以吗? ...只需摆脱异步,我认为它会起作用...但它更常见于 websockets 一个 js 客户端连接到套接字,而不是一个端点......你基本上失去了几乎所有的好处和痛苦通过像正常的 api 调用一样重复连接到 websockets 的所有不好的部分 @JoranBeasley 我之前尝试过,但我想如果我想在函数中使用await,它会告诉我必须在前面使用async 不,我认为您可以使用 asyncio 来调用函数而无需等待...您可能需要更多控件来检测何时建立连接 @JoranBeasley 是的,这是一种奇怪的情况,WS 服务器主要与 js 客户端交互,但对于这个特定的端点,如果我能够收集一些仅在该端点上可用的信息,那就太好了WS 服务器.. loop = asyncio.new_event_loop(); asyncio.set_event_loop(loop); result = loop.run_until_complete(some_async_fn("django")) (或使用更简单的东西(我很确定那里有一个纯 python websocket-client 包,使用起来非常简单)) 【参考方案1】:

您不能将异步与 django 响应一起使用(我认为...)

你可以试试

import asyncio

class AnAPI(views.APIView):
    def get(self, request):
        try:
        timeout = 5
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop) 
        try:                
            ws_conn = loop.run_until_complete(websockets.connect(WS_STRING), timeout)               
        except ConnectionTimeoutError as e:
             <.....>

        loop.run_until_complete(ws_conn.send(...))
        response = loop.run_until_complete(ws_conn.recv())
        ws_conn.close()
        return Response(...)

另外,我相信https://pypi.org/project/websocket_client/ 提供了一个非常易于使用的非异步接口

【讨论】:

谢谢哥们!更新了我的问题(使用采用的解决方案) 工作得相当好......老实说,我会为这个特定的用例推荐其他包......但是有一个辅助功能可能不会受到伤害

以上是关于如何在 Django API GET 请求中与 websocket 通信?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 django rest 框架从 GET 请求的查询参数中过滤多个 id?

如何在 django rest 框架中访问获取请求数据

无法从 reactjs 中的 axios 向 id=1 文章的 django REST API 后端发出 GET 请求

在发送新的 GET 请求之前删除请求记录 Django

如何在不登录 jwt django rest-framework 的情况下获取请求数据

Django REST framework框架之GET, POST, PUT, PATCH, DELETE等API请求接口设计