Node-RED:消息对象(Message)的奥秘
文章目录
关键字:
Node-RED、
消息对象、
msg详解、
msg消息传递规则、
调试技巧
JSONata表达式、
物联网数据处理
摘要
有次我帮同事排查一个奇怪的问题:他的流程明明连对了,Debug 节点却什么也不显示。
我们一起看了半小时,最后发现——他在 Function 节点里写了 return { payload: "ok" },但忘了这是个全新的对象,丢失了原始 msg.topic,导致下游 Switch 节点无法匹配条件。
那一刻我意识到:Node-RED 的灵魂,不在节点,而在消息。
在前三篇文章中,我们安装了环境、熟悉了界面、跑通了第一个流程。但如果你只把 msg.payload 当成“数据容器”,那你只用了 Node-RED 30% 的能力。今天,我们就来揭开 msg 对象的全部秘密——它到底包含什么?字段如何传递?为什么有时“明明有数据却收不到”?
这篇文章,就是你的“消息解剖课”。
一、msg 不是“变量”,而是一封“信”
想象一下:Node-RED 的每个节点,都是一个邮局。当你点击 Inject 节点,相当于寄出一封信。这封信(msg)里可以装任何东西:正文(payload)、收件人(topic)、优先级(priority)、附件(自定义字段)……
下游节点收到信后,可以:
- 只读正文
- 修改正文
- 加盖邮戳(新增字段)
- 甚至把信撕了(return null)
关键在于:这封信在整个流程中是同一个对象(引用传递),除非你主动创建新对象。
默认 msg 长什么样?
一个最简单的 Inject 节点(类型为 string,内容为 “hello”)发出的 msg 是:
{
"payload": "hello",
"topic": ""
}
如果你用的是 timestamp 类型:
{
"payload": 1729845600123, // 毫秒时间戳
"topic": ""
}
注意:topic 默认为空字符串,不是 undefined。这点在条件判断时很重要。
二、msg 的核心字段:不止 payload
虽然 payload 最常用,但 Node-RED 的强大之处在于利用其他字段实现上下文传递。
1. msg.payload:主数据载体
- 可以是任意类型:string、number、boolean、object、array、Buffer
- 大多数节点默认读写
payload - 示例:HTTP in 节点把请求体放
payload,MQTT out 节点从payload读取要发布的消息
2. msg.topic:主题/分类标识
- 常用于 MQTT(作为 topic)
- 也可用于 Switch 节点做路由
- 示例:多个传感器共用一个 MQTT in 节点,通过
topic区分设备
3. msg.headers:HTTP 请求/响应头
- HTTP in 节点会把请求头放入
msg.headers - HTTP request 节点可设置
msg.headers发送自定义头 - 示例:传递认证 token
msg.headers = { "Authorization": "Bearer xxx" };
return msg;
4. msg.statusCode:HTTP 状态码
- HTTP response 节点根据此字段返回状态(默认 200)
- 可用于返回 404、500 等错误
5. 自定义字段:你的“私有信封”
你可以随意添加字段,比如:
msg.deviceId = "sensor_001";
msg.location = "warehouse_A";
msg.timestamp = Date.now();
这些字段会一直传递到流程末尾,除非被覆盖或删除。
💡 最佳实践:用自定义字段传递元数据,避免把所有信息塞进
payload。这样下游节点逻辑更清晰。
三、消息传递的三大规则(新手必看)
规则1:节点必须 return msg,否则流程中断
Function 节点中,如果你只写:
msg.payload = "done";
// 忘记 return
那么消息流在此终止,后续节点收不到任何东西。
✅ 正确写法:
msg.payload = "done";
return msg; // 继续传递
或主动终止:
if (msg.payload < 0) {
return null; // 丢弃无效消息
}
return msg;
规则2:修改 msg 是“原地操作”,不是复制
// 错误认知:以为创建了新对象
let newMsg = msg;
newMsg.payload = "modified";
// 实际上 msg 和 newMsg 指向同一个对象!
如果你真的需要副本(比如并行处理不同分支),要用 RED.util.cloneMessage():
let copy1 = RED.util.cloneMessage(msg);
let copy2 = RED.util.cloneMessage(msg);
copy1.payload += "_branch1";
copy2.payload += "_branch2";
return [copy1, copy2]; // 多输出
规则3:连线顺序 = 执行顺序(单线程)
Node-RED 是单线程事件循环,消息按连线顺序依次处理。
即使你画了“并行”分支,实际也是串行执行(除非用 Link 节点或子流程隔离)。
四、调试 msg 的四种高效方法
方法1:Debug 节点“完整对象”模式
双击 Debug 节点,将 “Output” 从 msg.payload 改为 complete msg object。
这样就能看到所有字段,包括你自定义的。
方法2:Function 节点中打印
node.warn("当前 msg: " + JSON.stringify(msg));
// 注意:不要用 console.log,它不会显示在 Debug 面板
node.warn() 会在 Debug 面板显示黄色警告,适合临时调试。
方法3:使用 Context 查看中间状态
在 Function 节点中临时保存:
flow.set("last_msg", msg);
然后在右侧侧边栏切换到 Context 面板,展开 flow 就能看到完整结构。
方法4:用 Change 节点“窥探”而不修改
添加一个 Change 节点,规则设为:
- Set
debug_snapshot - To
$string($)(JSONata 表达式,转为字符串)
这样既保留原 msg,又能在 Debug 看到快照。
五、实战案例:用 msg 字段实现智能路由
假设你有三个设备上报数据,格式如下:
// 设备A
{ "type": "temperature", "value": 25.3 }
// 设备B
{ "type": "humidity", "value": 60 }
// 设备C
{ "type": "pressure", "value": 1013 }
目标:根据 type 字段,分别存入不同数据库表。
传统做法:写一个大 Function 节点,用 if-else 判断。
Node-RED 推荐做法:
- 用 Change 节点 提取
type到msg.topic:- Rule: Set
msg.topictomsg.payload.type
- Rule: Set
- 用 Switch 节点 按
topic路由:- Case 1:
temperature→ 温度处理流 - Case 2:
humidity→ 湿度处理流 - Case 3:
pressure→ 气压处理流
- Case 1:
- 各分支用 Change 节点 把
value提到payload:- Set
msg.payloadtomsg.payload.value
- Set
这样,每个分支只处理单一职责,逻辑清晰,易于维护。
六、常见陷阱与避坑指南
❌ 陷阱1:在 Function 中覆盖整个 msg
// 危险!丢失所有原始字段
msg = { payload: "new" };
✅ 正确做法:只修改需要的字段
msg.payload = "new";
// 保留 msg.topic, msg.headers 等
❌ 陷阱2:误以为 msg 在子流程间自动共享
子流程(Subflow)有独立的 msg 上下文。如果你在主流程设置 msg.token,进入子流程后依然存在;但子流程内部修改不会影响主流程(除非 return 出来)。
❌ 陷阱3:Buffer 数据未正确处理
串口或 TCP 节点返回的 payload 可能是 Buffer。直接 msg.payload.toString() 可能乱码。
✅ 正确做法:指定编码
let str = msg.payload.toString('utf8');
七、进阶:用 JSONata 表达式优雅操作 msg
Node-RED 内置 JSONata 引擎,可在 Change 节点、Switch 条件中直接操作 msg。
例如,把嵌套对象扁平化:
// 原始 msg.payload
{ "sensor": { "temp": 25, "unit": "C" } }
// JSONata 表达式
{ "temperature": payload.sensor.temp, "unit": payload.sensor.unit }
结果:
{ "temperature": 25, "unit": "C" }
这比写 Function 节点更简洁、安全。
写在最后:msg 是你的“上下文记忆”
Node-RED 的设计哲学是:让数据自己说话。
你不需要在全局变量里记“这是哪个设备的数据”,只需要在 msg.deviceId 里带上;
你不需要写复杂状态机,只需要在 msg.stage 里标记当前步骤。
理解了 msg,你就理解了 Node-RED 的“思维方式”。
动手练习:
创建一个流程:
- Inject 节点发送
{ "user": "alice", "action": "login" }- 用 Change 节点把
user提取到msg.topic- 用 Debug 节点显示完整 msg
观察topic是否正确设置。
724

被折叠的 条评论
为什么被折叠?



