使用Senparc.Weixin.WxOpen开发高可用的微信小程序
<p>Senparc.Weixin SDK介绍</p><p>Senparc.Weixin SDk是目前.net平台上使用率最高的微信SDK,除硬件平台暂未发布以外覆盖了所有微信平台模块,自2013年免费开源起已经持续更新了4年,是GitHub上目前Star和Fork数最多的中国C#开源项目。</p>
<p>目前大多数模块都支持了.net 4.0 / .net 4.5 / .net core 三个版本,Senparc官方计划在2017年将新增.net 4.6.2版本,并着力强化.net core版本,在7月左右会停止对.net 4.0版本的更新。</p>
<p>其中,小程序的模块命名为Senparc.Weixin.WxOpen,Nuget地址:</p>
<p>https://www.nuget.org/packages/Senparc.Weixin.WxOpen</p>
<p>v1.0版本已于2017年1月9日凌晨同步上线!</p>
<p>Senparc.Wexin SDK系列库:</p>
<p> </p>
<p>过去的4年时间,Senparc团队总共迭代发布了100多个稳定版本,目前总体框架已经比较完善,可以应对超高并发在内的各种系统环境。</p>
<p>以下是官方提供的一些资源:</p>
<p>官网:http://weixin.senparc.com/</p>
<p>源代码(包含Demo):https://github.com/JeffreySu/WeiXinMPSDK</p>
<p>在线Demo(包含Nuget项目入口):http://sdk.weixin.senparc.com/</p>
<p>Demo公众号:盛派网络小助手</p>
<p> </p>
<p>公众号开发系列教程:http://www.cnblogs.com/szw/p/weixin-course-index.html </p>
<p>下载类库chm帮助文档:http://sdk.weixin.senparc.com/Document</p>
<p>在线类库帮助文档:http://doc.weixin.senparc.com/</p>
<p>微信开发资源汇总项目:https://github.com/JeffreySu/WeixinResource</p>
<p>Senparc.Weixin SDK交流QQ群:342319110</p>
<p>小程序开发交流QQ群:108830388 </p>
<p> </p>
<p>微信平台生态关系<br>加上小程序之后,整个公众号的平台生态又多了一环,根据开发代码体量和包含关系(尤其是核心重叠部分),目前大概是下面这样的场景:</p>
<p> </p>
<p>整个2016年,开发者们被微信小程序吊足了胃口,每每微信官方有小程序的动态发布,都会被刷屏,大众对微信小程序的关注程度可见一斑。</p>
<p>对于开发者来说,无论微信小程序的实际商业应用广度或是被接受度如何,这都将是一块熟悉又陌生的广阔的处女地。</p>
<p>由于小程序的多数规则和微信公众号基本一致,因此我们的小程序模块(Senparc.Weixin.WxOpen.dll)多数代码是从公众号模块(Senparc.Weixin.MP.dll)移植过来的。</p>
<p>微信官方为小程序准备了比较细致的文档和教程(这一点和当初公众号相比简直不在一个级别上),以下是一些重要的线上资源:</p>
<p>小程序开发文:https://mp.weixin.qq.com/debug/wxadoc/dev/</p>
<p>小程序设计指南:https://mp.weixin.qq.com/debug/wxadoc/design/</p>
<p>开发工具下载:<br>https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html</p>
<p>下文将整理目前为止对小程序后端开发最为重要的知识点、注意点进行介绍,或给出对应资源的地址,并就和微信公众号相关的技能给出相关的索引。</p>
<p>对于一个在高并发场景下保持高可用性的小程序来说,这几个方面的处理细节是不可忽视的:</p>
<p>消息处理</p>
<p>上下文</p>
<p>消息加密</p>
<p>AccessToken令牌处理</p>
<p>高级接口</p>
<p>异步开发</p>
<p>分布式缓存</p>
<p>同步锁</p>
<p>WebSocket</p>
<p>跟踪日志及异常处理</p>
<p>消息处理<br>微信小程序在进入客服会话状态之后,即可与客服进行对话,可以发送文字或图片信息,和公众号不同的是,目前小程序的消息系统是不能进行直接回复的,必须使用异步的方式调用“客服接口”推送消息(下文会讲到),为此我们优化了Senparc.Weixin SDK中的消息处理流程:MessageHandler。</p>
<p>MessageHandler是一个信消息的处理模块,也是整个微信开发过程中不可缺少的一部分。在MessageHandler中,开发者可以非常轻松地处理所有类型的微信消息。小程序的MessageHandler类叫做WxOpenMessageHandler。</p>
<p>在ASP.NET MVC我们只需要如下几步就可实现对消息的处理(和微信公众号一致)。</p>
<p>第一步:创建上上下文类,CustomWxOpenMessageContext.cs:</p>
<p>有关WxOpenMessageContext(上下文)的内容下文会讲到。</p>
<p>CustomWxOpenMessageContext.cs 代码如下:</p>
<p>using Senparc.Weixin.Context;</p>
<p>using Senparc.Weixin.WxOpen.Entities;</p>
<p>namespace Senparc.Weixin.MP.Sample.CommonService.WxOpenMessageHandler</p>
<p>{<br>public class CustomWxOpenMessageContext : MessageContext<IRequestMessageBase,IResponseMessageBase></p>
<p>{<br>public CustomWxOpenMessageContext()</p>
<p>{<br>base.MessageContextRemoved += CustomMessageContext_MessageContextRemoved;</p>
<p>}</p>
<p>/// <summary></p>
<p>/// 当上下文过期,被移除时触发的时间</p>
<p>/// </summary></p>
<p>/// <param name="sender"></param></p>
<p>/// <param name="e"></param></p>
<p>void CustomMessageContext_MessageContextRemoved(object sender, Senparc.Weixin.Context.WeixinContextRemovedEventArgs<IRequestMessageBase,IResponseMessageBase> e)</p>
<p>{<br>/* 注意,这个事件不是实时触发的(当然你也可以专门写一个线程监控)</p>
<p>* 为了提高效率,根据WeixinContext中的算法,这里的过期消息会在过期后下一条请求执行之前被清除</p>
<p>*/</p>
<p>var messageContext = e.MessageContext as CustomWxOpenMessageContext;</p>
<p>if (messageContext == null)</p>
<p>{<br>return;//如果是正常的调用,messageContext不会为null</p>
<p>}</p>
<p>//TODO:这里根据需要执行消息过期时候的逻辑,下面的代码仅供参考</p>
<p>//Log.InfoFormat("{0}的消息上下文已过期",e.OpenId);</p>
<p>//api.SendMessage(e.OpenId, "由于长时间未搭理客服,您的客服状态已退出!");</p>
<p>}</p>
<p>}</p>
<p>}</p>
<p>源码:</p>
<p> </p>
<p>第二步:创建自定义 CustomWxOpenMessageHandler.cs:</p>
<p>CustomWxOpenMessageHandler.cs 代码如下:</p>
<p>using System.IO;</p>
<p>using System.Web.Configuration;</p>
<p>using Senparc.Weixin.MP.Entities;</p>
<p>using Senparc.Weixin.MP.Entities.Request;</p>
<p>using Senparc.Weixin.MP.MessageHandlers;</p>
<p>using IRequestMessageBase = Senparc.Weixin.MP.Entities.IRequestMessageBase;</p>
<p>using IResponseMessageBase = Senparc.Weixin.MP.Entities.IResponseMessageBase;</p>
<p>namespace Senparc.Weixin.MP.Sample.CommonService.WxOpenMessageHandler</p>
<p>{<br>/// <summary></p>
<p>/// 自定义MessageHandler</p>
<p>/// 把MessageHandler作为基类,重写对应请求的处理方法</p>
<p>/// </summary></p>
<p>public partial class CustomWxOpenMessageHandler : MessageHandler<CustomWxOpenMessageContext></p>
<p>{<br>private string appId = WebConfigurationManager.AppSettings["WxOpenAppId"];</p>
<p>private string appSecret = WebConfigurationManager.AppSettings["WxOpenAppSecret"];</p>
<p>public CustomWxOpenMessageHandler(Stream inputStream, PostModel postModel, int maxRecordCount = 0)</p>
<p>: base(inputStream, postModel, maxRecordCount)</p>
<p>{<br>//这里设置仅用于测试,实际开发可以在外部更全局的地方设置,</p>
<p>//比如MessageHandler<MessageContext>.GlobalWeixinContext.ExpireMinutes = 3。</p>
<p>WeixinContext.ExpireMinutes = 3;</p>
<p>if (!string.IsNullOrEmpty(postModel.AppId))</p>
<p>{<br>appId = postModel.AppId;//通过第三方开放平台发送过来的请求</p>
<p>}</p>
<p>//在指定条件下,不使用消息去重</p>
<p>base.OmitRepeatedMessageFunc = requestMessage =></p>
<p>{<br>var textRequestMessage = requestMessage as RequestMessageText;</p>
<p>if (textRequestMessage != null && textRequestMessage.Content == "容错")</p>
<p>{<br>return false;</p>
<p>}</p>
<p>return true;</p>
<p>};</p>
<p>}</p>
<p>public override void OnExecuting()</p>
<p>{<br>//测试MessageContext.StorageData</p>
<p>if (CurrentMessageContext.StorageData == null)</p>
<p>{<br>CurrentMessageContext.StorageData = 0;</p>
<p>}</p>
<p>base.OnExecuting();</p>
<p>}</p>
<p>public override void OnExecuted()</p>
<p>{<br>base.OnExecuted();</p>
<p>CurrentMessageContext.StorageData = ((int)CurrentMessageContext.StorageData) + 1;</p>
<p>}</p>
<p>/// <summary></p>
<p>/// 处理文字请求</p>
<p>/// </summary></p>
<p>/// <returns></returns></p>
<p>public override IResponseMessageBase OnTextRequest(RequestMessageText requestMessage)</p>
<p>{<br>//TODO:这里的逻辑可以交给Service处理具体信息,参考OnLocationRequest方法或/Service/LocationSercice.cs</p>
<p>//这里可以进行数据库记录或处理</p>
<p>return new SuccessResponseMessage();</p>
<p>}</p>
<p>public override IResponseMessageBase OnImageRequest(RequestMessageImage requestMessage)</p>
<p>{<br>//发来图片,进行处理</p>
<p>return DefaultResponseMessage(requestMessage);</p>
<p>}</p>
<p>public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)</p>
<p>{<br>//所有没有被处理的消息会默认返回这里的结果</p>
<p>return new SuccessResponseMessage();</p>
<p>//return new SuccessResponseMessage();等效于:</p>
<p>//base.TextResponseMessage = "success";</p>
<p>//return null;</p>
<p>}</p>
<p>}</p>
<p>}</p>
<p>源码:</p>
<p> </p>
<p>微信小程序的MessageHandler和微信公众号的MessageHandler的一些区别见下表,这也是微信小程序和微信公众号在消息处理上的一些差别,务必须要注意!</p>
<p><br>微信公众号</p>
<p>微信小程序</p>
<p>请求消息类型</p>
<p>众多</p>
<p>1、 文本消息</p>
<p>2、 图片消息</p>
<p>请求事件类型</p>
<p>众多</p>
<p>进入会话(客服)事件</p>
<p>返回类型</p>
<p>1、基于</p>
<p>Senparc.Weixin.MP.</p>
<p>ResponseMessageBase</p>
<p>的子类</p>
<p>2、返回空字符串或"success"</p>
<p>(也可以使用SuccessResponseMessage)</p>
<p>只返回空字符串或"success"</p>
<p>(也可以使用SuccessResponseMessage)</p>
<p>注意:微信小程序的文本及图片类型请求消息,是在“客服对话”的状态下转发到开发者服务器的。</p>
<p>第三步:创建Conntroller,为使用MessageHandler做准备:</p>
<p>using System;</p>
<p>using System.IO;</p>
<p>using System.Web.Configuration;</p>
<p>using System.Web.Mvc;</p>
<p>using Senparc.Weixin.MP.Entities.Request;</p>
<p>using Senparc.Weixin.MP.MvcExtension;</p>
<p>using Senparc.Weixin.MP.Sample.CommonService. WxOpenMessageHandler;</p>
<p>namespace Senparc.Weixin.MP.Sample.Controllers.WxOpen</p>
<p>{<br>/// <summary></p>
<p>/// 微信小程序Controller</p>
<p>/// </summary></p>
<p>public partial class WxOpenController : Controller</p>
<p>{<br>public static readonly string Token = WebConfigurationManager.AppSettings["WxOpenToken"];//与微信公众账号后台的Token设置保持一致,区分大小写。</p>
<p>public static readonly string EncodingAESKey = WebConfigurationManager.AppSettings["WxOpenEncodingAESKey"];//与微信公众账号后台的EncodingAESKey设置保持一致,区分大小写。</p>
<p>public static readonly string AppId = WebConfigurationManager.AppSettings["WxOpenAppId"];//与微信公众账号后台的AppId设置保持一致,区分大小写。</p>
<p>}</p>
<p>}</p>
<p>源码:</p>
<p> </p>
<p>第四步:添加Action,使用MessageHandler</p>
<p>微信小程序的Url验证逻辑和微信公众号是一致的,为此我们需要添加2个Action来分别处理验证(GET)的请求和微信转发的消息请求(POST)。</p>
<p>GET请求:</p>
<p>/// <summary></p>
<p>/// GET请求用于处理微信小程序后台的URL验证</p>
<p>/// </summary></p>
<p>/// <returns></returns></p>
<p></p>
<p></p>
<p>public ActionResult Get(PostModel postModel, string echostr)</p>
<p>{<br>if (CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token))</p>
<p>{<br>return Content(echostr); //返回随机字符串则表示验证通过</p>
<p>}</p>
<p>else</p>
<p>{<br>return Content("failed:" + postModel.Signature + "," + MP.CheckSignature.GetSignature(postModel.Timestamp, postModel.Nonce, Token) + "。" +</p>
<p>"如果你在浏览器中看到这句话,说明此地址可以被作为微信小程序后台的Url,请注意保持Token一致。");</p>
<p>}</p>
<p>}</p>
<p>GET请求部署完毕之后,我们直接使用浏览器打开,可以看到一条提示,表明此Action已经可以被访问:</p>
<p> </p>
<p>POST请求:</p>
<p>/// <summary></p>
<p>/// 用户发送消息后,微信平台自动Post一个请求到这里,并等待响应XML。</p>
<p>/// </summary></p>
<p></p>
<p></p>
<p>public ActionResult Post(PostModel postModel)</p>
<p>{<br>if (!CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token))</p>
<p>{<br>return Content("参数错误!");</p>
<p>}</p>
<p>postModel.Token = Token;//根据自己后台的设置保持一致</p>
<p>postModel.EncodingAESKey = EncodingAESKey;//根据自己后台的设置保持一致</p>
<p>postModel.AppId = AppId;//根据自己后台的设置保持一致</p>
<p>var maxRecordCount = 50;</p>
<p>//第一步:MessageHandler,对微信请求的详细判断操作都在这里面。</p>
<p>var messageHandler = new CustomMessageHandler(Request.InputStream, postModel, maxRecordCount);</p>
<p>//第二步:执行微信处理过程</p>
<p>messageHandler.Execute();</p>
<p>//第三步:返回结果</p>
<p>return new FixWeixinBugWeixinResult(messageHandler);</p>
<p>}</p>
<p>核心的“三部曲”和微信公众号保持了完全的一致。</p>
<p>现在,只需要在Web.config中配置正确的AppId等信息,就可以在小程序后台完成“消息推送”接口的配置:</p>
<p> </p>
<p>随后,我们就能在微信小程序的预览状态或正式发布之后,测试消息处理能力,下文中的“高级接口”部分我们会丰富消息处理过程,并进行发布。</p>
<p>上下文<br>在CustomWxOpenMessageHandler的基类设置的时候,我们看到使用了一个叫MessageContext的泛型(MessageHandler<MessageContext>),这个MessageContext是Senparc.Weixin SDK提供的一个默认的消息上下文处理类。</p>
<p>MessageContext主要用于消息由微信服务器统一转发的情况下,无法针对单个用户对话使用Session的情况,同时也具有消息去重等方法。这个默认的类已经能够处理最基础的情况,如果您的应用不是很复杂,那么直接用这个类就行了。如果项目比较复杂,您也可以根据自己的需要创建一个自己的类(实现IMessageContext接口),或继承这个类之后,再扩展更多的属性(例如工作流和比较特殊的分布式缓存,等等)。</p>
<p>上下文除了弥补Session的作用存储个人数据以外,还提供了完整对话记录的保存,对于机器人回复等场景都具有非常重要的作用。</p>
<p>Senparc.Weixin SDk已经对上下文功能进行了比较充分的解耦,使之成为一个独立的模块,可以进行自由的重写和扩展。开发者可以自由设置上下文保存的时间及条数。</p>
<p>微信服务器在没有迅速收到应用程序服务器回应的情况下,在5秒有效等待时间内,内会连续发送共计3条相同的消息,这会给程序带来一些困扰,因此,消息去重在MessageHandler里面是一个非常重要的“标配”。</p>
<p>消息去重已经在Senparc.Weixin.MP.MessageHandlers.MessageHandler.cs的OnExecuting()方法中实现,默认为开启,如果在某些特殊需求下需要关闭,可以将OmitRepeatedMessage参数设置为false。</p>
<p>由于去重方法是在OnExecuting()方法中优先执行,因此OmitRepeatedMessage的设置必须早于OnExecuting(),可以在MessageHandler的构造函数中,也可以在MessageHandler实例化之后进行设置,例如:</p>
<p>var messageHandler = new CustomMessageHandler(Request.InputStream, postModel);//接收消息(第一步)</p>
<p>messageHandler. OmitRepeatedMessage = false;</p>
<p>messageHandler.Execute();//执行微信处理过程</p>
<p>消息加密<br>出于消息安全考虑,微信提供了消息加密的方法,并且推荐使用。微信公众号的后台会要求开发者填写EncodingAESKey,或自动生成,如下图:</p>
<p> </p>
<p>消息加密的状态在MessageHandler内部已经进行了自动判断及相关解码、编码的处理,因此开发者无因为消息加密的类型编写任何代码,只需要在微信后台按需设置即可。</p>
<p>在数据格式方面,微信小程序比微信公众号多提供了一个可选的JSON数据格式, Senparc.Weixin.WxOpen.dll将在v1.2版本提供对JSON格式的支持,目前请使用和微信公众号相同的XML格式。</p>
<p>AccessToken令牌处理<br>AccessToken是用于提供高级接口请求身份验证的令牌,由AppId及Secret通过API获得。AppId及Secret可以在微信小程序后台的【设置】>【开发设置】中找到或生成。</p>
<p>微信小程序的AccessToken令牌规则及使用方式与公众号完全兼容,因此我们在小程序模块中,共享了公众号的AccessToken处理模块:AccessTokenContainer。</p>
<p>AccessTokenContainer的作用是自动处理AccessToken的过期、储存(包括分布式缓存)等问题,让开发者可以专注到逻辑开发中,从而彻底忽略由于AccessToken延伸出来的一堆麻烦事。</p>
<p>AccessTokenContainer的使用要求开发者在Global或App_Start中对AppId及Secret进行注册(当然也可以在需要用到AccessToken之前的任意程序位置进行),例如:</p>
<p>protected void Application_Start()</p>
<p>{<br>/* 微信配置开始</p>
<p>*</p>
<p>* 建议按照以下顺序进行注册,尤其须将缓存放在第一位!</p>
<p>*/</p>
<p>RegisterWeixinCache(); //注册分布式缓存(按需,如果需要,必须放在第一个)</p>
<p>RegisterWeixinThreads(); //激活微信缓存及队列线程(必须)</p>
<p>RegisterSenparcWeixin(); //注册Demo所用微信公众号的账号信息(按需)</p>
<p>RegisterSenparcQyWeixin(); //注册Demo所用微信企业号的账号信息(按需)</p>
<p>RegisterWeixinPay(); //注册微信支付(按需)</p>
<p>RegisterWeixinThirdParty(); //注册微信第三方平台(按需)</p>
<p>ConfigWeixinTraceLog(); //配置微信跟踪日志(按需)</p>
<p>/* 微信配置结束 */</p>
<p>}</p>
<p>/// <summary></p>
<p>/// 注册Demo所用微信公众号的账号信息</p>
<p>/// </summary></p>
<p>private void RegisterSenparcWeixin()</p>
<p>{<br>//注册公众号</p>
<p>AccessTokenContainer.Register(</p>
<p>System.Configuration.ConfigurationManager.AppSettings["WeixinAppId"],</p>
<p>System.Configuration.ConfigurationManager.AppSettings["WeixinAppSecret"],</p>
<p>"【盛派网络小助手】公众号");</p>
<p>//注册小程序(完美兼容)</p>
<p>AccessTokenContainer.Register(</p>
<p>System.Configuration.ConfigurationManager.AppSettings["WxOpenAppId"],</p>
<p>System.Configuration.ConfigurationManager.AppSettings["WxOpenAppSecret"],</p>
<p>"【盛派互动】小程序");</p>
<p>}</p>
<p>上述红色的代码是和注册AccessTokenContainer有关的,实际上只需要添加3行代码,即可完全忘掉AccessToken这回事。</p>
<p>高级接口<br>小程序目前提供了包括二维码、Code换取、模板消息等少量的高级接口,并且兼容了微信公众号的文本及图片类型的客服接口(48小时内主动推送)。</p>
<p>Senparc.Weixin SDK为高级接口提供了非常高效、便捷的处理方式,用户如果已经注册了AppId及Secret,那么在调用高级接口的时候无需再提供AccessToken,只需提供AppId即可,AccessTokenContainer会自动识别传入的字符串属于AppId或是Secret,并对令牌过期等问题进行自动处理,确保在最大的可能性下面,开发者可以一次性得到期望的响应结果。</p>
<p>例如上文介绍的消息处理过程中,我们可以使用高级接口来即时回复用户:</p>
<p>/// <summary></p>
<p>/// 处理文字请求</p>
<p>/// </summary></p>
<p>/// <returns></returns></p>
<p>public override IResponseMessageBase OnTextRequest(RequestMessageText requestMessage)</p>
<p>{<br>//TODO:这里的逻辑可以交给Service处理具体信息,参考OnLocationRequest方法或/Service/LocationSercice.cs</p>
<p>//这里可以进行数据库记录或处理</p>
<p>//发送一条客服消息回复用户</p>
<p>var result = new StringBuilder();</p>
<p>result.AppendFormat("您刚才发送了文字信息:{0}\r\n\r\n", requestMessage.Content);</p>
<p>if (CurrentMessageContext.RequestMessages.Count > 1)</p>
<p>{<br>result.AppendFormat("您刚才还发送了如下消息({0}/{1}):\r\n", CurrentMessageContext.RequestMessages.Count,</p>
<p>CurrentMessageContext.StorageData);</p>
<p>for (int i = CurrentMessageContext.RequestMessages.Count - 2; i >= 0; i--)</p>
<p>{<br>var historyMessage = CurrentMessageContext.RequestMessages;</p>
<p>string content = null;</p>
<p>if (historyMessage is RequestMessageText)</p>
<p>{<br>content = (historyMessage as RequestMessageText).Content;</p>
<p>}</p>
<p>else if (historyMessage is RequestMessageEvent_UserEnterTempSession)</p>
<p>{<br>content = "[进入客服]";</p>
<p>}</p>
<p>else</p>
<p>{<br>content = string.Format("[非文字信息:{0}]", historyMessage.GetType().Name);</p>
<p>}</p>
<p>result.AppendFormat("{0} 【{1}】{2}\r\n",</p>
<p>historyMessage.CreateTime.ToShortTimeString(),</p>
<p>historyMessage.MsgType.ToString(),</p>
<p>content</p>
<p>);</p>
<p>}</p>
<p>result.AppendLine("\r\n");</p>
<p>}</p>
<p>//处理微信换行符识别问题</p>
<p>var msg = result.ToString().Replace("\r\n", "\n");</p>
<p>//使用微信公众号的接口,完美兼容</p>
<p>Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendText(appId, WeixinOpenId, msg);</p>
<p>return new SuccessResponseMessage();</p>
<p>//和公众号一样回复XML是无效的:</p>
<p>// return new SuccessResponseMessage()</p>
<p>// {<br>// ReturnText = string.Format(@"<?xml version=""1.0"" encoding=""utf-8""?></p>
<p>//<xml></p>
<p>// <ToUserName><!]></ToUserName></p>
<p>// <FromUserName><!]></FromUserName></p>
<p>// <CreateTime>1357986928</CreateTime></p>
<p>// <MsgType><!]></MsgType></p>
<p>// <Content><!]></Content></p>
<p>//</xml>",requestMessage.FromUserName,requestMessage.ToUserName)</p>
<p>// };</p>
<p>}</p>
<p>其中高级接口调用代码只需要一行:</p>
<p>Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendText(appId, WeixinOpenId, msg);</p>
<p>进入客服的状态也用同样的方式处理:</p>
<p>public override IResponseMessageBase OnEvent_UserEnterTempSessionRequest(RequestMessageEvent_UserEnterTempSession requestMessage)</p>
<p>{<br>//进入客服</p>
<p>var msg = "欢迎您!这条消息来自Senparc.Weixin进入客服事件。";</p>
<p>Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendText(appId, WeixinOpenId, msg);</p>
<p>return DefaultResponseMessage(requestMessage);</p>
<p>}</p>
<p>我们再来改写图片处理方式(注意,这次我们会使用异步模式):</p>
<p>public override IResponseMessageBase OnImageRequest(RequestMessageImage requestMessage)</p>
<p>{<br>//发来图片,进行处理</p>
<p>Task.Factory.StartNew(async () =></p>
<p>{<br>await Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendTextAsync(appId, WeixinOpenId, "刚才您发送了这张图片:");</p>
<p>await Senparc.Weixin.MP.AdvancedAPIs.CustomApi.SendImageAsync(appId, WeixinOpenId, requestMessage.MediaId);</p>
<p>});</p>
<p>return DefaultResponseMessage(requestMessage);</p>
<p>}</p>
<p>上述代码中,使用了和微信公众号兼容的图片高级接口。</p>
<p>在微信小程序的预览或发布状态下,用户可以收到如下回复:</p>
<p> </p>
<p>进入客服状态及发送文字及回复</p>
<p> </p>
<p>发送图片及回复</p>
<p>异步开发<br>在高并发的场景下,异步编程可以说是入门级的一个必备。</p>
<p>在Senparc.Weixin SDK中,我们总共提供了400多个异步方法,除可能的个别遗漏以外,已经覆盖了所有的高级接口,方法命名规则为同步方法名后加Async。我们的目标是覆盖所有并发场景下会成为瓶颈的接口,其中MessageHandler的异步版本也已经计划在Senparc.Weixin.dll v4.11中上线。</p>
<p>异步方法的使用在.NET 4.5之后已经变得非常简单,例如下面的是文字消息的同步方法调用:</p>
<p>AdvancedAPIs.CustomApi.SendText(appId, WeixinOpenId, "这是一条文字信息");</p>
<p>使用异步方法:</p>
<p>Task.Factory.StartNew(async () =></p>
<p>{<br>await AdvancedAPIs.CustomApi.SendTextAsync(appId, WeixinOpenId, "这是一条文字信息");</p>
<p>});</p>
<p>分布式缓存<br>在需要考虑缓存生命周期、并发量等诸多因素的情况下,我们通常都会采用负载均衡来环节服务器压力,这种情况下几乎都会用到分布式缓存。</p>
<p>尤其对于微信的公众号的AccessToken,同一时间内真正有效的AccessToken只有一个(当然也存在一段时间的有效缓存,企业号是没有这个问题的),如果多台服务器各自管理自己内存中的AccessToken数据,必然会导致一场不会停止的“令牌争夺战”:各自判断AccessToken失效之后,刷新私有的AccessToken,直到当天的AccessToken获取次数被用完,系统无法提供高级接口服务。</p>
<p>这种情况下就需要一个统一的“数据中心”来储存AccessToken等独一份的信息,实现的方法有很多:分布式缓存、数据库、文件、数据交换模块等等,通常我们会首选分布式缓存。</p>
<p>Senparc.Weixin SDK为分布式缓存做了比较细致的架构,可以适应许多复杂情况下微信对于缓存的需求,并且提供了Memcached(Senparc.Weixin.Cache.Memcached.dll)和Redis(Senparc.Weixin.Cache.Redis.dll)两种缓存的实现。相比之下我们更推荐Redis,也在Redis模块上花了更多的精力进行优化。</p>
<p>Senparc.Weixin SDK已经实现了缓存策略的热切换,开发者可以在任意时候切换缓存状态(本地缓存、Reids、Memcached或自己扩展的任何缓存策略),并且不会影响到AccessTokenContainner等模块的稳定性。</p>
<p>Senparc.Weixin SDK默认缓存采用单机的本地内存,缓存生命周期和应用程序紧密联系,如果要突破这样的限制,或是在分布式环境中使用,就需要引入外部的缓存,以Redis缓存为例,我们只需要3行代码即可完成切换:</p>
<p>//定义Redis连接字符串</p>
<p>var redisConfiguration = System.Configuration.ConfigurationManager.AppSettings["Cache_Redis_Configuration"];</p>
<p>//设置Redis连接字符串</p>
<p>RedisManager.ConfigurationOption = redisConfiguration;</p>
<p>//指定缓存策略实例</p>
<p>CacheStrategyFactory.RegisterObjectCacheStrategy(() => RedisObjectCacheStrategy.Instance);</p>
<p>只要配置正确,接下来的存取、序列化、储存格式、key管理等问题开发者都不必关心,无论使用什么缓存从策略,逻辑代码都无需做任何修改,Senparc.Weixin SDK的缓存策略接口已经对行为进行了严格规范,实现类会针对不同的缓存进行差别化的处理。</p>
<p>同步锁、WebSocket、跟踪日志及异常处理<br>请期待下一篇。</p>
<p>本文的Demo已经发布到GitHub的开源项目中,位于:https://github.com/JeffreySu/WeiXinMPSDK/tree/master/src/Senparc.Weixin.MP.Sample</p>
<p>以上部分内容节选自正在编写的《微信公众平台快速开发(拟)》图书,编写工作已经进入尾声,预计于年后进入出版流程,欢迎参与众筹:</p>
<p> </p>
<p> </p>
<p> </p>
<p> <br> <br> <br>.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注</p>
<p><br>————————————————<br>版权声明:本文为CSDN博主「dotNET跨平台」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。<br>原文链接:https://blog.csdn.net/sD7O95O/article/details/78096919</p><br><br>
来源:https://www.cnblogs.com/Alex80/p/14726504.html
頁:
[1]