飞书机器人推送本质是符合严格json结构的http post请求,需精准匹配msg_type、字段名(如msg_type非msgtype)、嵌套层级及大小写,否则返回400或静默失败;签名验证需手动添加timestamp和url-safe base64编码的sign参数。

飞书机器人推送在 Go 中本质就是一次带特定 JSON 结构的 HTTP POST 请求,不需要 SDK,但必须严格遵循 msg_type 和字段嵌套规则,否则 400 或静默失败。
如何构造合法的 FeishuRobotPayload 结构体
飞书 API 对字段名、嵌套层级、必填项极其敏感。比如 text 类型消息必须是 {"msg_type":"text","content":{"text":"xxx"}},少一层 content 或拼错 msg_type 都会返回 400 错误。
-
MsgType字段必须小写字符串,常见值:"text"、"post"、"interactive";大小写错误(如"Text")直接拒收 - 所有 JSON tag 必须带
json:"xxx",且 key 名与飞书文档完全一致(例如不是msgtype,而是msg_type) - 如果发
post类型,Content字段需改为嵌套post对象,结构完全不同,不能复用text的 struct - 中文字符无需额外处理,
json.Marshal默认 UTF-8 编码,飞书完全支持
为什么 http.Post 经常返回 400 或无响应
绝大多数 400 错误不是网络问题,而是请求体或头信息不合规。飞书不返回详细错误原因,只给状态码,排查要从最基础处下手:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 检查
robotURL是否以https://开头,且末尾**没有多余斜杠**(.../hook/xxx/❌ →.../hook/xxx✅) - 确保
Content-Type是"application/json",且大小写完全匹配("application/json; charset=utf-8"也能过,但没必要加) - 用
curl -X POST -H "Content-Type: application/json" -d '{"msg_type":"text","content":{"text":"test"}}' YOUR_WEBHOOK_URL先验证 webhook 地址本身是否有效 - 注意:飞书对空格、换行符不敏感,但 Go 的 struct field 如果没导出(首字母小写),
json.Marshal会忽略该字段 → 所有字段名必须大写开头
签名验证开启后,http.Post 还能用吗
可以,但必须手动计算并添加 timestamp 和 sign 查询参数,http.Post 本身不处理 URL 拼接。跳过签名验证是最简方案,适合内部通知场景;若必须启用,则:
- 签名算法是
HMAC-SHA256,密钥是飞书后台提供的sign字符串(不是 base64 解码后的二进制) - 拼接字符串格式为:
timestamp\nsign,其中timestamp是当前秒级时间戳(time.Now().Unix()) - 最终 URL 形如:
https://open.feishu.cn/.../hook/xxx?timestamp=1743951234&sign=xxxxx - Go 标准库
crypto/hmac可完成计算,但要注意:sign 值需做 URL-safe base64 编码(base64.URLEncoding.EncodeToString),不是标准 base64
发送失败时,怎么快速定位是哪一环断了
不要依赖 err 的字符串内容——飞书返回的 error body 通常为空或只有模糊提示。关键看三件事:
- 打印
resp.StatusCode:200 = 成功;400 = 请求体或 URL 错;403 = 签名/时间戳失效或 webhook 地址错误;413 = 超过 20KB 限制 - 用
io.ReadAll(resp.Body)读取响应体(即使 status != 200),部分错误会在这里返回 JSON 提示(如{"code":10001,"msg":"invalid msg_type"}) - 把
payloadBytes用string()打印出来,和飞书文档的示例 JSON 逐字符比对,尤其注意引号、逗号、嵌套层级 - 测试阶段建议先硬编码一个最简
text消息,跑通后再加post或interactive复杂类型
真正容易被忽略的是:飞书 webhook 地址一旦生成,就绑定到具体群;换群必须重配,且旧地址立即失效——这个错误不会报错,只是消息发丢了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










