如何在 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 服务器(使用 websockets
和 asyncio
实现)的响应。结构如下:
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 方法返回 <class 'coroutine'>
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?
无法从 reactjs 中的 axios 向 id=1 文章的 django REST API 后端发出 GET 请求
如何在不登录 jwt django rest-framework 的情况下获取请求数据
Django REST framework框架之GET, POST, PUT, PATCH, DELETE等API请求接口设计