You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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'>`

错误原因

  1. DRF版本过低:DRF 3.11以下版本不支持异步视图,async def get会直接返回coroutine对象,而非框架预期的响应对象。
  2. 异步上下文错误使用async_to_sync:channel_layer.group_send本身是异步方法,在异步函数中无需用async_to_sync包裹,错误包裹会导致内部调用异常。
  3. 同步函数阻塞事件循环:get_one_stock_info如果是同步函数,直接在异步函数中调用会阻塞整个异步事件循环,破坏并发性能。
  4. 视图返回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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 15:04:51