应使用ConcurrentDictionary缓存access_token并预留10秒缓冲期主动刷新,按appid维度隔离,注意JSON字段小写、URL编码及并发竞态问题。

怎么用 C# 获取有效的 access_token
微信所有后台接口(菜单、消息、用户信息)都依赖 access_token,它不是“登录一次就永久有效”,而是 2 小时过期,且调用一次就会让上一个失效。很多人直接写死或每次请求都重取,结果被限流或推送失败。
正确做法是:缓存 + 过期前主动刷新。别等过期了再取,要预留至少 10 秒缓冲时间,否则高并发下多个线程同时发现过期,会并发调用微信接口,触发频率限制(errcode:45009)。
-
access_token必须按appid维度缓存,不同公众号不能混用 - 推荐用
ConcurrentDictionary<string access_token></string>存,避免锁竞争 - 解析微信返回的 JSON 时注意字段名大小写:
access_token是小写,不是AccessToken - GET 请求地址必须是:
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appid}&secret={secret}
创建自定义菜单为什么总不显示
菜单 POST 成功 ≠ 用户立刻看到。微信客户端强缓存,且只在两个时机拉取:用户新关注后首次进入对话页,或进入公众号主页时距上次拉取超 5 分钟。测试时别傻等 24 小时,用「取消关注 → 重新关注」最快验证。
- JSON 中按钮类型必须小写:
"type": "click",写成"Click"或"CLICK"会静默失败 - 一级菜单最多 4 个汉字,二级最多 7 个——超出部分直接截断为空字符串,不会报错,但菜单变空
- POST 地址末尾必须拼上有效的
access_token,且 URL 中不能有空格或中文编码错误 - 别用浏览器直接访问菜单创建 URL,必须用 POST 发送完整 JSON,Content-Type 设为
application/json
点击菜单收不到 CLICK 事件?先查服务器配置
根本原因往往不在 C# 代码,而在微信后台没配通。微信服务器会先发 GET 请求校验你的 URL 是否合法,这步失败,后续所有事件(包括菜单点击)都不会推过来。
- 你填的 Token 必须和代码里比对用的完全一致(区分大小写、空格)
- 签名验证三要素排序必须是字典序:
nonce、signature、timestamp、token—— 注意不是按参数名字母顺序,而是按值排序后拼接 - 服务器必须支持 HTTP GET 和 POST,且响应时间 ≤ 5 秒,超时微信会认为服务不可用
- 收到事件 XML 后,务必原样返回
echostr(仅首次 GET 校验),其他时候要返回空字符串或成功响应,不能抛异常或返回 HTML 页面
C# 推送模板消息总提示 invalid openid
这个错误不是 OpenID 写错了,而是你用的 OpenID 不属于当前公众号。常见于:用了小程序的 openid、用了其他公众号的粉丝 ID、或者用户根本没关注该号(只有关注后才能拿到合法 openid)。
- 服务号只能给已关注用户发模板消息;订阅号不能发模板消息(已下线),只能发群发
- 获取粉丝列表必须用
https://api.weixin.qq.com/cgi-bin/user/get,不能靠前端传来的 OpenID 直接信任 - 模板 ID 必须在公众号后台「模板消息」中手动选用并启用,复制 ID 时别带空格或换行
- 发送时
data字段每个 key 必须严格匹配模板里定义的 keyword(如keyword1),少一个、拼错一个都会报错
最麻烦的其实是 token 刷新和菜单缓存的竞态问题——两个线程同时发现过期,一个取到新 token,另一个取失败还把旧 token 覆盖了。这事没法靠加锁彻底解决,得靠原子写入+时间戳校验兜底。



















