使用Django Channels搭建WebSocket聊天时出现Channel Name类型错误
Django Channels WebSocket聊天功能TypeError问题解决
问题描述
使用Django Channels搭建基础WebSocket实时聊天功能,网站加载正常,但进入站点时终端抛出如下TypeError:
TypeError: Channel name must be a valid unicode string with length < 100 containing only ASCII alphanumerics, hyphens, underscores, or periods, not RedisChannelLayer(hosts=[{'host': '127.0.0.1', 'port': 6379}])
怀疑问题出在settings.py中,但当前Channel Layer配置如下,未发现异常:
CHANNEL_LAYERS = { 'default': { "BACKEND": 'channels_redis.core.RedisChannelLayer', "CONFIG": { "hosts": [('127.0.0.1', 6379)], }, }, }
完整报错回溯
Exception inside application: Channel name must be a valid unicode string with length < 100 containing only ASCII alphanumerics, hyphens, underscores, or periods, not RedisChannelLayer(hosts=[{'host': '127.0.0.1', 'port': 6379}]) Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/django/contrib/staticfiles/handlers.py", line 101, in __call__ return await self.application(scope, receive, send) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/channels/routing.py", line 62, in __call__ return await application(scope, receive, send) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/channels/security/websocket.py", line 37, in __call__ return await self.application(scope, receive, send) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/channels/sessions.py", line 47, in __call__ return await self.inner(dict(scope, cookies=cookies), receive, send) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/channels/sessions.py", line 263, in __call__ return await self.inner(wrapper.scope, receive, wrapper.send) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/channels/auth.py", line 185, in __call__ return await super().__call__(scope, receive, send) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/channels/middleware.py", line 24, in __call__ return await self.inner(scope, receive, send) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/channels/routing.py", line 116, in __call__ return await application( File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/channels/consumer.py", line 94, in app return await consumer(scope, receive, send) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/channels/consumer.py", line 58, in __call__ await await_many_dispatch( File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/channels/utils.py", line 50, in await_many_dispatch await dispatch(result) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/channels/consumer.py", line 73, in dispatch await handler(message) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/channels/generic/websocket.py", line 173, in websocket_connect await self.connect() File "/Users/alecsmith/Documents/Python Work/ChatApp/chat/consumers.py", line 10, in connect await self.channel_layer.group_add( File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/channels_redis/core.py", line 518, in group_add assert self.valid_channel_name(channel), "Channel name not valid" File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/channels/layers.py", line 160, in valid_channel_name raise TypeError(self.invalid_name_error.format("Channel", name)) TypeError: Channel name must be a valid unicode string with length < 100 containing only ASCII alphanumerics, hyphens, underscores, or periods, not RedisChannelLayer(hosts=[{'host': '127.0.0.1', 'port': 6379}])
当前consumer.py代码
import json from channels.generic.websocket import AsyncWebsocketConsumer # Class tells websocket how it should be used when doing real time events betweeen computer and server class ChatConsumer(AsyncWebsocketConsumer): # Accepts the connection of the websocket, makes a group name for the chatroom, # and add the group to the channel layer async def connect(self): self.roomGroupName = 'group_chat_gfg' await self.channel_layer.group_add( self.roomGroupName, self.channel_layer ) await self.accept() # Removes the group name and removes the group itself from the channel layer async def disconnect(self): await self.channel_layer.group_discard( self.roomGroupName, self.channel_layer ) # Recieves the message and spreads it to all other users in the chat room async def receive(self, text_data): text_data_json = json.loads(text_data) message = text_data_json['message'] username = text_data_json['username'] await self.channel_layer.group_send( self.roomGroupName, { 'type': 'sendMessage', 'message': message, 'username': username } ) # Takes the name of the user and their message and sends it to the chat room async def sendMessage(self, event): message = event['message'] username = event['username'] await self.send(text_data = json.dumps({'message': message, 'username': username}))
问题原因及解决方案
问题出在ChatConsumer的connect和disconnect方法中:
group_add和group_remove(原错误用了group_discard)的第二个参数应该是当前消费者的唯一通道名称(self.channel_name),你错误传入了通道层对象(self.channel_layer),导致不符合Channel名称格式要求。- Channels中从组移除通道的标准方法是
group_remove,而非group_discard。
修正后的ChatConsumer代码:
import json from channels.generic.websocket import AsyncWebsocketConsumer class ChatConsumer(AsyncWebsocketConsumer): async def connect(self): self.roomGroupName = 'group_chat_gfg' await self.channel_layer.group_add( self.roomGroupName, self.channel_name # 替换为当前连接的通道名称 ) await self.accept() async def disconnect(self, close_code): await self.channel_layer.group_remove( # 改用标准移除方法 self.roomGroupName, self.channel_name # 替换为当前连接的通道名称 ) async def receive(self, text_data): text_data_json = json.loads(text_data) message = text_data_json['message'] username = text_data_json['username'] await self.channel_layer.group_send( self.roomGroupName, { 'type': 'sendMessage', 'message': message, 'username': username } ) async def sendMessage(self, event): message = event['message'] username = event['username'] await self.send(text_data = json.dumps({'message': message, 'username': username}))
解释:
self.channel_name是每个WebSocket连接对应的唯一标识,完全符合Channel Layer对名称格式的要求。group_remove是Channels官方定义的从组中移除通道的方法,确保功能正常执行。
内容的提问来源于stack exchange,提问作者Alec Smith
相关产品推荐
相关产品推荐

