如何用ThinkPHP封装Webhook并通过钉钉机器人发送Markdown格式告警通知?

2026-04-27 19:071阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1083个文字,预计阅读时间需要5分钟。

如何用ThinkPHP封装Webhook并通过钉钉机器人发送Markdown格式告警通知?

直接使用+cURL或+file_get_contents+发起POST请求即可,无需安装SDK。锤子机器人只认+POST+application/json+。传递过去的是标准的JSON格式,ThinkPHP自带的+Http+类或原生+cURL+都能搞定。

关键不是“能不能发”,而是「怎么发才不被钉钉拒收」——比如签名失效、timestampsign 不匹配、JSON 格式错一个逗号就 400。

  • 必须带上 timestamp(毫秒时间戳)和 sign(HMAC-SHA256 签名),否则返回 {"errcode":310000,"errmsg":"invalid signature"}
  • sign 计算时,原始字符串是 "<timestamp>\n<secret>"</secret></timestamp>,注意换行符 \n 是必须的,不是空格也不是回车
  • Webhook 地址里的 access_tokensign 要动态拼,不能硬编码在 URL 里再 GET 请求 —— 钉钉只接受 POST body 里带签名

Markdown 消息体怎么写才不被截断或渲染失败

钉钉对 Markdown 支持有限,不是所有 GitHub 风格都管用。最稳妥的是用 text 类型打底,真要格式就上 markdown 类型,但得避开它的几个硬限制。

  • markdown 类型下,标题最多支持到 ##(即二级标题),### 及以下会被当普通文本
  • 表格必须用完整语法:|列1|列2|\n|---|---|\n|值1|值2|,少一行或缺竖线就整个消息变纯文本
  • 链接写法必须是 [文字](https://xxx)<a href="..."> 或纯 URL 都不解析
  • 别在 markdown 里混用 HTML 标签,钉钉会直接过滤掉整段内容

示例可用的最小可靠结构:

立即学习“PHP免费学习笔记(深入)”;

{ "msgtype": "markdown", "markdown": { "title": "服务告警", "text": "## [错误] <code>Db::table('user')->where('id', 0)->find()</code>\n> 时间:2024-06-12 14:22:05\n> 服务器:<code>web-prod-03</code>" } }

为什么本地测试通、上线就 400 或无响应

常见原因不是代码问题,而是环境差异导致签名或请求被拦。

  • 服务器时间不准:timestamp 和钉钉服务器时间差超过 1 小时,sign 直接失效 —— 用 ntpdate -u ntp.aliyun.com 同步下
  • PHP 开启了 open_basedir 或禁用了 curl:检查 phpinfo()disable_functions 是否含 curl_exec
  • Web 服务器(Nginx/Apache)限制了 POST body 大小:钉钉要求单条消息 ≤ 2048 字符,但如果你拼了个超长日志进去,可能被中间件截断
  • ThinkPHP 的 Env 配置没加载对:access_tokensecret 建议放 .env,别写死在控制器里,避免误提交到 Git

封装成 ThinkPHP 公共方法要注意什么

别写成静态工具类就完事。钉钉 Webhook 是外部依赖,必须考虑失败重试、日志记录、敏感信息隔离。

  • 方法入参建议只收 $title$text$type = 'markdown',其他如 token、secret、超时时间全部从配置读(config('dingtalk.webhook')
  • HTTP 超时设为 3 秒以内,钉钉响应通常
  • 失败时别静默吞掉错误,至少记到 think/log,包含原始响应体(比如 {"errcode":310000,...}
  • 如果用 Http::post(),记得手动设 header:['Content-Type' => 'application/json'],否则默认是 application/x-www-form-urlencoded,钉钉直接 400

复杂点在于签名生成和错误分类 —— timestamp 和 sign 必须原子生成,不能先取时间再算 sign 中间隔了几毫秒;而钉钉返回的 errcode 里,310000 是签名错,320000 是 access_token 过期,330000 是调用太频繁,每种该走不同处理路径。

本文共计1083个文字,预计阅读时间需要5分钟。

如何用ThinkPHP封装Webhook并通过钉钉机器人发送Markdown格式告警通知?

直接使用+cURL或+file_get_contents+发起POST请求即可,无需安装SDK。锤子机器人只认+POST+application/json+。传递过去的是标准的JSON格式,ThinkPHP自带的+Http+类或原生+cURL+都能搞定。

关键不是“能不能发”,而是「怎么发才不被钉钉拒收」——比如签名失效、timestampsign 不匹配、JSON 格式错一个逗号就 400。

  • 必须带上 timestamp(毫秒时间戳)和 sign(HMAC-SHA256 签名),否则返回 {"errcode":310000,"errmsg":"invalid signature"}
  • sign 计算时,原始字符串是 "<timestamp>\n<secret>"</secret></timestamp>,注意换行符 \n 是必须的,不是空格也不是回车
  • Webhook 地址里的 access_tokensign 要动态拼,不能硬编码在 URL 里再 GET 请求 —— 钉钉只接受 POST body 里带签名

Markdown 消息体怎么写才不被截断或渲染失败

钉钉对 Markdown 支持有限,不是所有 GitHub 风格都管用。最稳妥的是用 text 类型打底,真要格式就上 markdown 类型,但得避开它的几个硬限制。

  • markdown 类型下,标题最多支持到 ##(即二级标题),### 及以下会被当普通文本
  • 表格必须用完整语法:|列1|列2|\n|---|---|\n|值1|值2|,少一行或缺竖线就整个消息变纯文本
  • 链接写法必须是 [文字](https://xxx)<a href="..."> 或纯 URL 都不解析
  • 别在 markdown 里混用 HTML 标签,钉钉会直接过滤掉整段内容

示例可用的最小可靠结构:

立即学习“PHP免费学习笔记(深入)”;

{ "msgtype": "markdown", "markdown": { "title": "服务告警", "text": "## [错误] <code>Db::table('user')->where('id', 0)->find()</code>\n> 时间:2024-06-12 14:22:05\n> 服务器:<code>web-prod-03</code>" } }

为什么本地测试通、上线就 400 或无响应

常见原因不是代码问题,而是环境差异导致签名或请求被拦。

  • 服务器时间不准:timestamp 和钉钉服务器时间差超过 1 小时,sign 直接失效 —— 用 ntpdate -u ntp.aliyun.com 同步下
  • PHP 开启了 open_basedir 或禁用了 curl:检查 phpinfo()disable_functions 是否含 curl_exec
  • Web 服务器(Nginx/Apache)限制了 POST body 大小:钉钉要求单条消息 ≤ 2048 字符,但如果你拼了个超长日志进去,可能被中间件截断
  • ThinkPHP 的 Env 配置没加载对:access_tokensecret 建议放 .env,别写死在控制器里,避免误提交到 Git

封装成 ThinkPHP 公共方法要注意什么

别写成静态工具类就完事。钉钉 Webhook 是外部依赖,必须考虑失败重试、日志记录、敏感信息隔离。

  • 方法入参建议只收 $title$text$type = 'markdown',其他如 token、secret、超时时间全部从配置读(config('dingtalk.webhook')
  • HTTP 超时设为 3 秒以内,钉钉响应通常
  • 失败时别静默吞掉错误,至少记到 think/log,包含原始响应体(比如 {"errcode":310000,...}
  • 如果用 Http::post(),记得手动设 header:['Content-Type' => 'application/json'],否则默认是 application/x-www-form-urlencoded,钉钉直接 400

复杂点在于签名生成和错误分类 —— timestamp 和 sign 必须原子生成,不能先取时间再算 sign 中间隔了几毫秒;而钉钉返回的 errcode 里,310000 是签名错,320000 是 access_token 过期,330000 是调用太频繁,每种该走不同处理路径。