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

Django+Next.js实时聊天项目:Channels依赖未识别及WebSocket连接失败求助

Django + Next.js 实时聊天项目问题排查与解决

问题描述

1. Channels 包导入与环境依赖异常

已安装channels和channels-redis,但代码中import channels.db无法被解析,requirements.txt显示这两个包未在当前环境中安装,使用virtualenv和poetry创建环境均出现该问题。

2. WebSocket 连接失败

前端尝试连接后端时,报错WebSocket connection to 'ws://127.0.0.1:8000/' failed,切换为ws://localhost:8000/后问题依旧。


项目相关代码

settings.py

WSGI_APPLICATION = 'backend.wsgi.application'
ASGI_APPLICATION = "backend.asgi.application"
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("redis", "6379")],
        },
        }
    }

asgi.py

import os

from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from . import routing

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')

django_asgi_app = get_asgi_application()

# application = get_asgi_application()
application = ProtocolTypeRouter({
    "http": django_asgi_app,
    "websocket": AuthMiddlewareStack(
        URLRouter(
            routing.websocket_urlpatterns
        )
    ),
})

routing.py

from django.urls import re_path
from chat.consumers import ChatConsumer

websocket_urlpatterns = [
    re_path(r'ws/chat/', ChatConsumer.as_asgi()),
]

consumers.py

import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.layers import get_channel_layer
from channels.db import database_sync_to_async
from .models import Message


class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_group_name = 'chat'
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )
        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    @database_sync_to_async
    def create_message(self, message, username):
        message_obj = Message.objects.create(
            content=message,
            username=username
        )
        return message_obj

    async def receive(self, text_data):
        data = json.loads(text_data)
        message = data['message']
        username = data['username']

        # Create a new message object and save it to the database
        message_obj = await self.create_message(message, username)

        # Send the message to the group
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message_obj.content,
                'username': message_obj.username,
                'timestamp': str(message_obj.timestamp)
            }
        )

    async def chat_message(self, event):
        message = event['message']
        username = event['username']
        timestamp = event['timestamp']

        # Send the message to the websocket
        await self.send(text_data=json.dumps({
            'message': message,
            'username': username,
            'timestamp': timestamp
        }))

前端代码(Next.js)

import { useState, useEffect } from "react";
import useWebSocket, { ReadyState } from "react-use-websocket";

const ChatEngine = (props) => {
  const [showChat, setShowChat] = useState(false);
  const [socket, setSocket] = useState(null);
  const [username, setUsername] = useState("");
  const [email, setEmail] = useState("");
  const [message, setMessage] = useState("");
  const [messages, setMessages] = useState([]);
  const [welcomeMessage, setWelcomeMessage] = useState("");

  const { readyState } = useWebSocket("ws://127.0.0.1:8000/", {
    onOpen: () => {
      console.log("Connected!");
    },
    onClose: () => {
      console.log("Disconnected!");
    },
    // New onMessage handler
    onMessage: (e) => {
      console.log(e);
    },
  });

  const connectionStatus = {
    [ReadyState.CONNECTING]: "Connecting",
    [ReadyState.OPEN]: "Open",
    [ReadyState.CLOSING]: "Closing",
    [ReadyState.CLOSED]: "Closed",
    [ReadyState.UNINSTANTIATED]: "Uninstantiated",
  }[readyState];

  console.log("connection status: ", connectionStatus);

问题解决方案

问题1:Channels 包导入与环境依赖异常

  1. 校验环境与包安装状态
    • 激活对应环境后,执行pip list(virtualenv)或poetry show(poetry),确认channels和channels-redis是否存在。
    • 若未安装,virtualenv环境下执行pip install channels channels-redis;poetry环境下执行poetry add channels channels-redis。
  2. 修正依赖文件生成方式
    • virtualenv环境:安装完成后执行pip freeze > requirements.txt更新依赖列表。
    • poetry环境:执行poetry export -f requirements.txt --output requirements.txt --without-hashes生成标准requirements文件。
  3. 版本兼容校验
    若导入仍报错,尝试安装稳定版本:pip install channels==4.0.0 channels-redis==4.1.0,避免版本迭代导致的模块路径变更。

问题2:WebSocket 连接失败

  1. 修正前端连接路径
    后端路由配置的WebSocket路径为ws/chat/,前端需对应修改连接URL:
    const { readyState } = useWebSocket("ws://127.0.0.1:8000/ws/chat/", {
      // 原有配置保留
    });
    
  2. 使用ASGI模式启动后端
    Django默认runserver需开启ASGI模式支持WebSocket:
    python manage.py runserver --asgi
    
    或使用Daphne服务器启动:
    daphne backend.asgi:application
    
  3. Redis服务与配置校验
    • 本地开发时,将CHANNEL_LAYERS中的主机名改为127.0.0.1:
      CHANNEL_LAYERS = {
          'default': {
              'BACKEND': "channels_redis.core.RedisChannelLayer",
              "CONFIG": {
                  "hosts": [("127.0.0.1", "6379")],
              },
          }
      }
      
    • 执行redis-cli ping确认Redis服务正常运行,返回PONG则服务正常。
  4. 跨域配置(端口不一致时)
    若Next.js运行在3000端口,Django在8000端口,需配置跨域:
    • 安装django-cors-headers:pip install django-cors-headers
    • 修改settings.py:
      INSTALLED_APPS = [
          # 其他应用
          'corsheaders',
      ]
      
      MIDDLEWARE = [
          'corsheaders.middleware.CorsMiddleware',
          'django.middleware.common.CommonMiddleware',
          # 其他中间件
      ]
      
      CORS_ALLOW_ALL_ORIGINS = True  # 开发环境临时配置,生产环境需指定域名
      CORS_ALLOW_CREDENTIALS = True
      
    • 为WebSocket添加主机校验:
      from channels.security.websocket import AllowedHostsOriginValidator
      
      application = ProtocolTypeRouter({
          "http": django_asgi_app,
          "websocket": AllowedHostsOriginValidator(
              AuthMiddlewareStack(
                  URLRouter(
                      routing.websocket_urlpatterns
                  )
              )
          ),
      })
      

内容的提问来源于stack exchange,提问作者phanio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 14:30:56