【问题标题】:How do I communicate with a websocket within a Django API GET request?如何在 Django API GET 请求中与 websocket 通信?
【发布时间】: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 包,使用起来非常简单))

标签: python django websocket django-rest-framework


【解决方案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/ 提供了一个非常易于使用的非异步接口

【讨论】:

  • 谢谢哥们!更新了我的问题(使用采用的解决方案)
  • 工作得相当好......老实说,我会为这个特定的用例推荐其他包......但是有一个辅助功能可能不会受到伤害
猜你喜欢
  • 1970-01-01
  • 2020-09-09
  • 2018-01-08
  • 2017-10-30
  • 2011-03-30
  • 2019-11-28
  • 2018-01-03
  • 2016-05-27
  • 1970-01-01
相关资源
最近更新 更多