Django DRF异步视图返回协程对象引发AssertionError问题
解决Django DRF异步API视图返回coroutine的AssertionError问题
问题描述
开发异步API视图时触发如下AssertionError:
AssertionError: 期望视图返回
Response、HttpResponse或StreamingHttpResponse对象,但实际收到了<class 'coroutine'>
源代码
async def send_stock_updates(symbol): while True: stock_info = get_one_stock_info(symbol) # 通过WebSocket发送股票信息 channel_layer = get_channel_layer() await async_to_sync(channel_layer.group_send)( 'stock_updates', # 组名称 { 'type': 'send_stock_info', 'stock_info': json.dumps(stock_info), } ) await asyncio.sleep(1) class OneStockInfoAPIView(APIView): async def get(self, request): symbol = request.query_params.get('symbol') if not symbol: return Response({'error': 'symbol参数为必填项'}, status=status.HTTP_400_BAD_REQUEST) asyncio.create_task(send_stock_updates(symbol)) return JsonResponse({'message': 'WebSocket更新已启动'}, status=200)
错误栈
WebSocket CONNECT /ws/stock-info/ [127.0.0.1:35028] 内部服务器错误: /stock-info/ Traceback (最近的调用最后): 文件 "/home/planet/Dhruvil/DRF/websoket/venv/lib/python3.10/site-packages/asgiref/sync.py", line 518, in thread_handler raise exc_info[1] 文件 "/home/planet/Dhruvil/DRF/websoket/venv/lib/python3.10/site-packages/django/core/handlers/exception.py", line 42, in inner response = await get_response(request) 文件 "/home/planet/Dhruvil/DRF/websoket/venv/lib/python3.10/site-packages/django/core/handlers/base.py", line 253, in _get_response_async response = await wrapped_callback( 文件 "/home/planet/Dhruvil/DRF/websoket/venv/lib/python3.10/site-packages/django/views/decorators/csrf.py", line 60, in _view_wrapper return await view_func(request, *args, **kwargs) 文件 "/home/planet/Dhruvil/DRF/websoket/venv/lib/python3.10/site-packages/django/views/generic/base.py", line 104, in view return self.dispatch(request, *args, **kwargs) 文件 "/home/planet/Dhruvil/DRF/websoket/venv/lib/python3.10/site-packages/rest_framework/views.py", line 511, in dispatch self.response = self.finalize_response(request, response, *args, **kwargs) 文件 "/home/planet/Dhruvil/DRF/websoket/venv/lib/python3.10/site-packages/rest_framework/views.py", line 423, in finalize_response assert isinstance(response, HttpResponseBase), ( AssertionError: 期望视图返回`Response`、`HttpResponse`或`StreamingHttpResponse`对象,但实际收到了`<class 'coroutine'>`
错误原因
- DRF版本过低:DRF 3.11以下版本不支持异步视图,
async def get会直接返回coroutine对象,而非框架预期的响应对象。 - 异步上下文错误使用
async_to_sync:channel_layer.group_send本身是异步方法,在异步函数中无需用async_to_sync包裹,错误包裹会导致内部调用异常。 - 同步函数阻塞事件循环:
get_one_stock_info如果是同步函数,直接在异步函数中调用会阻塞整个异步事件循环,破坏并发性能。 - 视图返回Django原生响应:虽然
JsonResponse合法,但DRF的异步视图更适配自身的Response对象,避免框架处理异常。
修复方案
1. 升级DRF到支持异步的版本
执行命令升级:
pip install --upgrade djangorestframework
2. 修正异步函数逻辑
- 移除
async_to_sync,直接异步调用channel_layer.group_send - 用
sync_to_async包裹同步函数get_one_stock_info,避免阻塞事件循环
3. 统一使用DRF的Response对象
替换JsonResponse为DRF提供的Response,确保框架正确处理响应。
修复后的完整代码
import asyncio import json from asgiref.sync import sync_to_async from channels.layers import get_channel_layer from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status def get_one_stock_info(symbol): # 原同步函数逻辑,示例返回模拟数据 return {"symbol": symbol, "price": 100.0} async def send_stock_updates(symbol): while True: # 用sync_to_async包裹同步函数,异步执行 stock_info = await sync_to_async(get_one_stock_info)(symbol) channel_layer = get_channel_layer() # 直接异步调用group_send await channel_layer.group_send( 'stock_updates', { 'type': 'send_stock_info', 'stock_info': json.dumps(stock_info), } ) await asyncio.sleep(1) class OneStockInfoAPIView(APIView): async def get(self, request): symbol = request.query_params.get('symbol') if not symbol: return Response({'error': 'symbol参数为必填项'}, status=status.HTTP_400_BAD_REQUEST) asyncio.create_task(send_stock_updates(symbol)) # 使用DRF的Response替代JsonResponse return Response({'message': 'WebSocket更新已启动'}, status=status.HTTP_200)
注:如果
get_one_stock_info本身是异步函数,可直接await get_one_stock_info(symbol),无需sync_to_async包裹。
内容的提问来源于stack exchange,提问作者Dhruvil Antala
相关产品推荐
相关产品推荐

