news 2026/7/7 8:37:26

飞书富文本消息换行实战:告别\n失效,用JSON数组构建清晰通知

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
飞书富文本消息换行实战:告别\n失效,用JSON数组构建清晰通知

1. 为什么飞书消息换行这么麻烦?

第一次用飞书机器人发消息时,我也被换行问题坑得不轻。明明在普通文本里加个\n就能搞定的事,到了飞书这里偏偏行不通。后来才发现,飞书的消息推送机制和其他平台不太一样,它把消息分成了几种类型,每种类型的处理方式都不同。

最常用的普通文本消息(text类型)确实不支持\n换行,这是飞书API的设计决定的。我猜可能是为了防止消息注入攻击,或者是为了统一消息格式。不过飞书提供了更强大的富文本消息(post类型),这才是解决换行问题的正确姿势。

post类型的消息本质上是个迷你网页,你可以把它想象成一张可以自由排版的电子海报。它支持文字、链接、图片、@提醒等各种元素,而且每行内容都可以精确控制。代价就是需要构造一个稍微复杂的JSON结构,但熟悉之后其实也没那么可怕。

2. 解密飞书富文本消息的JSON结构

先来看个完整的post消息模板:

{ "msg_type": "post", "content": { "post": { "zh_cn": { "title": "你的消息标题", "content": [ [ {"tag": "text", "text": "第一行内容"}, {"tag": "a", "text": "超链接", "href": "https://example.com"} ], [ {"tag": "text", "text": "第二行内容"}, {"tag": "at", "user_id": "ou_123456789"} ] ] } } } }

这个结构的关键点在于content数组。数组的每个元素代表一行,行内的元素可以是:

  • 普通文本(tag: text)
  • 超链接(tag: a)
  • @提醒(tag: at)
  • 图片(tag: img)

我刚开始用的时候总搞混层级关系,后来发现可以用"行→列"的思路来理解:

  1. 最外层content数组控制行数
  2. 每行的子数组控制该行的内容元素
  3. 每个元素用tag标识类型

3. 实战:构建多行通知消息

假设我们要发送一个项目更新通知,包含三部分:

  1. 更新说明
  2. 详情链接
  3. @相关责任人

用Java代码构建这个JSON的完整示例:

public String buildFeishuMessage() { StringBuilder builder = new StringBuilder(); // 消息头 builder.append("{\"msg_type\":\"post\",\"content\":{\"post\":{\"zh_cn\":{"); builder.append("\"title\":\"项目更新通知\","); builder.append("\"content\":["); // 第一行:更新说明 builder.append("[[{\"tag\":\"text\",\"text\":\"项目v2.3.0已发布,主要更新:\"},"); builder.append("{\"tag\":\"text\",\"text\":\"修复登录异常问题\"}]],"); // 第二行:详情链接 builder.append("[[{\"tag\":\"text\",\"text\":\"详情请查看:\"},"); builder.append("{\"tag\":\"a\",\"text\":\"发布说明\",\"href\":\"https://yourdomain.com/release\"}]],"); // 第三行:@负责人 builder.append("[[{\"tag\":\"text\",\"text\":\"负责人:\"},"); builder.append("{\"tag\":\"at\",\"user_id\":\"ou_18eac8d17ad4f02e8bbbb\"}]]"); // 消息尾 builder.append("]}}}}"); return builder.toString(); }

实际发送时,建议用JSON库而不是字符串拼接。比如用Jackson库:

ObjectMapper mapper = new ObjectMapper(); ObjectNode contentNode = mapper.createObjectNode(); ArrayNode contentArray = mapper.createArrayNode(); // 构建第一行 ArrayNode line1 = mapper.createArrayNode(); line1.add(mapper.createObjectNode().put("tag", "text").put("text", "第一行内容")); contentArray.add(line1); // 构建第二行 ArrayNode line2 = mapper.createArrayNode(); line2.add(mapper.createObjectNode().put("tag", "text").put("text", "第二行内容")); contentArray.add(line2); // 组装完整消息 ObjectNode message = mapper.createObjectNode(); message.put("msg_type", "post"); message.set("content", mapper.createObjectNode() .set("post", mapper.createObjectNode() .set("zh_cn", mapper.createObjectNode() .put("title", "测试消息") .set("content", contentArray)))); String json = mapper.writeValueAsString(message);

4. 避坑指南:那些年我踩过的JSON坑

4.1 特殊字符转义问题

最常遇到的问题是文本中包含JSON特殊字符,比如引号、反斜杠等。比如要发送这样的内容:

用户输入:"Hello\nWorld"

直接放到JSON里会报错,必须转义:

String text = "用户输入:\\\"Hello\\\\nWorld\\\"";

建议对所有用户输入的内容都做转义处理,可以用StringEscapeUtils.escapeJson(Apache Commons Lang)或者自己写转义逻辑。

4.2 中文字符编码

遇到过中文变成unicode编码的问题,比如:

{"text":"\u4e2d\u6587"}

解决方法是在创建ObjectMapper时配置:

ObjectMapper mapper = new ObjectMapper(); mapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, false);

