后端即时通讯金融科技【免费下载链接】WeiXinMPSDK微信全平台 .NET SDK Senparc.Weixin for C#支持 .NET Framework 及 .NET Core、.NET 10.0。已支持微信公众号、小程序、小游戏、微信支付、企业微信/企业号、开放平台、JSSDK、微信周边等全平台。 WeChat SDK for C#.项目地址https://gitcode.com/gh_mirrors/we/WeiXinMPSDK点击查看免费下载公众号自定义菜单是用户与公众号交互的第一入口也是公众号界面中最重要的导航元素之一。本文基于 WeiXinMPSDKSenparc.Weixin for C#仓库中的 公众号菜单设置文档系统讲解菜单的设置与使用两大环节从可视化编辑器No Code到代码方式创建菜单再到自定义 MessageHandler 中接收并处理菜单点击事件并深入 SDK 源码剖析菜单实体模型与底层 API 调用链。读完本文你将掌握用 Senparc.Weixin.MP 完整落地「创建菜单 → 点击响应」全流程的能力。一、菜单的两大环节设置与使用公众号菜单从生命周期上可以拆分为两个相互独立又紧密衔接的环节设置Create把菜单结构一级菜单、二级菜单、按钮类型、跳转地址、事件 Key 等上传到微信服务器使其在公众号界面上生效使用Use用户点击菜单后微信服务器把点击事件以 XML 消息的形式推送到消息 URL由我们的 MessageHandler 接收并作出响应。其中“设置”是一次性动作通常只在菜单结构变更时才需要重新执行因此官方文档建议把创建菜单的代码放在管理员后台手动运行而不是放在每次请求都会执行的业务逻辑中而“使用”则是持续性的在线处理逻辑每一次用户点击都会触发一次事件推送。二、设置菜单的三种方式方式一微信公众号后台非开发模式直接登录微信公众号后台在「自定义菜单」页面可视化编辑。此方式不涉及开发模式适合菜单结构简单、无需与业务系统联动的场景。原文档对此略过本文也不再展开。方式二推荐可视化编辑器No CodeSDK 官方提供在线可视化菜单编辑器地址为 https://sdk.weixin.senparc.com/Menu原文档中给出的链接。该编辑器支持零代码拖拽配置菜单配置完成后即可生成可直接用于 SDK 的菜单 JSON 或调用代码适合不熟悉实体模型、希望快速上手的开发者。原文档将其列为推荐方式。方式三使用代码设置推荐用于生产环境使用 Senparc.Weixin.MP 提供的ButtonGroup实体模型在代码中构建菜单结构再调用CommonApi.CreateMenuAsync(appId, bg)一次性提交。原文档给出的完整示例public async Task CreateMenuAsync() { ButtonGroup bg new ButtonGroup(); //定义一级菜单 var subButton new SubButton() { name 一级菜单 }; bg.button.Add(subButton); //下属二级菜单 subButton.sub_button.Add(new SingleViewButton() { url https://book.weixin.senparc.com/book/link?codeSenparcRobotMenu, name 《微信开发深度解析》 }); subButton.sub_button.Add(new SingleClickButton() { key OneClick, name 单击测试 }); subButton.sub_button.Add(new SingleViewButton() { url https://weixin.senparc.com/, name Url跳转 }); //最多可添加 3 个一级自定义菜单每个菜单下最多 5 个子菜单 var result await CommonApi.CreateMenuAsync(appId, bg); }示例中构建了一个一级菜单「一级菜单」其下挂载三个二级菜单项一个SingleViewButton点击跳转到外部链接如《微信开发深度解析》书籍页面、一个SingleClickButton点击触发事件推送key为OneClick、以及另一个SingleViewButton跳转到官网。这段代码需要基于「完整继承 深入讲解」的原则进行展开下面进入源码层。三、菜单实体模型源码剖析菜单结构在 SDK 中被设计为一套完整的实体类继承体系位于 src/Senparc.Weixin.MP/Senparc.Weixin.MP/Entities/Menu 目录下Entities/Menu/ ├── ButtonGroupBase.cs // 所有菜单组的基类含 button 列表 ├── IButtonGroupBase.cs // 菜单组接口 ├── Custom/ │ └── ButtonGroup.cs // 普通自定义菜单的整个按钮设置 ├── Conditional/ │ ├── ConditionalButtonGroup.cs // 个性化条件菜单 │ └── MenuMatchRule.cs // 个性化菜单匹配规则 └── Buttons/ ├── BaseButton.cs // 所有按钮基类name ├── SingleButton.cs // 所有单击按钮基类type ├── SubButton.cs // 子菜单sub_button ├── SingleClickButton.cs // click 按钮key ├── SingleViewButton.cs // view 按钮url ├── SingleMiniProgramButton.cs// 小程序按钮 ├── SingleScancodePushButton.cs ├── SingleScancodeWaitmsgButton.cs ├── SinglePicSysphotoButton.cs ├── SinglePicPhotoOrAlbumButton.cs ├── SinglePicWeixinButton.cs ├── SingleLocationSelectButton.cs ├── SingleMediaIdButton.cs ├── SingleViewLimitedButton.cs ├── SingleArticleIdButton.cs └── SingleArticleViewLimitedButton.cs3.1 菜单组ButtonGroup 与 ButtonGroupBaseCustom/ButtonGroup.cs 是普通自定义菜单整个按钮设置的入口类注释明确指出“可以直接用 ButtonGroup 实例返回 JSON 对象”——也就是说构建好的实体对象会由底层序列化为微信 API 所需的 JSON 请求体。其基类 ButtonGroupBase.cs 定义了核心属性public abstract class ButtonGroupBase : IButtonGroupBase { /// summary /// 按钮数组按钮个数应为1-3个 /// /summary public ListBaseButton button { get; set; } public ButtonGroupBase() { button new ListBaseButton(); } }button一级菜单数组数量限制为 13 个与微信官方规则一致构造函数自动初始化列表因此示例中可以直接bg.button.Add(...)而无需手动 new。3.2 按钮基类BaseButton 与 SingleButtonButtons/BaseButton.cs 是所有按钮的基类定义了唯一的通用属性public class BaseButton : IBaseButton { /// summary /// 按钮描述既按钮名字不超过16个字节子菜单不超过40个字节 /// /summary public string name { get; set; } }name即按钮显示名称注意微信对长度有字节限制普通按钮不超过 16 字节子菜单不超过 40 字节一个汉字在 UTF-8 下占 3 字节中文名需格外留意。Buttons/SingleButton.cs 是所有“可点击”按钮的抽象基类新增了type属性按钮类型如 click / view构造函数要求传入类型字符串public abstract class SingleButton : BaseButton, IBaseButton { /// summary /// 按钮类型click或view /// /summary public string type { get; set; } public SingleButton(string theType) { type theType; } }3.3 子菜单SubButtonButtons/SubButton.cs 表示带二级菜单的一级菜单项public class SubButton : BaseButton, IBaseButton { /// summary /// 子按钮数组按钮个数应为2~5个 /// /summary public ListSingleButton sub_button { get; set; } public SubButton() { sub_button new ListSingleButton(); } public SubButton(string name) : this() { base.name name; } }sub_button二级菜单数组数量限制为 25 个提供SubButton(string name)便捷构造函数可直接以菜单名初始化。3.4 常用按钮类型SingleClickButton 与 SingleViewButton示例中用到的两个最典型按钮SingleClickButton.csclick 型点击推送事件public class SingleClickButton : SingleButton { /// summary /// 类型为click时必须。 /// 按钮KEY值用于消息接口(event类型)推送不超过128字节 /// /summary public string key { get; set; } public SingleClickButton() : base(MenuButtonType.click.ToString()) { } }构造函数自动把type设为clickkey是按钮的事件标识用户点击后该值会通过EventKey字段随事件消息推送过来不超过 128 字节用于在服务端区分具体点了哪个按钮。SingleViewButtonview 型点击直接跳转网页则携带url属性对应示例中的跳转链接。仓库中 Buttons 目录还提供了SingleMiniProgramButton跳小程序、SingleScancodePushButton扫码推事件、SingleLocationSelectButton弹出地理位置选择器等更多按钮类型可按需选用。四、菜单创建的底层 API 调用链原文档调用的是CommonApi.CreateMenuAsync(appId, bg)其同步底层实现在 CommonAPIs/Menu/CommonApi.Menu.Custom.cs 中public static WxJsonResult CreateMenu(string accessTokenOrAppId, ButtonGroup buttonData, int timeOut Config.TIME_OUT) { return ApiHandlerWapper.TryCommonApi(accessToken { var urlFormat Config.ApiMpHost /cgi-bin/menu/create?access_token{0}; //对特殊符号进行URL转义 ... return CommonJsonSend.SendWxJsonResult(accessToken, urlFormat, buttonData, timeOut: timeOut); }); }从源码可以确认以下实现事实请求接口最终调用微信官方接口POST /cgi-bin/menu/create?access_token{0}AccessToken 自动管理方法签名是accessTokenOrAppId即既可直接传 AccessToken也可传 AppId——当传 AppId 且 AccessToken 失效时SDK 会通过TryCommonApi自动获取/刷新一次 AccessToken传null时则使用当前注册的第一个 AppId参数透传ButtonGroup实体直接作为请求体序列化提交返回WxJsonResult判断是否创建成功超时控制timeOut默认取Config.TIME_OUT可自定义异步版本CreateMenuAsync是对上述同步方法的异步封装Task 包装因此原文档示例中可以直接await。五、使用菜单在 MessageHandler 中接收点击事件5.1 事件推送机制菜单设置完成后当用户在微信客户端点击菜单按钮时若点击的是view 型按钮微信直接跳转url不会推送事件到服务器若点击的是click 型按钮或其他会触发事件的按钮类型微信服务器会把event类型消息Event click自动推送到消息 URL即进入已经配置好的 MessageHandler。因此服务端只需在自定义 MessageHandler 中重写override对应的事件处理方法即可完成响应。5.2 重写 OnEvent_ClickRequestAsync针对原文档“方法三”中创建的菜单当用户点击【单击测试】按钮key OneClick时原文档给出如下处理代码public override async Task OnEvent_ClickRequestAsync(RequestMessageEvent_Click requestMessage) { var reponseMessage CreateResponseMessage(); if (requestMessage.EventKey OneClick) { reponseMessage.Content 您点击了【单击测试】按钮; } else { reponseMessage.Content 您点击了其他事件按钮; } return reponseMessage; }要点说明RequestMessageEvent_Click是点击事件请求消息实体EventKey字段即携带按钮的key值对应SingleClickButton.key通过requestMessage.EventKey OneClick即可精确区分点击的是哪个按钮使用CreateResponseMessage()创建响应消息并设置Content文本即可实现点击后的自动回复该方法是 MessageHandler 内置的便捷方法可指定泛型如CreateResponseMessageResponseMessageText()事件处理返回的响应消息会由框架自动回复给用户。5.3 仓库中的真实参考实现原文档末尾指向的参考文件位于 Samples/MP/Senparc.Weixin.Sample.MP/MessageHandlers/CustomMessageHandler_Events.cs。仓库中该文件确实实现了同名方法第 114 行起/// summary /// 点击事件 /// /summary /// param namerequestMessage请求消息/param /// returns/returns public override async TaskIResponseMessageBase OnEvent_ClickRequestAsync(RequestMessageEvent_Click requestMessage) { var reponseMessage CreateResponseMessageResponseMessageText(); if (requestMessage.EventKey OneClick) { reponseMessage.Content 您点击了【单击测试】按钮; } else { reponseMessage.Content 您点击了其他事件按钮; } return reponseMessage; }这段真实示例与原文档代码几乎一致且与 CustomMessageHandler.cs 配合通过partial class方式组织事件处理逻辑验证了文档所述方案的可行性与标准写法。SDK 的 MP 示例项目 Senparc.Weixin.Sample.MP 中还有完整的项目配置Program.cs、appsettings.json可作为可直接运行的最小复现工程参考。六、完整实战从创建菜单到点击响应将上述两部分串起来一个完整的公众号菜单实战流程为构建菜单用ButtonGroupSubButtonSingleClickButton/SingleViewButton等实体构建菜单树注意一级菜单 13 个、二级菜单每个 25 个、按钮名长度限制发布菜单在管理员后台仅需执行一次调用await CommonApi.CreateMenuAsync(appId, bg)上传菜单并检查返回的WxJsonResulterrcode 0表示成功接收事件确保消息 URL服务器配置中的回调地址正确指向 MessageHandler处理点击在CustomMessageHandler中重写OnEvent_ClickRequestAsync根据requestMessage.EventKey分发业务逻辑如回复文本、调用客服接口、跳转页面等测试验收在微信客户端点击各菜单项验证跳转与事件回复是否符合预期。七、小结公众号菜单分为「设置」与「使用」两个环节设置是一次性上传动作推荐代码方式 后台手动触发使用是持续的事件处理逻辑Senparc.Weixin.MP 通过 Entities/Menu 下完整的实体继承体系ButtonGroup → SubButton → SingleButton → 具体按钮类型屏蔽了微信菜单 JSON 的复杂度实体即请求体底层由 CommonApi.Menu.Custom.cs 中的CreateMenu/CreateMenuAsync完成cgi-bin/menu/create接口调用并内置 AccessToken 自动管理点击事件click 型通过OnEvent_ClickRequestAsync在自定义 MessageHandler 中处理以EventKey区分按钮参考实现见 CustomMessageHandler_Events.cs。掌握本文内容后你可以在任意基于 WeiXinMPSDK 的公众号项目中独立完成菜单的构建、发布与点击交互的全链路开发。赞分享后端即时通讯金融科技【免费下载链接】WeiXinMPSDK微信全平台 .NET SDK Senparc.Weixin for C#支持 .NET Framework 及 .NET Core、.NET 10.0。已支持微信公众号、小程序、小游戏、微信支付、企业微信/企业号、开放平台、JSSDK、微信周边等全平台。 WeChat SDK for C#.项目地址https://gitcode.com/gh_mirrors/we/WeiXinMPSDK点击查看免费下载相关推荐WeiXinMPSDK 微信公众号自定义菜单开发指南从代码设置到事件响应全流程WeiXinMPSDK 微信公众号自定义菜单开发指南从代码设置到事件响应全流程 本文围绕 menu.md https://link.gitcode.com/i后端即时通讯金融科技Ant Design Dropdown 菜单项点击事件处理基于 key 分发操作的技术指南Ant Design Dropdown 菜单项点击事件处理基于 key 分发操作的技术指南 导读 Dropdown下拉菜单是 Ant Design 组件库前端UI组件设计系统x64dbg 插件菜单点击回调 PLUG_CB_MENUENTRY 完全指南从菜单注册到事件分发x64dbg 插件菜单点击回调 PLUG_CB_MENUENTRY 完全指南从菜单注册到事件分发 导读 PLUG_CB_MENUENTRY 是 x64dbg逆向工程调试器开发工具应用安全上一篇免费可商用的开源楷体中文字体霞鹜文楷安装指南、字重选择与授权说明下一篇突破像素限制pixelmatch不同分辨率图像对比实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
Word 2021 非初次安装 MathType 报错 DLL 找不到的修复指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 5:01:35
烧录良率上不去?从硬件链路到MES数据的系统化排查方法 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 5:01:28
MCU上跑神经网络:NNoM边缘推理实战指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:39:03
机房微孔天花选型:低成本高适配的实战指南 做过机房项目的人都有体会,天花选型这件事,看着不起眼,翻起车来是真要命。我见过一个项目,为省几万块钱选了普通石膏板当机房吊顶,半年不到板面受潮发霉、边角掉皮,空调回风也因为这层“闷罐”带不动&#… · 2026/9/25 7:39:03
碳交易遇上需求响应:综合能源系统调度优化的关键变量 前阵子复盘一个园区综合能源系统项目时,我盯着调度结果看了很久:明明天然气价格有优势,为什么优化器把一部分供暖负荷从燃气锅炉挪到了电锅炉?没有任何人工干预,只是把碳交易成本写进了目标函数。这个结果让我重新理解… · 2026/9/25 7:39:03
交互式座位图开发指南:从数据建模到Canvas渲染 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:38:57
CODESYS结合CANopen实现伺服运动控制一体化配置实战指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:38:57
Atlas 300V 24G是运算加速卡吗?从硬件到YOLO部署全解析 这段时间后台总有人问我同一个问题:Atlas 300V 24G到底算不算运算加速卡?还有人上来就问“atlas部署yolo”,但给的信息就一句话,没有型号、没有软件栈、没有目标场景。我猜很多人是被产品页上的“视频分析加速卡”这几个字带偏了&… · 2026/9/25 7:38:57
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37