SSE本身不规定JSON结构,仅负责按data:行和\n\n边界传输文本;落地需四点:统一JSON.stringify为单行、根级加type/v/ts/id字段、前端解析前校验合法性与时效性、特殊字段用base64等预编码规避协议冲突。

SSE 本身不规定 JSON 结构,它只负责把文本按 data: 行 + \n\n 边界安全地传过去。传输“复杂的 JSON 控制指令”,关键不在怎么“塞进去”,而在于怎么组织、校验、解析、容错——尤其当指令含嵌套对象、数组、二进制编码字段(如 base64)、或需强语义约束时。
以下四点是落地必须考虑的实操环节:
✅ 使用结构化 data 块,避免多行 data 拼接歧义
SSE 允许一条消息含多行 data:,但前端 EventSource 默认只取最后一行 data: 作为 event.data。若指令本身含换行(比如带缩进的 JSON 或含 \n 的字符串),直接写 data: { ... } 会出错。
正确做法:
- 后端统一将整个指令对象
JSON.stringify()成单行字符串 - 不拆成多行
data:;也不手动加\n到 JSON 内部 - 示例(合法):
data: {"cmd":"set_config","target":"panel_01","params":{"theme":"dark","refresh":3000,"widgets":["cpu","mem"]}} \n\n - ❌ 避免这样(易被截断/解析错):
data: {"cmd":"set_config", data: "target":"panel_01"} \n\n
✅ 加入语义标识与版本字段,便于前端路由和兼容
复杂指令常需区分类型、来源、协议版本。建议在 JSON 根级固定字段:
-
type: 指令类别(如"ui_update"、"auth_token_refresh"、"batch_action") -
v: 协议版本(如"v2"),前端可拒收不支持的v3+指令 -
ts: 时间戳(毫秒),用于防重放或时效判断 -
id: 全局唯一指令 ID(非 SSE 的id:字段),便于日志追踪与幂等处理
示例:
{
"type": "ui_update",
"v": "v2",
"ts": 1722982500123,
"id": "cmd_abc789xyz",
"payload": {
"action": "replace_widget",
"widget_id": "chart_cpu",
"config": {"interval": 2000, "color": "#2563eb"}
}
}
✅ 前端解析前做轻量校验,拒绝非法或过期指令
收到 event.data 后,不要直接 JSON.parse() 就执行。先做三步过滤:
- 检查是否为空或仅空白字符
-
try/catch解析,失败则丢弃并上报(不中断流) - 验证必要字段存在且类型正确(如
type是 string,ts是 number) - 可选:检查
ts是否超时(如 > 当前时间 30 秒,视为无效)
eventSource.onmessage = (e) => {
const raw = e.data.trim();
if (!raw) return;
let cmd;
try {
cmd = JSON.parse(raw);
} catch (err) {
console.warn('Invalid JSON command', raw.slice(0, 60));
return;
}
if (!cmd.type || typeof cmd.type !== 'string' || !cmd.ts || Date.now() - cmd.ts > 30000) {
console.warn('Dropped invalid/expired command', cmd);
return;
}
handleCommand(cmd); // 路由到具体处理器
};
✅ 对特殊字段做预编码,规避 JSON 与 SSE 协议冲突
若指令需传递原始二进制、不可见字符、或已含 data: 字符串,直接 JSON 序列化会破坏格式。应对策略:
- 用
base64编码二进制字段(如图标 buffer、加密密钥片段) - 对
payload中可能含\n或控制字符的字符串,启用JSON.stringify(..., null, 0)强制单行,并确保后端不额外换行 - 若指令本身要模拟 SSE 协议(比如中转推送),把整条
data: {...}\n\n当作字符串字段值,外层再 JSON 包一层,并声明"encoding": "sse-raw"提示前端特殊处理
例如传输一段需原样注入 DOM 的 HTML 片段:
{
"type": "inject_html",
"content_b64": "PGgxPkJhbmtlciBQYW5lbDwvaDE+"
}
前端解码后使用 element.innerHTML = atob(cmd.content_b64),而非直接插入 raw 字符串。
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