4.3 数组越界错误

content数组必须至少包含一个子数组(哪怕空数组),否则会报格式错误。这是正确的空消息:

"content": [[]]

而这样会报错:

"content": []

4.4 消息长度限制

飞书post消息有长度限制(约20KB),超长消息会被截断。解决方法:

  1. 精简内容,去掉冗余信息
  2. 把长文本拆分成多条消息
  3. 改用飞书文档,然后发文档链接

5. 高级技巧:动态构建复杂消息

当消息内容需要动态生成时,可以封装一个工具类。这是我常用的FeishuMessageBuilder:

public class FeishuMessageBuilder { private final ObjectMapper mapper = new ObjectMapper(); private final ObjectNode message; private final ArrayNode content; public FeishuMessageBuilder(String title) { this.message = mapper.createObjectNode(); this.content = mapper.createArrayNode(); message.put("msg_type", "post"); message.set("content", mapper.createObjectNode() .set("post", mapper.createObjectNode() .set("zh_cn", mapper.createObjectNode() .put("title", title) .set("content", content)))); } public FeishuMessageBuilder addLine(String text) { ArrayNode line = mapper.createArrayNode(); line.add(mapper.createObjectNode() .put("tag", "text") .put("text", text)); content.add(line); return this; } public FeishuMessageBuilder addLineWithLink(String text, String linkText, String url) { ArrayNode line = mapper.createArrayNode(); line.add(mapper.createObjectNode() .put("tag", "text") .put("text", text)); line.add(mapper.createObjectNode() .put("tag", "a") .put("text", linkText) .put("href", url)); content.add(line); return this; } public String build() throws JsonProcessingException { return mapper.writeValueAsString(message); } }

使用示例:

FeishuMessageBuilder builder = new FeishuMessageBuilder("每日报告"); builder.addLine("今日新增用户:42") .addLineWithLink("详情:", "点击查看", "https://example.com/stats") .addLine("负责人:@张三"); String json = builder.build();

6. 测试与调试技巧

发送飞书消息最头疼的就是调试JSON格式。推荐几个方法:

6.1 使用JSON格式化工具

先把构建好的JSON字符串用在线工具(如jsonformatter.org)格式化,检查结构是否正确。常见的格式问题:

  • 多余的逗号
  • 缺失的引号
  • 嵌套层级错误

6.2 飞书开放平台调试工具

飞书开放平台提供了消息调试工具(在开发者后台→消息卡片工具),可以实时预览消息效果。

6.3 逐步构建法

不要一次性写完整个JSON,而是逐步添加元素测试:

  1. 先发只有标题的空消息
  2. 添加一行纯文本
  3. 添加带链接的行
  4. 添加@提醒

6.4 错误处理

飞书API返回的错误信息有时候不太直观,主要关注:

  • code为0表示成功
  • code为19001通常是JSON格式错误
  • code为99991668可能是权限问题

建议的异常处理代码:

try { String response = sendToFeishu(json); JsonNode node = mapper.readTree(response); if (node.path("code").asInt() != 0) { log.error("飞书消息发送失败:{} - {}", node.path("code").asText(), node.path("msg").asText()); } } catch (Exception e) { log.error("飞书消息发送异常", e); }

7. 性能优化建议

当需要高频发送消息时(比如监控告警),有几个优化点:

7.1 复用ObjectMapper

