websocket发送json报错90%因未调用json.stringify(),send()只接受string等类型,传对象会转成"[object object]"导致后端解析失败;必须显式序列化并加try-catch防循环引用、undefined等异常。

WebSocket 发送 JSON 报错,90% 是因为没调用 JSON.stringify() 就直接传对象——这不是浏览器兼容性问题,而是协议层硬性限制:send() 只接受 string、ArrayBuffer、Blob 或 TypedArray,JS 对象会被隐式转成 "[object Object]" 字符串,后端一解析就崩。
为什么 send({type:"login"}) 会静默失败或发空消息
原生 WebSocket 不做任何类型转换。传入一个字面量对象,等价于执行 String({type:"login"}),结果就是 "[object Object]"。服务端收到后 JSON.parse() 直接抛 SyntaxError,前端却看不到错误(因为没 catch),连接还在,但消息已丢。
- 常见错误现象:
ws.onmessage没触发、服务端日志报invalid JSON、抓包看到 payload 是[object Object] - 不是所有环境都报
TypeError:Chrome 可能静默吞掉,Safari 或旧版 Edge 可能直接 throw,行为不一致 - 即使你用的是封装库(如
reconnecting-websocket),只要底层调的还是原生send(),这步就不能跳
JSON.stringify() 必须加 try-catch 的三个理由
不加 try 会导致消息静默丢失,且难以定位。尤其在用户输入含非法值时(比如表单里选了日期但没处理、上传了文件对象、用了 undefined 字段)。
-
undefined、function、Symbol字段会让JSON.stringify()返回undefined,最终send(undefined)等价于send(),发空帧 - 循环引用对象(如
obj.parent = obj)直接抛TypeError: Converting circular structure to JSON -
Date对象虽可序列化,但默认输出 ISO 字符串;若后端只认 Unix 时间戳,字段格式错位会导致逻辑错误
推荐写法:
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
const payload = { type: "submit", data: formValue, timestamp: Date.now() };<br>try {<br> ws.send(JSON.stringify(payload));<br>} catch (e) {<br> console.error("JSON serialize failed:", e, payload);<br> // 可降级为上报错误事件或 toast 提示<br>}
统一封装消息结构时必须带 type 字段和版本标识
纯 {"user":"Alice"} 这类裸对象无法路由、难扩展、升级不兼容。真实项目中建议顶层固定结构,避免每个业务自己拼。
- 最小可行结构:
{"v":"1","t":"auth/login","p":{...}},其中v是协议版本,t是 type,p是 payload - 加
v字段是为了后续兼容:比如 v2 协议加了签名字段sig,客户端可按v值决定是否校验 - 不用
type全拼而用缩写t,是为节省带宽——文本帧无压缩时,每条消息省 3–5 字节,高频场景积少成多 - payload 强制为对象(非 null/数组/原始值),保证结构一致性,方便后端统一反序列化到 struct
后端收到 JSON 后不能直接 json.loads() 就信任
前端序列化只是第一步,后端反序列化才是风险高发区。未校验的 json.loads() 或 json.Unmarshal() 容易被构造数据击穿。
- 必须先判断
event.data类型:WebSocket 可能发来Blob或ArrayBuffer,直接JSON.parse()会报错 - 必须
try/catch SyntaxError:非法 JSON(如截断、控制字符)不能让整个连接中断 - 必须做字段存在性检查:比如
data.t不存在就拒绝处理,而不是默认 fallback - 对关键操作(如支付、删除),建议 payload 内嵌
nonce和expires,服务端验证时效性,防重放
真正容易被忽略的点是:消息边界和分片。WebSocket 协议允许单条消息跨多个帧发送,但大多数前端库(包括原生 API)已自动组装好完整 event.data。只有当你手动处理 binaryType 或用低层库(如 ws 的 receiver)时,才需自己维护帧缓冲——这点绝大多数人根本碰不到,但一旦踩中,JSON.parse() 就会因数据不全而报错。










