新闻详情

新闻详情

首页 / 资讯中心 / 详情

用libcurl给微信公众号推送模板消息的C语言实战

发布时间:2026/9/25 1:38:15来源:尧图网络
用libcurl给微信公众号推送模板消息的C语言实战
简介面向需要在C/C工程中接入微信公众号消息推送的开发者这里提供一份基于libcurl的报警推送实现可调用微信接口向粉丝微信账号发送通知消息解决自建系统缺少移动端告警渠道的问题。压缩包共106个文件总体积约3.59MB内容覆盖完整工程与依赖既有C与C源文件、Visual Studio工程与解决方案也包含85个头文件、4个lib库、4个dll动态库及3个PDB调试符号并附自动构建脚本等辅助配置文件可直接编译也可按需扩展。已有319人学习该资源适合正在研究微信推送方案或准备集成curl的初中级开发者。代码中给出curl POST封装、微信消息组包示例、编译脚本与依赖库同时提供VS工程与构建辅助文件可在Windows下快速编译运行验证推送效果借此可理清libcurl调用流程、微信公众平台消息接口对接要点稍作调整即可用于报警系统、运维通知、设备状态推送等常见场景。1. 用 libcurl 给微信公众号推消息从接口文档到可跑工程如果你的服务端是 C 或 C 写的又需要在设备告警、订单状态变更时给用户推一条微信公众号消息libcurl 是最直接的轮子。微信官方只提供 HTTP 接口没有 C SDK网上绝大多数示例是 Python 或 Node 的C 工程想接就得自己拼 HTTP。libcurl-weixin-message 这套工程正好把微信公众号模板消息的访问令牌获取、消息 JSON 拼装、POST 下发整条链路串了起来依赖只有 libcurl 和 OpenSSL不挑运行环境。适合刚接触微信公众平台接口的新手照着改也适合老手拿它当模板换成自己的业务字段就上线。2. 接口长什么样模板消息 POST、token 缓存与 libcurl 的职责边界2.1 模板消息接口的请求结构微信公众号推送消息生产环境用得最多的是模板消息接口。请求行是 POST 到https://api.weixin.qq.com/cgi-bin/message/template/send查询串里带 access_token请求体是 JSON包含 touser、template_id、url、data 和可选的 miniprogram 五个字段。touser 是接收者的 openid这个 openid 必须用当前公众号 appid 去换不能填 unionid更不能填手机号。template_id 是公众平台后台申请的模板 ID模板字段结构由后台决定不是代码里发明出来的。data 里填实际推送内容每个 key 对应模板里的一个占位符value 是字符串color 可以省略。url 字段是可选的用户点击模板消息会跳转到这个地址。如果想跳转小程序要用 miniprogram 字段里面传 appid 和 pagepath二者必须与当前公众号绑定。这里有个常见的认知偏差很多新手以为模板消息像短信一样直接推到聊天窗口其实它是收进微信“服务通知”里的用户感知强度远低于聊天消息。理解这一点后面排查“为什么发了用户没看到”会省很多力气。另外要区分模板消息和客服消息。客服消息要求用户近 48 小时内有交互才能下发适合在线客服对话模板消息不受这个限制但内容和模板强绑定不能随意聊天。业务上做告警推送、业务通知模板消息是对的选型如果要做人工客服模板消息做不了得走客服消息或微信客服。2.2 libcurl 在这个场景里的职责边界用 libcurl 做微信推送它管的是 HTTP 层TCP 连接、TLS 握手、请求头发送、响应体读取、超时控制。微信接口的业务逻辑——access_token 的解析与缓存、模板消息 JSON 构造、errcode 判定——都得自己在 C 代码里做。把这层边界想清楚写代码时就不会把所有逻辑堆进一个函数。对比其他方案选型理由很现实如果服务端已经是 C/C 工程为一条推送消息引入 Python 或 Node 运行时部署和运维成本都上去了用 system 调 curl 命令又拿不到细粒度的错误码和响应体还多了进程创建的开销。libcurl 作为库嵌入进程一个回调函数就能拿到微信返回的完整 JSON等于把 HTTP 这个“黑匣子”打开给你看。方案额外依赖适合场景主要约束libcurl 嵌入 C 工程libcurl OpenSSL已有 C 服务端要求低延迟低开销需要自己解析 JSON、管理 tokensystem(curl ...)curl 命令行一次性脚本、调试拿不到结构化错误进程开销大Python requestsPython 运行时快速原型、运维脚本引入运行时嵌入式环境不方便还有一个构建时容易忽略的点libcurl 的 DNS 解析默认走系统库c-ares 是可选组件不是必带。微信推送场景走系统 DNS 就够用如果发行版把 c-ares 静态编进 libcurl链接时会多出符号依赖但不影响功能。2.3 一次推送的完整生命周期先 token再 send最后解析微信接口调用顺序是固定的先从/cgi-bin/token拉一个 access_token有效期 7200 秒拿到后拼到模板消息接口的 query 里再 POST JSON 过去响应体带 errcode。errcode 为 0 表示成功非 0 是业务错误码比如 40001 是 token 无效40003 是 openid 非法45009 是接口调用超限。这个“两段式”结构是新手最早翻车的地方。有人图省事每次发送前都重新拉 token结果高频推送几分钟就打满每日配额也有人把 token 存在静态变量里进程一重启就丢然后被 40001 反复折磨。正确做法是维护一份 token 缓存记录拉取时间和过期时间未过期就复用。这套工程里 token 缓存和发送逻辑分开也是为了让两块职责各自可测。编译之前先确认环境里的 libcurl 版本。Linux 上执行curl --version就能看到只要不是特别老的发行版7.x 基本都支持用到的接口。模板消息场景对 libcurl 版本不敏感真正要确认的是有没有 cJSON 或等价 JSON 解析库因为响应解析全程绕不开 JSON。3. 跑通完整链路access_token 获取、模板 JSON 拼装与消息下发3.1 先拿 access_tokenGET 请求与内存回调拉取 token 是 GET 请求URL 是/cgi-bin/token三个参数固定grant_typeclient_credential、appid、secret。响应是 JSON形如{access_token:xxx,expires_in:7200}。C 代码要做的是发起请求、把响应体写进内存、再解析出 access_token 字段。下面的代码演示最基础的内存回调。curl 在收到数据时会反复调用 WRITEFUNCTION每段数据大小不定所以回调里要把数据追加到一个动态缓冲区#include stdio.h #include stdlib.h #include string.h #include curl/curl.h /* 响应体累积缓冲区 */ struct resp_buf { char *data; size_t len; }; static size_t on_write(void *ptr, size_t size, size_t nmemb, void *userdata) { size_t bytes size * nmemb; struct resp_buf *buf (struct resp_buf *)userdata; char *p realloc(buf-data, buf-len bytes 1); if (!p) return 0; /* 返回 0 会让 libcurl 中断本次传输 */ buf-data p; memcpy(buf-data buf-len, ptr, bytes); buf-len bytes; buf-data[buf-len] \0; return bytes; }这个回调是整套代码的基础后续发送模板消息也要复用它。参数有三个要点ptr 是本次收到的数据指针size 和 nmemb 相乘才是真实字节数userdata 指向调用方传入的结构体所有状态都要挂在它上面回调返回的字节数必须等于实际写入的字节数否则 libcurl 会判定传输异常。接着写拉取 token 的函数。appid 和 secret 通常是 wx 开头的 ASCII 字符串但我一般还是会过一遍 curl_easy_escape防止哪天密钥里混进特殊字符导致 URL 被截断。parse_token_json 内部就是 cJSON 取 access_token 字段头文件需要按你的工程路径 include cJSON.hstatic int get_access_token(const char *appid, const char *secret, char *out_token, size_t out_size) { CURL *curl curl_easy_init(); if (!curl) return -1; char url[1024]; char *enc_appid curl_easy_escape(curl, appid, 0); char *enc_secret curl_easy_escape(curl, secret, 0); snprintf(url, sizeof(url), https://api.weixin.qq.com/cgi-bin/token ?grant_typeclient_credentialappid%ssecret%s, enc_appid, enc_secret); curl_free(enc_appid); curl_free(enc_secret); struct resp_buf buf {0}; curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, on_write); curl_easy_setopt(curl, CURLOPT_WRITEDATA, buf); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15L); CURLcode res curl_easy_perform(curl); curl_easy_cleanup(curl); if (res ! CURLE_OK) { fprintf(stderr, curl error: %s\n, curl_easy_strerror(res)); free(buf.data); return -1; } /* buf.data 此刻是完整 JSON用 cJSON 解析出 access_token */ parse_token_json(buf.data, out_token, out_size); free(buf.data); return 0; }参数说明CURLOPT_CONNECTTIMEOUT 是 TCP 连接超时微信接口偶发拥塞连接阶段 5 秒足够CURLOPT_TIMEOUT 是整请求总超时给到 15 秒是防止代理链路慢时拖死业务线程。curl_easy_cleanup 必须放在所有 setopt 用完、且 buf 数据已经拷走之后再调因为回调内存是调用方持有的跟 curl 句柄没有归属关系。3.2 拼装模板消息 JSON字段映射与转义微信模板消息的 JSON 有一个固定骨架外面是 touser、template_id、url、datadata 里面是 first、keyword1、keyword2、remark 这类占位字段。占位字段名不是代码里随便起的要以公众平台后台你申请到的那个模板为准。下面是拼装代码。实际工程里建议用 cJSON 库构造而不是手写字符串这里为了直观展示字段结构先用字符串字面量const char *build_template_body(const char *openid, const char *template_id, const char *order_no, const char *time_str) { /* 实际工程建议用 cJSON 组装避免手动转义 */ static char body[2048]; snprintf(body, sizeof(body), { \touser\:\%s\, \template_id\:\%s\, \url\:\https://api.example.com/order?no%s\, \data\:{ \first\:{\value\:\您的订单已发货\}, \keyword1\:{\value\:\%s\}, \keyword2\:{\value\:\%s\}, \remark\:{\value\:\请保持手机畅通\} } }, openid, template_id, order_no, order_no, time_str); return body; }这段代码有一个必须留意的点如果 order_no 里出现双引号、反斜杠直接拼字符串会把 JSON 结构打穿这是用静态 buffer 手拼的代价。生产环境里订单号基本是数字加横线风险不大但如果推送内容来自用户输入就必须先做 JSON 转义把双引号替换成转义形式、反斜杠加倍。常见做法是把转义逻辑放在字段写入之前统一过一遍别散落在各个业务模块里。3.3 发送模板消息POST、Header 与响应判定发送接口是 POSTbody 就是上一节拼好的 JSON。libcurl 里设置 POSTFIELDS 即可同时要显式带上 Content-Type微信侧要求 application/json。static int send_template(const char *token, const char *json_body) { CURL *curl curl_easy_init(); if (!curl) return -1; char url[1024]; snprintf(url, sizeof(url), https://api.weixin.qq.com/cgi-bin/message/template/send ?access_token%s, token); struct curl_slist *headers NULL; headers curl_slist_append(headers, Content-Type: application/json; charsetutf-8); headers curl_slist_append(headers, Accept: application/json); struct resp_buf buf {0}; curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_body); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)strlen(json_body)); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, on_write); curl_easy_setopt(curl, CURLOPT_WRITEDATA, buf); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15L); CURLcode res curl_easy_perform(curl); long http_code 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, http_code); curl_slist_free_all(headers); curl_easy_cleanup(curl); if (res ! CURLE_OK) { free(buf.data); return -1; } if (http_code ! 200) { fprintf(stderr, http %ld: %s\n, http_code, buf.data ? buf.data : ); free(buf.data); return -1; } int errcode parse_errcode(buf.data); /* 解析响应中的 errcode */ free(buf.data); return errcode; /* 0 表示成功 */ }几个参数解释。CURLOPT_POSTFIELDS 指向的字符串在 curl_easy_perform 执行期间必须一直有效所以 json_body 不能是函数里的临时栈变量用 static 或堆上内存都行。CURLOPT_POSTFIELDSIZE_LARGE 显式告诉 libcurl body 长度避免它自己用 strlen 量也防止 body 里恰好有\0时被截断。HTTP 状态码和 errcode 是两回事HTTP 200 只代表请求到达微信并被处理业务上成功与否要看 errcode。curl_easy_getinfo 在 perform 之后调用拿到的状态码才有效。parse_errcode 内部就是 cJSON 取整数字段代码量很小。3.4 主流程与编译把上面两个函数串起来主流程很直接先拉 token再发消息最后按 errcode 判断结果。int main(void) { char token[512] {0}; if (get_access_token(wxYOUR_APPID, YOUR_SECRET, token, sizeof(token)) ! 0) { return 1; } const char *body build_template_body( openid_from_user, /* 接收者 openid */ template_id_from_mp, /* 公众平台申请到的模板 ID */ ORD20240618001, /* 订单号会写进 url 和 keyword1 */ 2024-06-18 10:30); /* 时间写进 keyword2 */ int errcode send_template(token, body); if (errcode ! 0) { fprintf(stderr, template send failed, errcode%d\n, errcode); return 1; } return 0; }编译时链接 libcurl 和 cJSONgcc -o weixin_push weixin_push.c -lcurl -lcjson提示模板 ID 不能照抄网上的示例它和公众号一一对应。A 公众号申请的模板 ID在 B 公众号下发会报模板不匹配。上线前先用测试 openid 发一条真实消息确认内容格式和跳转地址都正常。4. 中文、URL 编码和证书消息不显示或发送失败的三个隐蔽点4.1 别手拼 URLcurl_easy_escape 与 参数截断很多人在这一步栽过消息发出去了 errcode 也是 0用户点击跳转落地页参数却丢了一半。常见场景是模板消息的 url 里带 query比如https://api.example.com/order?no123fromweixin这个地址作为 JSON 字符串传给微信后微信会做 URL 解码再交给浏览器。如果 没有适当处理微信可能把fromweixin这一截丢掉落地页只拿到 no123。处理方式有两种。一是 URL 里的 query 值先做一次 percent 编码 变成%26二是在 JSON 里把 写成\u0026让 JSON 解析后得到真正的 。我一般倾向于后者因为 JSON 层面的转义更可控不容易和 URL 编码叠加出双重百分号。凡是拼进 url 或 miniprogram pagepath 的参数值一律过一遍转义别偷懒。再顺带说一个容易混淆的点如果模板消息要跳转小程序miniprogram 字段的值是 appid 加 pagepathpagepath 是pages/xxx这种相对路径不接收weixin://dl/business/这类业务深链。热搜里经常出现的一长串weixin://dl/business/?appid...path...参数是商城扫码、广告落地页专用的跳转协议跟模板消息的 miniprogram 字段不是一回事别混着用。4.2 UTF-8 与 JSON 中文转义乱码的根源在源码编码模板消息的中文内容微信侧要求 UTF-8 编码。看起来简单但 C 工程里翻车点在于源码文件的编码。很多老项目源文件是 GBK编译器按本地字符集读取字符串字面量代码里写“订单已发货”实际发到微信的是 GBK 字节流微信当 UTF-8 解析出来的就是乱码。解决办法是统一工程编码。Git 仓库层面强制 UTF-8编译时用-finput-charsetUTF-8指定源码字符集。如果历史遗留的 GBK 文件改不动常见做法是在拼接 data 之前把 GBK 字符串用 iconv 转成 UTF-8 再进 JSON。这个转码点要放在所有用户输入汇聚的地方别散落在各个业务模块里。#include iconv.h /* 把 src 从 GBK 转成 UTF-8dst 由调用方分配 */ static int gbk_to_utf8(const char *src, char *dst, size_t dst_len) { iconv_t cd iconv_open(UTF-8, GBK); if (cd (iconv_t)-1) return -1; char *in (char *)src; char *out dst; size_t in_len strlen(src); size_t out_len dst_len; int rc iconv(cd, in, in_len, out, out_len); iconv_close(cd); if (rc (size_t)-1) return -1; *out \0; return 0; }这段代码说明 iconv 的输入输出指针在调用过程中会被移动所以必须用局部变量承接不能直接拿原指针转码失败时检查 errno 是 EILSEQ 还是 E2BIG前者是源串里有非法字符后者是目标缓冲区太小。data 的 value 如果来自表单或数据库里面可能带双引号、换行、emoji。双引号会破坏 JSON 结构换行会让模板消息在微信里显示异常emoji 在模板消息里很多机型渲染不出来显示成豆腐块。稳妥做法是 value 拼接前统一过滤引号转义、换行去掉、非 BMP 字符丢弃。4.3 HTTPS 与证书SSL 校验关不关的取舍微信接口强制 HTTPSlibcurl 默认会校验服务器证书。开发环境里经常遇到报错 60 SSL certificate problem第一反应是关掉校验——CURLOPT_SSL_VERIFYPEER 设 0L 确实能通但这是把生产环境的隐患提前埋下了。微信侧证书链调整时关掉校验的客户端不会立刻出问题直到某次证书更新或中间证书替换连接才突然开始报 56 或 60而且下次再想排查时间成本极高。生产环境的正解是让 libcurl 找到正确的 CA 路径。Linux 发行版默认装了 ca-certificates 时curl 一般能自动找到内网环境有自签 CA 或 SSL 网关时用 CURLOPT_CAINFO 指定 CA 文件路径比全局关校验安全得多。CURLOPT_SSL_VERIFYHOST 同理保持默认 2不要为了省事降到 0。注意生产环境不要把 CURLOPT_SSL_VERIFYPEER 设 0正确做法是指定 CA 文件。4.4 POST 重定向与超时参数顺手开关可能把发送变成 GET模板消息接口正常不会重定向但有人会顺手开 CURLOPT_FOLLOWLOCATION觉得“跟着跳转总没错”。这个选项在 POST 场景是有副作用的部分 HTTP 栈在 301/302 时会把 POST 降级成 GET 并丢掉 body微信侧收到的是一个没有请求体的 GET返回 405 或参数缺失。微信接口既然文档没要求跟随重定向就别开这个选项。遇到非 200 响应先看 Location 再决定要不要跟靠代码显式处理。超时参数也值得按接口特征调微信模板消息接口的 p95 响应时间一般在几百毫秒到 3 秒之间高峰时段可能到 5 秒。CURLOPT_CONNECTTIMEOUT 给 5 秒、CURLOPT_TIMEOUT 给 15 秒是比较安全的下限如果业务线程紧张可以把总超时压到 10 秒但一定要把超时日志打出来否则故障时只能看到一条冷冷的 CURLE_OPERATION_TIMEDOUT根本不知道卡在哪个环节。5. 避坑与排查按返回码逐条定位模板消息发送问题5.1 先看返回码再动手errcode 对照清单微信模板消息接口的错误码是定位问题的第一入口比猜日志高效得多。模板消息的错误分两类网络层错误和业务层错误。网络层错误是 curl 层面报的比如连接超时、证书错这类可以重试业务层错误是微信返回的 errcode代表请求到了微信但被判定失败这类通常不该重试。排查的第一步永远是先分清错误出现在哪一层再往下看。下面这张表是高频出现的几个建议贴在项目文档里errcode含义常见触发场景0发送成功——40001access_token 无效或过期token 缓存被多进程搞乱、本机时钟偏差40003openid 非法填了 unionid、手机号或跨公众号传 openid45009接口调用超限高频推送打满每日配额47003模板参数不匹配data 字段名与模板占位符不一致表格里的错误码含义以微信官方文档最新返回码为准但 40001、40003、45009 这几个排查方向基本稳定。5.2 40001token 失效的三种隐蔽场景现象代码刚拉完 token紧接着发送就返回 40001 invalid credential重试偶发成功。原因最常见的是多进程共用了一份 token 缓存进程 A 刷新了 token进程 B 还拿旧的发也有的是本机时钟偏慢token 在客户端侧被提前判定过期。微信返回的 expires_in 是 7200 秒但日志出现 40001 的时间点往往在 7000 秒之前说明缓存刷新时机不能卡着点。解决token 缓存加锁保证同时只有一个刷新动作刷新后以服务器返回的时间戳作为基准不要用本机 time() 直接加 expires_in。实际工程里我会把过期时间设成 expires_in - 300提前 5 分钟刷新实测能筛掉大半 40001。5.3 40003openid 的命名空间错位现象明明从用户表里拿的 openid发送却报 40003 invalid openid。原因openid 是按 appid 隔离的。同一个微信用户在公众号 A 和公众号 B 下的 openid 是两串不同的值。用户表里存的是小程序或另一个公众号的 openid直接拿来给当前公众号发模板消息微信侧不认识。解决先确认 openid 是从当前公众号获取的。用公众号的“用户管理-已关注用户”接口拉一份真实 openid 列表做对照替换掉错误数据。如果业务上有多个公众号用户表里要给 openid 加 appid 列发送时按 appid 过滤。5.4 60 / 56证书与连接层问题现象curl_easy_perform 返回 CURLE_PEER_FAILED_VERIFICATION60或 CURLE_RECV_ERROR56HTTP 层根本没到响应体为空。原因60 是证书校验失败常见于内网环境 CA 未安装或代理做了 TLS 拦截56 是连接中途断掉常见于公司出口代理对长连接限流或微信接口偶发拥塞。这类问题最迷惑的地方是它不稳定有时连续成功几十条后突然来一次排查的时候让人想翻车。解决60 的处理方式是给 CURLOPT_CAINFO 指定正确的 CA 文件不要把 SSL 校验整体关掉56 则检查网络链路缩短 CURLOPT_TIMEOUT 并加入连接失败重试。重试逻辑要放在这一层业务层面的返回码不要重试。5.5 errcode 0 但用户没收到先查折叠与服务通知现象接口返回 errcode 0用户在微信里却找不到这条消息来投诉说“没收到”。原因这条算是血泪经验。模板消息不是聊天消息它统一收进微信的“服务通知”折叠列表。用户如果长期不点开服务通知新消息会被折叠成一条摘要感知很弱另外模板消息在下发后没有失败回执但如果用户当天取消关注又重关注旧 openid 可能失效接口也可能返回 0 但实际未送达。解决排查时先看用户是否还关注公众号再看公众平台后台的“模板消息”发送记录确认这条消息是否真的下发。给重要推送加一层兜底比如短信或客服消息如果权限允许不要把模板消息当成唯一通道。5.6 45009配额打满时的合并推送思路现象日终跑批推送时报 45009 reach max api daily quota limit当天发送量明明没到估算值。原因模板消息接口有每日调用上限上限按公众号整体计算不是按模板分开算。跑批任务一旦失败重跑重放的消息全记进配额很快打满。解决把同用户的多次零散推送合并成一条比如把“订单已支付”“订单已发货”两条合并成一条包含两个 keyword 的模板消息跑批任务引入去重同一业务 ID 只允许发送一次。重试同样只针对网络层错误业务失败不要无脑重放。6. 进阶封装token 提前刷新、保守重试与发送耗时验证6.1 用时间戳管理 token而不是每次现拉把 token 缓存封装成一个结构体记录 token 值和绝对过期时间。每次发送前检查一次token 为空或当前时间加 300 秒已经超过过期时间就重新拉取。这个提前量是吃过亏之后加上的微信侧的 token 在生命周期末期经常出现“能拉到但发不出去”的玄学现象提前 5 分钟刷新能避开。#include time.h /* token_valid 用到 time() */ typedef struct { char token[512]; time_t expire_at; } wx_token_cache; static int token_valid(const wx_token_cache *c) { return c-token[0] ! \0 time(NULL) 300 c-expire_at; }6.2 重试只看网络层错误微信返回的业务错误码不能重试尤其是模板消息重发一次用户就多收一条投诉风险很高。可重试的只有 CURLcode 层面的连接失败、超时之类。重试间隔用 500ms、1s、2s 递进三次封顶记录每次重试的返回码方便事后对账。6.3 发送耗时与响应体一起打印调试时把发送耗时带出来故障定位会快很多。curl_easy_getinfo 的 CURLINFO_TOTAL_TIME 能拿到整段请求耗时和 errcode、http_code 一起打日志。一条耗时正常但 errcode 非 0 的日志指向的是业务参数问题耗时接近超时上限的指向网络或代理链路。这两类问题的处理方向完全不同日志里分不清就只能靠猜。这套模块写完后我的习惯是先跑一个最小闭环用测试 openid 发一条真实模板消息确认 token 刷新、发送、日志三块都正常再往上接业务。工程里我把 token 缓存、发送、日志拆成独立模块改字段就能接入自己的业务。最早我贪图省事直接关掉 SSL 校验上线几个月后微信侧证书调整某天发送大面积失败查了一整天才定位到是证书问题。从那以后我每次接微信推送都强制把 token 缓存、证书校验、重试策略写进检查清单先把基础链路走一遍再谈业务。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

