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

Django与Flutter WebSocket连接失败问题求助

Django + Flutter WebSocket连接失败问题排查与解决

核心问题

Django后端通过Channels实现WebSocket服务,Flutter前端连接时返回404错误,握手无法升级为WebSocket协议。


现有代码与配置

Django端

1. Message模型

class Message(models.Model):
    room = models.ForeignKey(Room, on_delete=models.CASCADE)
    sender = models.ForeignKey(User, on_delete=models.CASCADE)
    content = models.TextField()
    timestamp = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.content

2. Consumers代码

class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name

        # Join room group
        self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        self.accept()

    def disconnect(self, close_code):
        # Leave room group
        self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

3. Routing配置(chat_app/routing.py)

websocket_urlpatterns = [
    re_path(r'ws/chat_app/(?P<room_name>\w+)/$', ChatConsumer.as_asgi()),
]

4. 项目URL配置

urlpatterns = [
    path('admin/', admin.site.urls),
    ...,
    path('ws/', include(websocket_urlpatterns)),
]

5. ASGI配置

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

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": OriginValidator(
        AuthMiddlewareStack(
            URLRouter(
                websocket_urlpatterns
            ),
            ['*']
        )
    ),
})

6. CORS配置

INSTALLED_APPS = [
    # other installed apps
    'corsheaders',
]
CORS_ALLOW_ALL_ORIGINS = True
ALLOWED_HOSTS = ['*']

Flutter端连接代码

class ChatScreen extends StatefulWidget {
  const ChatScreen({
    Key? key,
    required this.room,
  }) : super(key: key);
  final RoomModelResponse room;

  @override
  _ChatScreenState createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen> {
  final TextEditingController _controller = TextEditingController();
  final List<String> _messages = [];
  late WebSocketChannel _channel;

  @override
  void initState() {
    super.initState();
    _channel = IOWebSocketChannel.connect(
        'wss://192.168.0.11:8000/ws/chat_app/${widget.room.roomName}/');
    _channel.stream.listen((message) {
      setState(() {
        _messages.add(message);
      });
    });
  }
}

错误信息

Flutter终端报错

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: WebSocketChannelException: WebSocketChannelException: WebSocketException: Connection to 'http://192.168.0.11:8000/ws/chat_app/3wVCio/#' was not upgraded to websocket
#0      new IOWebSocketChannel._withoutSocket.<anonymous closure> (package:web_socket_channel/io.dart:119:24)
#1      Stream.handleError.<anonymous closure> (dart:async/stream.dart:931:16)
#2      _HandleErrorStream._handleError (dart:async/stream_pipe.dart:269:17)
#3      _ForwardingStreamSubscription._handleError (dart:async/stream_pipe.dart:157:13)
#4      _RootZone.runBinaryGuarded (dart:async/zone.dart:1606:10)
#5      _BufferingStreamSubscription._sendError.sendError (dart:async/stream_impl.dart:358:15)
#6      _BufferingStreamSubscription._sendError (dart:async/stream_impl.dart:376:7)
#7      _BufferingStreamSubscription._addError (dart:async/stream_impl.dart:280:7)
#8      _SyncStreamControllerDispatch._sendError (dart:async/stream_controller.dart:788:19)
#9 <…>

Django日志

Not Found: /ws/chat_app/3wVCio/
[15/Feb/2024 16:46:19] "GET /ws/chat_app/3wVCio/ HTTP/1.1" 404 4379

Postman测试结果

Error: Unexpected server response: 404
Handshake Details
Request URL: http://192.168.0.11:8000/ws/chat_app/3wVCio/
Request Method: GET
Status Code: 404 Not Found
Request Headers
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: GW/3ZvtVDOXDlKNkIwg2zw==
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Host: 192.168.0.11:8000
Response Headers
Date: Fri, 16 Feb 2024 15:28:45 GMT
Server: WSGIServer/0.2 CPython/3.12.1
Content-Type: text/html; charset=utf-8
X-Frame-Options: DENY
Content-Length: 4379
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin

解决方案

1. 修复路由重复问题

当前路由配置存在路径重复:websocket_urlpatterns中已包含ws/chat_app/前缀,项目URL又添加了ws/前缀,导致实际匹配路径为/ws/ws/chat_app/xxx/,但请求路径是/ws/chat_app/xxx/,因此返回404。

修改chat_app/routing.py:

websocket_urlpatterns = [
    re_path(r'chat_app/(?P<room_name>\w+)/$', ChatConsumer.as_asgi()),
]

项目URL配置保持不变,此时正确的请求路径为/ws/chat_app/xxx/,与前端请求一致。

2. 修复ASGI配置中的OriginValidator参数错误

OriginValidator的允许源参数位置错误,需调整为:

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

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": OriginValidator(
        AuthMiddlewareStack(
            URLRouter(websocket_urlpatterns)
        ),
        allowed_origins=['*']  # 此参数属于OriginValidator,而非AuthMiddlewareStack
    ),
})

3. Flutter端使用正确的WebSocket协议

Django开发服务器默认未配置SSL,需将wss://改为ws://:

_channel = IOWebSocketChannel.connect(
    'ws://192.168.0.11:8000/ws/chat_app/${widget.room.roomName}/');

4. 确认Channels核心配置

确保settings.py中已正确配置Channels:

INSTALLED_APPS = [
    ...,
    'channels',
    'chat_app',
    'corsheaders',
]

# 开发环境使用内存通道层,生产环境建议替换为Redis
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels.layers.InMemoryChannelLayer",
    },
}

# 指定ASGI应用入口
ASGI_APPLICATION = "project.asgi.application"

5. 适配房间ID的正则匹配

如果房间ID包含\w(字母、数字、下划线)之外的字符,需修改路由正则为:

re_path(r'chat_app/(?P<room_name>[^/]+)/$', ChatConsumer.as_asgi()),

此正则会匹配除斜杠外的所有字符,兼容更多格式的房间ID。


验证步骤

  1. 重启Django开发服务器
  2. 用Postman测试WebSocket连接,URL为ws://192.168.0.11:8000/ws/chat_app/3wVCio/
  3. 运行Flutter应用,重新尝试连接

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 18:38:14