如何用ThinkPHP封装Webhook并通过钉钉机器人发送Markdown格式告警通知?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1083个文字,预计阅读时间需要5分钟。
直接使用+cURL或+file_get_contents+发起POST请求即可,无需安装SDK。锤子机器人只认+POST+application/json+。传递过去的是标准的JSON格式,ThinkPHP自带的+Http+类或原生+cURL+都能搞定。
关键不是“能不能发”,而是「怎么发才不被钉钉拒收」——比如签名失效、timestamp 和 sign 不匹配、JSON 格式错一个逗号就 400。
- 必须带上
timestamp(毫秒时间戳)和sign(HMAC-SHA256 签名),否则返回{"errcode":310000,"errmsg":"invalid signature"} -
sign计算时,原始字符串是"<timestamp>\n<secret>"</secret></timestamp>,注意换行符\n是必须的,不是空格也不是回车 - Webhook 地址里的
access_token和sign要动态拼,不能硬编码在 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_token和secret建议放.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分钟。
直接使用+cURL或+file_get_contents+发起POST请求即可,无需安装SDK。锤子机器人只认+POST+application/json+。传递过去的是标准的JSON格式,ThinkPHP自带的+Http+类或原生+cURL+都能搞定。
关键不是“能不能发”,而是「怎么发才不被钉钉拒收」——比如签名失效、timestamp 和 sign 不匹配、JSON 格式错一个逗号就 400。
- 必须带上
timestamp(毫秒时间戳)和sign(HMAC-SHA256 签名),否则返回{"errcode":310000,"errmsg":"invalid signature"} -
sign计算时,原始字符串是"<timestamp>\n<secret>"</secret></timestamp>,注意换行符\n是必须的,不是空格也不是回车 - Webhook 地址里的
access_token和sign要动态拼,不能硬编码在 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_token和secret建议放.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 是调用太频繁,每种该走不同处理路径。

