ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

公众号 MessageHandler 完全指南:Senparc.Weixin SDK 消息处理器的自定义、中间件与 Controller 双托管方案

公众号 MessageHandler 完全指南:Senparc.Weixin SDK 消息处理器的自定义、中间件与 Controller 双托管方案 后端即时通讯金融科技【免费下载链接】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 开源仓库的官方文档与源码完整讲解微信公众号MessageHandler消息处理器的使用方法。你将掌握如何通过继承 SDK 的MessageHandlerTMC基类创建自定义处理器并重写各类型消息与事件的处理方法以及如何通过**中间件Middleware**和Controller两种方式把处理器托管到 URL 上供微信服务器回调。文中所有代码与配置均取自仓库中的可运行示例可直接复制到你的 .NET 项目中实践。MessageHandler 是什么在 Senparc.Weixin SDK 中MessageHandler是公众号对话窗口消息的统一入口用户在公众号对话框中发送的文本、图片、语音、视频、位置、链接、文件等消息以及关注、取消关注、点击菜单等事件都会由微信服务器以 XML 形式 POST 到开发者配置的 URLSDK 负责把这些 XML 解析成强类型请求消息实体并交给MessageHandler分发到对应的处理方法。SDK 已经内置了消息解析、签名校验、上下文缓存、响应 XML 生成等全部基础能力开发者只需要创建一个自定义子类重写自己关心的业务处理方法即可其余工作全部由基类完成。自定义 MessageHandler三个文件的职责划分仓库示例Samples/MP/Senparc.Weixin.Sample.MP/MessageHandlers/目录将一个自定义处理器拆成了三个文件职责划分如下文件职责是否必须CustomMessageHandler.cs处理器主文件类声明、构造函数、工厂委托以及常规消息文本、图片、语音、视频等的处理必须CustomMessageHandler_Events.cs事件消息处理关注、取消关注、菜单点击、扫码等全部OnEvent_XxxRequestAsync方法必须与主文件组成同一个partial classCustomMessageContext.cs自定义消息上下文可选用于在上下文过期被移除时执行额外逻辑可选主文件的类声明如下CustomMessageHandler.cspublic partial class CustomMessageHandler : MessageHandlerDefaultMpMessageContextpartial class关键字允许将处理器分散到多个文件中如果不需要自定义上下文逻辑可以像注释中写的那样直接使用MessageHandlerDefaultMpMessageContext连CustomMessageContext.cs都可以不建。必须重写DefaultResponseMessage()在CustomMessageHandler*.cs中演示的所有override方法里只有DefaultResponseMessage()是必须重写的自 v1.5 起它成为抽象方法其他所有OnXxxRequest()方法都是虚方法、均可选。当用户发送一条消息、而对应类型的重写方法不存在时DefaultResponseMessage()就会被调用它相当于兜底处理器。示例实现CustomMessageHandler.cspublic override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage) { var responseMessage this.CreateResponseMessageResponseMessageText(); responseMessage.Content $这条消息来自DefaultResponseMessage。\r\n您收到这条消息表明该公众号没有对【{requestMessage.MsgType}】类型做处理。; return responseMessage; }从源码注释可以看出这个兜底方法还承担了另一个重要使命消息委托转发。如果需要把微信请求整体委托给其他服务器例如分布式架构或第三方网关只需在这里统一发出委托请求例如注释中给出的var responseMessage MessageAgent.RequestResponseMessage(agentUrl, agentToken, RequestDocument.ToString()); return responseMessage;为中间件提供工厂委托GenerateMessageHandler为了让中间件能够在每次收到请求时创建处理器实例示例在类中定义了一个静态工厂委托CustomMessageHandler.cspublic static FuncStream, PostModel, int, IServiceProvider, CustomMessageHandler GenerateMessageHandler (stream, postModel, maxRecordCount, serviceProvider) new CustomMessageHandler(stream, postModel, maxRecordCount, false /* 是否只允许处理加密消息以提高安全性 */, serviceProvider: serviceProvider);委托签名对应基类构造函数MessageHandler(Stream inputStream, PostModel postModel, int maxRecordCount, bool onlyAllowEncryptMessage, IServiceProvider serviceProvider)它把请求流、PostModel包含 Token / AppId / EncodingAESKey 等参数、上下文最大记录数和 DI 容器实例传给新创建的处理器是中间件与处理器之间的桥梁。构造函数中的全局设置构造函数中通常还可以做一些全局性设置CustomMessageHandler.cspublic CustomMessageHandler(Stream inputStream, PostModel postModel, int maxRecordCount 0, bool onlyAllowEncryptMessage false, IServiceProvider serviceProvider null) : base(inputStream, postModel, maxRecordCount, onlyAllowEncryptMessage, serviceProvider: serviceProvider) { GlobalMessageContext.ExpireMinutes 3; // 消息上下文过期时间分钟 OnlyAllowEncryptMessage true; // 是否只允许接收加密消息默认为 false }GlobalMessageContext.ExpireMinutes控制上下文在缓存中的存活时间示例设为 3 分钟实际开发也可以在更全局的位置设置。OnlyAllowEncryptMessage置为true后只处理加密消息可提高安全性但要求公众号后台开启消息加密且EncodingAESKey配置正确。MessageHandler 的两种托管方式处理器写好后还需要让它暴露给微信服务器访问。SDK 提供两种托管方式**中间件方式推荐**和Controller 方式。两种方式共用的都是同一个CustomMessageHandler因此可以随时切换或共存——例如一部分 URL 走中间件另一部分走 Controller。方式一中间件托管推荐中间件方式不需要创建任何新文件只需在Program.cs中、所有 Senparc.Weixin 注册代码执行完毕后追加一行调用即可。仓库示例Program.cs中的完整写法app.UseMessageHandlerForMp(/WeixinAsync, CustomMessageHandler.GenerateMessageHandler, options { // 获取默认微信配置 var weixinSetting Senparc.Weixin.Config.SenparcWeixinSetting; // [必填] 指定微信配置 options.AccountSettingFunc context weixinSetting; // [可选] 设置文本返回长度限制如需超长消息可通过客服接口分段回复 options.TextResponseLimitOptions new TextResponseLimitOptions(2048, weixinSetting.WeixinAppId); });官方文档给出的最简形式为app.UseMessageHandlerForMp( /WeixinAsync, CustomMessageHandler.GenerateMessageHandler, (options) { options.AccountSettingFunc (context) Senparc.Weixin.Config.SenparcWeixinSetting; } );参数说明参数说明/WeixinAsync路径规则路径开头可带参数此路径同时承担微信后台 URL 校验与消息推送CustomMessageHandler.GenerateMessageHandler上文介绍的处理器工厂委托options.AccountSettingFunc必填返回当前请求应使用的公众号配置ISenparcWeixinSettingForMP支持多公众号场景下按context动态选择options.TextResponseLimitOptions可选限制文本响应长度超出后可通过客服接口分段回复配置完成后MessageHandler 即可通过域名/WeixinAsync访问将该地址填入公众号后台的服务器配置/接口配置信息消息 URL 即可生效。注意中间件默认会使用异步方法messageHandler.ExecuteAsync()执行消息处理详见源码注释MpMessageHandlerMiddleware.cs。中间件的底层原理app.UseMessageHandlerForMp是MessageHandlerMiddlewareExtension提供的扩展方法其内部将请求交给泛型中间件MpMessageHandlerMiddlewareTMC处理MpMessageHandlerMiddleware.cspublic static IApplicationBuilder UseMessageHandlerForMpTMC(this IApplicationBuilder builder, PathString pathMatch, FuncStream, PostModel, int, IServiceProvider, MessageHandlerTMC, IRequestMessageBase, IResponseMessageBase messageHandler, ActionMessageHandlerMiddlewareOptionsISenparcWeixinSettingForMP options) where TMC : DefaultMpMessageContext, IMessageContextIRequestMessageBase, IResponseMessageBase, new() { return builder.UseMessageHandlerMpMessageHandlerMiddlewareTMC, TMC, PostModel, ISenparcWeixinSettingForMP(pathMatch, messageHandler, options); }中间件内部自动完成了三件关键工作GET 请求URL 校验GetCheckSignature用CheckSignature.Check校验signature通过后把echostr参数原样写回响应MpMessageHandlerMiddleware.cs这与微信公众号后台接口配置信息的 URL 验证流程完全对应。POST 请求签名校验PostCheckSignature在正式处理消息前再次校验签名失败直接返回签名校验失败MpMessageHandlerMiddleware.cs。PostModel 自动装配GetPostModel从options.AccountSettingFunc拿到公众号配置自动填充Token、AppId、EncodingAESKey并从查询字符串读取signature、timestamp、nonce、msg_signatureMpMessageHandlerMiddleware.cs。也就是说使用中间件后控制器里的签名校验、参数打包、消息执行、结果返回全被封装起来一行代码即可上线。方式二Controller 托管当中间件方式无法满足需求时例如需要在每个步骤中精确控制或干预执行过程可以使用 Controller 把执行流程展开。使用 Controller 需要创建2 个 Action一个GET用于微信后台的 URL 验证一个POST用于接收真实的消息推送。仓库示例位于 WeixinController.cs配置完成后通过域名/Weixin访问。GET微信后台验证[HttpGet] [ActionName(Index)] public ActionResult Get(PostModel postModel, string echostr) { if (CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token)) { return Content(echostr); //返回随机字符串则表示验证通过 } else { return Content(failed: postModel.Signature , CheckSignature.GetSignature(postModel.Timestamp, postModel.Nonce, Token) 。 如果你在浏览器中看到这句话说明此地址可以被作为微信公众账号后台的Url请注意保持Token一致。); } }三个静态常量Token、EncodingAESKey、AppId分别对应公众号后台配置MpSetting来自全局微信设置示例中的BaseController提供。Token 必须与公众号后台保持一致且区分大小写否则签名永远校验不通过。POST消息推送主流程POST Action 完整演示了 Controller 方式下消息处理的三步走WeixinController.cs[HttpPost] [ActionName(Index)] public async TaskActionResult Post(PostModel postModel) { if (!CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token)) { return Content(参数错误); } // 打包 PostModel 信息 postModel.Token Token; postModel.EncodingAESKey EncodingAESKey; postModel.AppId AppId; var maxRecordCount 10; // 第一步接收消息创建处理器 var messageHandler new CustomMessageHandler(await Request.GetRequestMemoryStreamAsync(), postModel, maxRecordCount); // 消息去重默认开启这里仅作为演示 messageHandler.OmitRepeatedMessage true; // 当同步方法被重写、且异步方法未被重写时尝试调用同步方法 messageHandler.DefaultMessageHandlerAsyncEvent DefaultMessageHandlerAsyncEvent.SelfSynicMethod; try { messageHandler.SaveRequestMessageLog(); // 记录 Request 日志可选 var ct new CancellationToken(); await messageHandler.ExecuteAsync(ct); // 第二步执行微信处理过程关键 messageHandler.SaveResponseMessageLog(); // 记录 Response 日志可选 return new FixWeixinBugWeixinResult(messageHandler); // 第三步返回结果 } catch (Exception ex) { // 异常写入 App_Data/Error_xxx.txt 日志文件并返回空内容 ... return Content(); } }各步骤要点第一步通过Request.GetRequestMemoryStreamAsync()读取请求体流连同PostModel一起构造CustomMessageHandler。maxRecordCount 10表示每个用户上下文最多缓存 10 条请求消息防止内存占用过多小于等于 0 则不限制实际最大 99999。注意如果使用分布式缓存不建议该值设置过大如需保存历史消息请使用数据库。OmitRepeatedMessage true开启消息去重。收到重复消息通常是因为微信服务器没有及时收到响应会持续发送 2~5 条相同内容的请求SDK 会自动过滤。该功能默认已是开启状态此处仅作为演示也可设为false在本次请求中停用。DefaultMessageHandlerAsyncEvent控制异步方法未重写时如何处理——SelfSynicMethod表示回退调用同名同步方法DefaultResponseMessageAsync表示直接调用默认异步响应方法详见下文默认方法的分发逻辑。第二步ExecuteAsync(ct)是真正的消息处理入口所有重写方法的调度都发生在这一步内部。第三步FixWeixinBugWeixinResult是 SDK 对微信换行 bug的修复包装负责把处理器生成的ResponseDocument以正确格式返回给微信服务器。日志SaveRequestMessageLog()/SaveResponseMessageLog()分别保存请求与响应 XML 日志可选。异常处理捕获所有异常后写入App_Data/Error_随机文件名.txt并通过WeixinTrace.Log记录最后返回空内容。Controller 与中间件的选择Controller 方式能让你在签名校验、参数打包、去重开关、异步回退策略、日志记录、异常处理等每一个环节都拥有完全控制权适合有特殊定制需求的场景中间件方式则适合绝大多数常规项目代码量最小。两种方式共用一个CustomMessageHandler可随时切换与共存。消息分发机制重写方法是如何被调用的理解了两种托管方式后再看 SDK 内部如何把收到的 XML 消息路由到你的重写方法。核心在BuildResponseMessageAsync中按RequestMessage.MsgType进行分发MessageHandlerAsync.csswitch (RequestMessage.MsgType) { case RequestMsgType.Text: ResponseMessage (await CurrentMessageHandlerNode.ExecuteAsync(requestMessage, this, weixinAppId)) ?? (await OnTextOrEventRequestAsync(requestMessage)) ?? (await OnTextRequestAsync(requestMessage)); break; case RequestMsgType.Location: ResponseMessage await OnLocationRequestAsync(RequestMessage as RequestMessageLocation); break; case RequestMsgType.Image: ResponseMessage await CurrentMessageHandlerNode.ExecuteAsync(...) ?? await OnImageRequestAsync(...); break; case RequestMsgType.Voice: ResponseMessage await OnVoiceRequestAsync(...); break; case RequestMsgType.Video: ResponseMessage await OnVideoRequestAsync(...); break; case RequestMsgType.Link: ResponseMessage await OnLinkRequestAsync(...); break; case RequestMsgType.ShortVideo: ResponseMessage await OnShortVideoRequestAsync(...); break; case RequestMsgType.File: ResponseMessage await OnFileRequestAsync(...); break; case RequestMsgType.Unknown: ResponseMessage await OnUnknownTypeRequestAsync(RequestMessage as RequestMessageUnknownType); break; ... }从代码可以读出三个重要事实文本消息的优先级链文本消息依次尝试CurrentMessageHandlerNode.ExecuteAsyncNeuChar 消息处理节点可接入第三方消息处理逻辑→OnTextOrEventRequestAsync文本与事件统一预处理→OnTextRequestAsync常规文本处理前一个返回null才进入下一个。事件消息走OnEventRequestAsync及其下属的OnEvent_XxxRequestAsync细分方法见下一节。未知消息类型走OnUnknownTypeRequestAsync不会直接抛异常v14.8.3 之前会抛异常。默认方法的分发逻辑当某个消息类型的异步方法没有被重写时SDK 根据DefaultMessageHandlerAsyncEvent自动决定回调哪个默认方法MessageHandlerAsync.csswitch (base.DefaultMessageHandlerAsyncEvent) { case DefaultMessageHandlerAsyncEvent.DefaultResponseMessageAsync: return await DefaultResponseMessageAsync(requestMessage); case DefaultMessageHandlerAsyncEvent.SelfSynicMethod: return await Task.Run(syncMethod); // 调用同名同步方法 default: throw new MessageHandlerException(...); }也就是说只要你的子类重写了DefaultResponseMessage()或DefaultResponseMessageAsync()任何未处理的消息类型都会安全地落到这个兜底方法上。事件消息处理CustomMessageHandler_Events.cs微信公众号的事件如关注、菜单点击、扫码是消息处理的高频场景示例在 CustomMessageHandler_Events.cs 中给出了几乎所有事件类型的重写方法。核心的几个如下// 订阅关注事件返回欢迎语 public override async TaskIResponseMessageBase OnEvent_SubscribeRequestAsync(RequestMessageEvent_Subscribe requestMessage) { var responseMessage ResponseMessageBase.CreateFromRequestMessageResponseMessageText(requestMessage); responseMessage.Content GetWelcomeInfo(); if (!string.IsNullOrEmpty(requestMessage.EventKey)) { responseMessage.Content \r\n\r\n场景值 requestMessage.EventKey; } return responseMessage; } // 退订事件用户实际上无法收到非订阅账号的消息这里可以随便写 // unsubscribe 事件的意义在于及时删除已记录的 OpenID 绑定、消除冗余数据并观测用户流失 public override async TaskIResponseMessageBase OnEvent_UnsubscribeRequestAsync(RequestMessageEvent_Unsubscribe requestMessage) { ... } // 菜单点击事件根据 EventKey 区分按钮 public override async TaskIResponseMessageBase OnEvent_ClickRequestAsync(RequestMessageEvent_Click requestMessage) { var reponseMessage CreateResponseMessageResponseMessageText(); if (requestMessage.EventKey OneClick) reponseMessage.Content 您点击了【单击测试】按钮; else reponseMessage.Content 您点击了其他事件按钮; return reponseMessage; }示例还覆盖了OnEvent_EnterRequestAsync进入会话、OnEvent_LocationRequestAsync位置上报、OnEvent_ScanRequestAsync扫码关注可读取EventKey场景值、OnEvent_ViewRequestAsync打开网页、OnEvent_MassSendJobFinishRequestAsync群发完成、OnEvent_ScancodePushRequestAsync/OnEvent_ScancodeWaitmsgRequestAsync扫码推、OnEvent_PicPhotoOrAlbumRequestAsync/OnEvent_PicSysphotoRequestAsync/OnEvent_PicWeixinRequestAsync拍照发图、OnEvent_LocationSelectRequestAsync地理位置选择器、OnEvent_QualificationVerifySuccessRequestAsync微信认证成功等。另外还有一个特殊的预处理钩子OnTextOrEventRequestAsyncCustomMessageHandler_Events.cs它同时接收文本与事件请求具有三个特征返回null时继续执行OnTextRequestAsync或OnEventRequestAsync返回非null时直接终止后续处理该值成为最终响应如果是事件SDK 会自动将事件消息转为RequestMessageText其中Content就是事件的EventKey——非常适合菜单按钮与关键字共用同一套逻辑的场景。消息上下文DefaultMpMessageContext 与自定义上下文每个用户OpenId与公众号的会话状态由**消息上下文MessageContext**维护包括请求/响应历史、过期时间、自定义存储数据等。SDK 提供了默认实现DefaultMpMessageContextDefaultMpMessageContext.cs它负责两件核心工作请求消息映射GetRequestEntityMappingResult把微信 XML 中的MsgType/Event字符串映射为强类型请求实体。从源码可以看到它覆盖了Text、Location、Image、Voice、Video、Link、ShortVideo、File、NeuChar等消息类型以及SUBSCRIBE、UNSUBSCRIBE、CLICK、SCAN、VIEW、LOCATION、MASSSENDJOBFINISH、SCANCODE_PUSH、各类卡券、微信认证等几十种事件类型无法识别的类型回退为RequestMessageUnknownType。响应消息映射GetResponseEntityMappingResult把ResponseMsgType枚举映射为ResponseMessageText、ResponseMessageNews、ResponseMessageMusic、ResponseMessageImage、ResponseMessageVoice、ResponseMessageVideo、ResponseMessageTransfer_Customer_Service、ResponseMessageNoResponse、SuccessResponseMessage等响应实体。何时需要自定义上下文v16.8.0 之后 SDK 支持分布式缓存大多数情况下直接使用DefaultMpMessageContext即可CustomMessageContext并非必需。如果需要在上下文过期被移除时执行清理逻辑例如用户长时间未互动后退出客服状态、发送提醒消息可以像示例那样继承它并订阅事件CustomMessageContext.cspublic class CustomMessageContext : DefaultMpMessageContext { public CustomMessageContext() { base.MessageContextRemoved CustomMessageContext_MessageContextRemoved; } void CustomMessageContext_MessageContextRemoved(object sender, WeixinContextRemovedEventArgsIRequestMessageBase, IResponseMessageBase e) { // 注意这个事件不是实时触发的可以专门写一个线程监控。 // 为提高效率根据 WeixinContext 中的算法 // 过期消息会在过期后、下一条请求执行之前被清除。 var messageContext e.MessageContext as CustomMessageContext; if (messageContext null) return; // TODO: 在这里执行消息过期时的业务逻辑 // Log.InfoFormat({0}的消息上下文已过期, e.OpenId); // api.SendMessage(e.OpenId, 由于长时间未搭理客服您的客服状态已退出); } }在处理器中读写上下文处理器重写执行生命周期方法OnExecutingAsync/OnExecutedAsync即可操作上下文数据CustomMessageHandler.cs示例演示了用StorageData记录每个用户的请求次数public override async Task OnExecutingAsync(CancellationToken cancellationToken) { var currentMessageContext await base.GetUnsafeMessageContext();//分布式缓存下读写效率更高需要实时数据应使用 GetCurrentMessageContext() if (currentMessageContext.StorageData null || !(currentMessageContext.StorageData is int)) { currentMessageContext.StorageData (int)0; } await base.OnExecutingAsync(cancellationToken); } public override async Task OnExecutedAsync(CancellationToken cancellationToken) { var currentMessageContext await base.GetUnsafeMessageContext(); currentMessageContext.StorageData ((int)currentMessageContext.StorageData) 1; GlobalMessageContext.UpdateMessageContext(currentMessageContext);//储存到缓存 await base.OnExecutedAsync(cancellationToken); }OnExecutingAsync在每个请求处理前触发OnExecutedAsync在请求处理完成后触发非常适合做统一日志、计数、会话维护等横切逻辑。GetUnsafeMessageContext()用于在分布式缓存下提高读写效率需要实时数据时应改用GetCurrentMessageContext()。常规消息处理实战示例CustomMessageHandler.cs中给出了全部常规消息类型的处理范例以下摘录几个典型实现。文本消息关键字匹配RequestHandler 链式处理文本消息是最高频的场景示例使用RequestHandler提供链式关键字匹配CustomMessageHandler.cs比传统if...else...更清晰public override async TaskIResponseMessageBase OnTextRequestAsync(RequestMessageText requestMessage) { var defaultResponseMessage base.CreateResponseMessageResponseMessageText(); var requestHandler await requestMessage.StartHandler() // 关键字不区分大小写按顺序匹配命中后不再运行后面的逻辑 .Keyword(关键字1, () { defaultResponseMessage.Content 收到关键字1; return defaultResponseMessage; }) // 匹配任一关键字 .Keywords(new[] { 关键字2, 关键字3 }, () { defaultResponseMessage.Content 收到“关键字2”或“关键字3”; return defaultResponseMessage; }) .Keyword(OPENID, () { var openId requestMessage.FromUserName;//获取OpenId var userInfo Weixin.MP.AdvancedAPIs.UserApi.Info(appId, openId, Language.zh_CN); defaultResponseMessage.Content string.Format(您的OpenID为{0} ..., requestMessage.FromUserName, ...); return defaultResponseMessage; }) .Keyword(MUTE, () // 不回复任何消息 { // 方案一返回 SuccessResponseMessage输出 success return new SuccessResponseMessage(); // 方案二var muteResponseMessage base.CreateResponseMessageResponseMessageNoResponse(); // 方案三base.TextResponseMessage success; return null; // 方案四return null; 并在 Action 中结合 FixWeixinBugWeixinResult 使用 }) // 菜单选择关键字微信服务器端最终格式ids:101,content满意 .SelectMenuKeyword(101, () { ... }) // 正则表达式匹配 .Regex(^\d#\d$, () { ... }) // 默认兜底未命中任何关键字时执行 .Default(async () { defaultResponseMessage.Content $您刚才发送了文字信息{requestMessage.Content}; return defaultResponseMessage; }); return requestHandler.GetResponseMessage() as IResponseMessageBase; }使用要点.Keyword()单关键字、.Keywords()多关键字、.Regex()正则、.SelectMenuKeyword()菜单选择、.Default()兜底按书写顺序依次匹配命中即停。当Default使用异步方法时需写在最后且StartHandler()前要用await等待异步方法执行使用同步方法时位置不限。MUTE关键字展示了不回复任何消息的四种方案其中返回SuccessResponseMessage会向微信输出success字符串是最干净的静默方案。OPENID分支演示了通过UserApi.Info(appId, openId, ...)拉取用户信息——从 2021 年 12 月 27 日起公众号已无法直接获取昵称、性别、地区等信息需要改用 OAuth 2.0 接口示例中给出了跳转授权页的说明。图片消息交替返回图文或图片示例用上下文中的请求历史做奇偶判断CustomMessageHandler.cs偶数次返回ResponseMessageNews图文奇数次返回ResponseMessageImage原图public override async TaskIResponseMessageBase OnImageRequestAsync(RequestMessageImage requestMessage) { if (base.GlobalMessageContext.GetMessageContext(requestMessage).RequestMessages.Count() % 2 0) { var responseMessage CreateResponseMessageResponseMessageNews(); responseMessage.Articles.Add(new Article() { Title 您刚才发送了图片信息, Description 您发送的图片将会显示在边上, PicUrl requestMessage.PicUrl, Url https://sdk.weixin.senparc.com }); return responseMessage; } else { var responseMessage CreateResponseMessageResponseMessageImage(); responseMessage.Image.MediaId requestMessage.MediaId; // 原样返回图片 return responseMessage; } }这里演示了GlobalMessageContext.GetMessageContext(requestMessage).RequestMessages读取该用户的请求历史——正是消息上下文能力的典型应用。语音消息返回音乐并推送客服消息public override async TaskIResponseMessageBase OnVoiceRequestAsync(RequestMessageVoice requestMessage) { var responseMessage CreateResponseMessageResponseMessageMusic(); // 上传临时素材获得缩略图 media_id var uploadResult Weixin.MP.AdvancedAPIs.MediaApi.UploadTemporaryMedia(appId, UploadMediaFileType.image, ServerUtility.ContentRootMapPath(~/Images/Logo.jpg)); responseMessage.Music.Title 天籁之音; responseMessage.Music.Description 播放您上传的语音; responseMessage.Music.MusicUrl https://sdk.weixin.senparc.com/Media/GetVoice?mediaId requestMessage.MediaId; responseMessage.Music.HQMusicUrl https://sdk.weixin.senparc.com/Media/GetVoice?mediaId requestMessage.MediaId; responseMessage.Music.ThumbMediaId uploadResult.media_id; // 再主动推送一条客服消息 try { CustomApi.SendText(appId, OpenId, 本次上传的音频MediaId requestMessage.MediaId); } catch { } return responseMessage; }视频消息异步上传素材并推送视频客服消息视频消息处理演示了Task.Factory.StartNew异步后台任务CustomMessageHandler.cs先通过MediaApi.GetAsync下载用户视频到临时目录再UploadTemporaryMediaAsync上传为永久素材最后用CustomApi.SendVideoAsync主动推送视频客服消息异常时通过WeixinTrace.Log记录并通过客服消息告知用户。未知消息类型OnUnknownTypeRequestAsyncSDK 可能遇到它不认识的新消息类型重写OnUnknownTypeRequestAsync可以优雅兜底CustomMessageHandler.cspublic override async TaskIResponseMessageBase OnUnknownTypeRequestAsync(RequestMessageUnknownType requestMessage) { // 原始XML可以通过 requestMessage.RequestDocument或 this.RequestDocument获取 var msgType Senparc.NeuChar.Helpers.MsgTypeHelper.GetRequestMsgTypeString(requestMessage.RequestDocument); var responseMessage this.CreateResponseMessageResponseMessageText(); responseMessage.Content 未知消息类型 msgType; WeixinTrace.SendCustomLog(未知请求消息类型, requestMessage.RequestDocument.ToString());//记录到日志中 return responseMessage; }如果不重写此方法遇到未知请求类型将抛出异常v14.8.3 之前的版本行为即如此。上线前配置清单无论采用哪种托管方式以下公众号后台配置都不可或缺URL中间件方式填https://你的域名/WeixinAsyncController 方式填https://你的域名/Weixin示例中WeixinController的IndexAction 同时响应 GET 与 POST。Token与代码中PostModel.Token/MpSetting.Token保持一致区分大小写。消息加解密方式若在构造函数中把OnlyAllowEncryptMessage设为true后台必须开启加密模式且EncodingAESKey与代码一致GetPostModel会自动读取msg_signature参数完成密文消息验签。AppId / Secret在 appsettings.json 的SenparcWeixinSetting中配置示例通过register.RegisterMpAccount(weixinSetting, 公众号名称)注册Program.cs。小结MessageHandler是公众号消息处理的统一入口SDK 封装了签名校验、XML 解析、上下文缓存、响应生成等全部基础能力开发者只需继承MessageHandlerDefaultMpMessageContext并重写业务方法。唯一必须重写的是DefaultResponseMessage()兜底响应其余OnXxxRequestAsync均为可选OnUnknownTypeRequestAsync负责未知类型的优雅降级。托管方式二选一**中间件方式推荐**一行代码接入、自动处理签名与 PostModelController 方式完全展开执行流程适合需要精确干预每一步的场景两者共用同一个CustomMessageHandler可随时切换或共存。借助RequestHandler链式关键字、OnTextOrEventRequestAsync文本/事件统一预处理、MessageContext上下文读写StorageData、过期事件可以低成本实现绝大多数公众号对话业务。进一步阅读完整的可运行示例位于 Samples/MP/Senparc.Weixin.Sample.MP/MessageHandlers 与 Samples/MP/Senparc.Weixin.Sample.MP/Controllers/WeixinController.cs中间件源码可查看 MpMessageHandlerMiddleware.cs消息分发与默认方法回退逻辑见 MessageHandlerAsync.cs消息类型映射见 DefaultMpMessageContext.cs。赞分享后端即时通讯金融科技【免费下载链接】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点击查看免费下载相关推荐Senparc.Weixin 小程序 MessageHandler自定义消息处理器与中间件/Controller 两种承载方式Senparc.Weixin 小程序 MessageHandler自定义消息处理器与中间件/Controller 两种承载方式 在微信小程序服务端开发中客服后端即时通讯金融科技Senparc.Weixin 企业微信 MessageHandler 实战自定义消息处理器与中间件、Controller 两种承载方式Senparc.Weixin 企业微信 MessageHandler 实战自定义消息处理器与中间件、Controller 两种承载方式 本文基于仓库文档 Me后端即时通讯金融科技BiliTools AI视频总结知识管理效率提升3倍的智能化解决方案BiliTools AI视频总结知识管理效率提升3倍的智能化解决方案 在信息爆炸的时代B站用户面临着海量视频内容与有限学习时间之间的矛盾。传统的视频观看模式后端即时通讯金融科技上一篇明日方舟游戏资源库一站式获取5000高清素材的完整解决方案下一篇django-allauth WebAuthn 与 Passkey 支持完整指南配置启用、登录流程与源码实现剖析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表