JNA 常见问题深度解析:从映射错误排查到结构体用法与性能调优 2026/9/25 2:08:21

JNA 常见问题深度解析:从映射错误排查到结构体用法与性能调优

系统编程后端 【免费下载链接】jna Java Native Access 项目地址: https://gitcode.com/gh_mirrors/jn/jna 点击查看 免费下载 本文以 JNA(Java Native Access)官方 FAQ(www/FrequentlyAskedQuestions.md)为骨架&…

阅读更多 →
VSCode老手如何高效使用MounRiver Studio II开发沁恒RISC-V 2026/9/25 2:08:15

VSCode老手如何高效使用MounRiver Studio II开发沁恒RISC-V

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
IDA 5.0经典反汇编工具实战:老版本逆向分析全流程指南 2026/9/25 2:08:15

IDA 5.0经典反汇编工具实战:老版本逆向分析全流程指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
三菱Q64AD模拟量输入模块从参数配置到梯形图采集全解析 2026/9/25 2:08:15

三菱Q64AD模拟量输入模块从参数配置到梯形图采集全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Delphi 12.3 下 ReportMachine 7.0 安装配置与报表开发实战 2026/9/25 2:08:08

Delphi 12.3 下 ReportMachine 7.0 安装配置与报表开发实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Matlab间断有限元求解声波方程:高阶格式实现与稳定计算 2026/9/25 2:08:08

Matlab间断有限元求解声波方程:高阶格式实现与稳定计算

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