ObjectMapper创建成本高,应该全局复用:

private static final ObjectMapper MAPPER = new ObjectMapper();

7.2 使用消息模板

对于固定格式的消息,可以准备模板然后替换变量:

String template = "{\"text\":\"${content}\"}"; String message = template.replace("${content}", "实际内容");

7.3 异步发送

不要阻塞主线程,用线程池或消息队列异步发送:

executor.submit(() -> { sendToFeishu(buildMessage()); });

7.4 批量发送

多条消息合并发送(注意飞书可能有频率限制):

List<String> messages = getPendingMessages(); if (!messages.isEmpty()) { String batch = "[" + String.join(",", messages) + "]"; sendToFeishu(batch); }

8. 替代方案比较

除了post类型,飞书还支持其他消息类型:

类型换行支持适合场景复杂度
text不支持简单文本通知
post支持富文本通知
card支持交互式卡片
image不支持纯图片

选择建议:

  • 纯文本且不需要换行:text类型
  • 需要换行或简单排版:post类型
  • 需要按钮交互:card类型
  • 只发图片:image类型

9. 真实案例:服务器监控告警

这是我们生产环境用的服务器监控通知:

public String buildServerAlert(String server, String metric, String value, String threshold) { return new FeishuMessageBuilder("服务器告警") .addLine("服务器:" + server) .addLine("监控项:" + metric) .addLine("当前值:" + value + "(阈值:" + threshold + ")") .addLineWithLink("", "查看详情", getGrafanaUrl(server)) .addLine("处理人:@运维团队") .build(); }

效果:

[服务器告警] 服务器:web-server-01 监控项:CPU使用率 当前值:95%(阈值:80%) 查看详情:https://grafana.example.com 处理人:@运维团队

10. 最佳实践总结

经过多个项目的实践,我总结了这些经验:

  1. 始终优先使用post类型发送重要通知
  2. 用JSON库而不是字符串拼接构建消息
  3. 对所有动态内容进行转义处理
  4. 添加适当的空行提升可读性
  5. 重要消息加上@提醒确保被看到
  6. 在测试环境充分验证消息格式
  7. 监控消息发送失败的情况

刚开始用飞书消息API时,我也觉得这个JSON结构有点复杂。但熟悉之后发现,这种设计其实提供了很大的灵活性。现在我们的监控系统、CI/CD通知、日报周报都通过这套机制推送,效果比原来的纯文本好太多了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/3 9:55:37

覆盖文理工商各专业需求:gradpaper 毕业论文功能的定制化设计

Gradpaper-免费查重复率aigc检测/开题报告/毕业论文/智能排版/文献综述/课程论文。 Gradpaper论文智能生成软件&#xff0c;10分钟生成万字毕业论文、期刊论文、文献综述、PPT&#xff0c;Agc查重、降重报告、文献资料。只需一个标题&#xff0c;从开题报告到答辩一键生成软件&…

作者头像 李华
网站建设 2026/7/3 22:17:02

如何在5分钟内用EfficientNet-PyTorch完成终极图像分类任务

如何在5分钟内用EfficientNet-PyTorch完成终极图像分类任务 【免费下载链接】EfficientNet-PyTorch A PyTorch implementation of EfficientNet 项目地址: https://gitcode.com/gh_mirrors/ef/EfficientNet-PyTorch 你是否曾经为复杂的深度学习模型配置而头疼&#xff1…

作者头像 李华
网站建设 2026/7/3 23:01:19

CGAL实战:泊松表面重建从理论到代码实现

1. 泊松表面重建&#xff1a;从点云到三维模型的魔法 第一次接触泊松表面重建时&#xff0c;我被它的效果惊艳到了——就像变魔术一样&#xff0c;能把一堆杂乱的点云变成光滑的三维模型。这让我想起小时候用橡皮泥捏造型&#xff0c;只不过现在是计算机在帮我们完成这个塑形过…

作者头像 李华
网站建设 2026/7/3 1:48:23

Sunshine游戏串流服务器深度解析:从零打造个人云游戏平台

Sunshine游戏串流服务器深度解析&#xff1a;从零打造个人云游戏平台 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine 还在为无法随时随地畅玩书房里的3A大作而烦恼吗&#xff1f;想…

作者头像 李华