Django Channels的receive方法不自动解析JSON,需手动json.loads()并处理空消息、UnicodeDecodeError等边界情况;text_data和bytes_data不会同时非空,必须分别处理。

直接说结论:Django Channels 的 receive 方法不是“自动解析 JSON”的黑盒,它只传原始字节或字符串,你必须自己 json.loads(),且要处理 UnicodeDecodeError 和空消息等边界情况——否则前端发个带 emoji 的消息,后端直接断连。
为什么 receive 收到的是 bytes 而不是 dict
Django Channels 的 AsyncWebsocketConsumer.receive 方法设计上就是“原生通道”,不预设消息格式。它只保证把客户端发来的 WebSocket 帧内容原样交给你,类型取决于前端怎么发:
- 前端用
socket.send("hello")→ 后端text_data是str - 前端用
socket.send(JSON.stringify(obj))→ 后端text_data是str,但内容是 JSON 字符串,需手动json.loads() - 前端用
socket.send(blob)或ArrayBuffer→ 后端bytes_data是bytes,需按协议解码(比如 UTF-8 或自定义二进制格式)
常见错误现象:JSONDecodeError: Expecting value,本质是前端发了空字符串、换行符或未 stringify 的对象,后端却无条件 json.loads(text_data)。
receive 方法里必须做的三件事
别跳过任何一步,漏一个就可能让连接静默关闭:
立即学习“Python免费学习笔记(深入)”;
华为云 Python SDK 查询工具。自动发现已安装 SDK 的服务包、Client类、API 方法、入参详情、类信息(__init__ 参数、属性、类型、描述),支持关键字搜索,快速定位目标 API 使用方法。
- 先检查
text_data是否为空:if not text_data.strip(): return(空格、换行、\x00 都得过滤) - 再
try/except包住json.loads(),捕获json.JSONDecodeError和UnicodeDecodeError,并调用await self.close(code=4002)显式断连(避免悬挂连接) - 最后校验解析后的
dict是否含必要字段,比如"type"和"content",缺失就return或报错
示例片段:
async def receive(self, text_data=None, bytes_data=None):
if text_data:
text_data = text_data.strip()
if not text_data:
return
try:
data = json.loads(text_data)
except (json.JSONDecodeError, UnicodeDecodeError) as e:
logger.warning(f"Invalid JSON from {self.scope['user']['id']}: {e}")
await self.close(code=4002)
return
if "type" not in data or "content" not in data:
await self.close(code=4004)
return
# 后续逻辑...
text_data 和 bytes_data 不能同时非空
WebSocket 协议规定一帧只能是文本帧或二进制帧,Channels 严格遵循这点:text_data 和 bytes_data 永远不会同时有值。实际开发中几乎全用 text_data(JSON 为主),bytes_data 仅用于文件传输、音视频流等特殊场景。
- 误判风险:写成
if text_data or bytes_data:看似稳妥,实则掩盖了前端发错帧类型的 bug - 正确姿势:明确分支,
if text_data:处理 JSON;elif bytes_data:处理二进制(并加长度限制,防内存爆) - 性能影响:
bytes_data不经编码转换,比text_data更快,但 Django ORM 和日志系统对 bytes 支持弱,调试成本高
前端发消息时最容易踩的坑
后端 receive 写得再稳,前端一个操作就能让它失效:
- 用
JSON.stringify()但没 catch 序列化错误(比如循环引用对象),导致发出去的是"undefined"字符串 - 消息体里混入不可见控制字符(如
\u2028、\u2029),Pythonjson.loads()会直接抛JSONDecodeError - 没设请求头
Content-Type: application/json(虽然 WebSocket 不强制,但某些代理或中间件会据此过滤) - 前端重连逻辑里重复调用
socket.send(),导致后端收到重复或乱序消息
建议在前端发送前加一层校验:
function safeSend(socket, obj) {
try {
const str = JSON.stringify(obj);
// 过滤掉 JS 中合法但 JSON 标准不支持的行分隔符
const clean = str.replace(/[\u2028\u2029]/g, '');
socket.send(clean);
} catch (e) {
console.error("Failed to stringify", obj, e);
}
}
真正难的不是写 receive,而是让前后端对“什么算一条合法消息”达成共识。协议文档写两行,调试时间省两天。

















