thinkphp调用钉钉webhook需用post发送标准json,关键要正确生成timestamp(毫秒)和sign(hmac-sha256,含\n换行符),消息体用markdown类型时须遵守标题≤##、表格语法完整、链接用[文字](url)等限制,避免400错误。

ThinkPHP 里怎么调用钉钉 Webhook 发告警
直接用 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="..."></a>或纯 URL 都不解析 - 别在
markdown里混用 HTML 标签,钉钉会直接过滤掉整段内容
示例可用的最小可靠结构:
将 Markdown 格式的回复转换为适合 Telegram 的富文本,避免渲染错误。适用于用户偏好 Markdown 但目标平台为 Telegram 的场景。
{
"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 是调用太频繁,每种该走不同处理路径。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










