返回 登录
0

微信开发深度解析:MessageHandler——简化消息处理流程

本文节选自苏震巍撰写的《微信开发深度解析:微信公众号、小程序高效开发秘籍》一书,由电子工业出版社出版。
责编:陈秋歌,关注微信开发等领域,寻求报道或者投稿请发邮件至chenqg#csdn.net。

MessageHandler 是一个微信消息的处理模块,也是整个微信开发过程中不可缺少的一部分。在 MessageHandler 中,开发者可以非常轻松地处理所有类型的微信消息。

本文将介绍 MessageHandler 的原理以及使用方法,包括支撑MessageHandler 运行所必需的实体类型、工厂方法等相关知识的介绍。

设计思想

如果你已经了解微信消息的基本通信原理,那我们现在可以非常方便地构造出一个简单的消息处理功能,如下:


StreamReader str = new StreamReader(Request.InputStream, System.Text.Encoding. UTF8);
XmlDocument xml = new XmlDocument();
xml.Load(str);
var wx = new WeixinRequest();
wx.ToUserName = xml.SelectSingleNode("xml").SelectSingleNode("ToUserName").InnerText;
wx.FromUserName = xml.SelectSingleNode("xml").SelectSingleNode("FromUserName"). InnerText;
wx.MsgType = xml.SelectSingleNode("xml").SelectSingleNode("MsgType").InnerText;
if (wx.MsgType.Trim() == "event")
{
    wx.EventName = xml.SelectSingleNode("xml").SelectSingleNode("Event").InnerText;
    //  WriteLog(wx.EventName);
    if (wx.EventName.ToUpper() == "LOCATION")
    {
        wx.Latitude = xml.SelectSingleNode("xml").SelectSingleNode("Latitude").InnerText;
        wx.Longitude = xml.SelectSingleNode("xml").SelectSingleNode("Longitude"). InnerText;
        wx.Precision = xml.SelectSingleNode("xml").SelectSingleNode("Precision"). InnerText;
    }
    else
    {
        wx.EventKey = xml.SelectSingleNode("xml").SelectSingleNode("EventKey"). InnerText;
    }
}
else if (wx.MsgType.Trim() == "text")
{
    wx.Content= xml.SelectSingleNode("xml").SelectSingleNode("Content").InnerText;
} else if (...)
{
    //...
}

这个方法也是目前很多其他框架甚至微信官方的Demo使用的,但是这种方法我可以用“不美好”来形容。

不美——首先使用字符串拼接的方式非常丑陋,其次哪怕使用 XmlDocument 或 XDocument 等面向对象的方式去处理,面对几十种不同的微信消息类型以及一一对应的不同的格式,代码将变得非常冗长而且难以维护。这样的代码你的老板或客户会喜欢吗?

不好——这样的写法坏处太多:

  • 可移植性差
  • 并没有做到很好地分离(无论是和整个应用程序还是不同请求类型之间)
  • 如果要做单元测试就必须整体代码一起上
  • 基本上不具备可扩展性
  • 容错能力很差,即使做到了,代码已经无法直视
  • 正常人用多了会心情不好

那么,“美好”的消息处理方式应该是怎么样的呢?

下面就将 Senparc.Weixin.MP.MessageHandler 介绍给你。

首先,美好的 MessageHandler 必须具有对消息类型的自动识别和分类能力。

第二,美好的 MessageHandler 必须能够同时、自动处理“明文”“兼容模式”“加密模式”三种(所有)消息加密类型,并且让开发者忘掉加密这回事情的存在。

第三,美好的 MessageHandler 必须能够提供很好的消息容器以及储存容器,来解决消息去重、Session 等一系列的问题。

第四,美好的 MessageHandler 必须能够兼容 MVC 和 WebFroms 不同的请求处理方式。

第五,美好的 MessageHandler 必须能够提供统一逻辑处理的接口,方便在特定的环节对消息进行统一处理。

第六,美好的 MessageHandler 必须具备优秀的可测试性和扩展能力。

第七,美好的 MessageHandler 必须能做到很好的逻辑分离。

第八,美好的 MessageHandler 必须让你用起来心情好。

第九,美好的 MessageHandler 不能保证你能在 10 分钟内,完成一个满足以上八条的简单微信应用从开发到上线、发布的全过程。但是我们做到了。

消息类型

概述

微信的互动消息包含请求消息响应消息两类:

  • 请求消息:由微信服务器发送到应用服务器的消息(通常由用户微信操作触发或用户主动发送)。
    -响应消息:应用服务器收到请求消息之后,返回给微信服务器的消息。响应消息可以被转发到用户微信,也可以采用“沉默”的方式不给予响应。

所有的消息类型如图1所示(至本文发布,消息类型又丰富了许多)。

图片描述

图1

无论是请求消息还是响应消息,各自还包括不同的消息类型,其中每一种消息类型,又都有不同的参数和对应的格式要求。

对应每一种消息具体的参数和 XML 格式可以参考微信官方Wiki,这里不再赘述。本书将重点针对面向对象的类展开介绍,使用 Senparc.Weixin SDK 的开发者基本上可以忘掉微信的 XML 格式和要求,只需要了解如何面向对象地处理消息。当然,对于 XML 的了解将帮助你更加从容地处理一些问题,例如测试和调试过程都可能需要用到 XML。

命名规则

为了可以使用 C# 面向对象地处理问题,同时也更加规范地进行编程,我们决定在一些地方改变微信原有的命名规则(全部使用小写,用下划线_分隔不同单词),而使用 C# 推荐的命名方式(Pascal 大小写命名法)来作为类名或属性名称。如微信文档中的属性或名称可能为 access_token,在 Senparc.Weixin 中将被命名为 AccessToken。当阅读微信官方的文档时理解这样的变化举一反三即可。

当然,这种改变是灵活的,有一些地方我们仍然需要保持参数名称的“原貌”来确保 JSON 和 XML 自动转换的准确性和稳定性。

这样的改变不仅出现在消息类型中,对本书后面会介绍的 API 和其他功能同样适用。
全局消息基类

在所有这些消息类型中,都具有一些相同的属性,因此在 MessageHandler 体系中,我们为所有的消息创建了一个基类 MessageBase

    /// <summary>
    /// 所有Request和Response消息的基类
    /// </summary>
    public abstract class MessageBase : IMessageBase
    {
        public string ToUserName { get; set; }
        public string FromUserName { get; set; }
        public DateTime CreateTime { get; set; }
    }

MessageBase将作为所有消息的基类,无论是请求消息还是响应消息,也无论是微信公众号还是企业号或开放平台等,MessageBase 对微信消息进行了非常高度的抽象和概括。

下面我们将分别介绍的请求消息和响应消息都是以 MessageBase 作为基类的。

请求消息

请求消息又分为两种类型:普通消息事件推送消息。无论是哪一种消息,它们都具有相同的基础参数,如表1所示。

图片描述

为此,在 MessageBase 的基础上,我们为所有的请求消息设置了一个基类 RequestMessageBase

图片描述

请注意 RequestMessageBase 是一个抽象类,因此不可以被直接实例化。事实上,这个类也不应该被实例化,因为我们没有办法通过实例化这个类达到操作一个具体的请求类型消息的目的,我们仍然需要创建基于 RequestMessageBase 的对应每一个消息类型的实体来对其进行操作(作为全局考虑,不光微信公众号的请求消息需要用到 RequestMessageBase,企业号、开放平台等其他模块也需要用到)。

以上的基类都定义在 Senparc.Weixin.dll 中,普通消息的定义在Senparc.Weixin.MP.dll 中,直接使用 RequestMessageBase 作为基类,对应关系如表2所示。

图片描述

事件推送消息比普通消息多了 Event 这个属性,因此我们为所有的事件推送消息定义了一个统一的基类 RequestMessageEventBase

    public class RequestMessageEventBase : RequestMessageBase, IRequestMessageEventBase
    {
        public override RequestMsgType MsgType
        {
            get { return RequestMsgType.Event; }
        }

        /// <summary>
        /// 事件类型
        /// </summary>
        public virtual Event Event
        {
            get { return Event.ENTER; }
        }
    }

可以看到 RequestMessageEventBaseRequestMessageBase 多了一个 Event 属性,并且对MsgType 属性进行了强制的规定,默认情况下所有继承 RequestMessageEventBase 的类型都将返回 RequestMsgType.Event,这样 MessageHandler 将可以对事件推送消息消息类型进行特殊的处理。

由于事件推送消息类型比较多,我们将其分为 3 大类:

  • 常规事件(公众号基础功能返回事件)
  • 菜单事件(各种类型的公众号菜单返回事件)
  • 应用事件(应用模块返回事件,例如卡券、多客服等)

对应如表3所示。

图片描述

以上的类型中,都具有从 RequestMessageEventBase 继承而来的 Event 属性,有部分类型都具有 EventKey 属性,说明如表4所示。

图片描述

并不是所有的事件都具有EventKey,因此我们提供了IRequestMessageEventKey 接口,所有实现此接口的消息类型必须提供 EventKey,例如订阅事件:

    /// <summary>
    /// 事件之订阅
    /// </summary>
    public class RequestMessageEvent_Subscribe : RequestMessageEventBase, IRequestMessageEventBase, IRequestMessageEventKey
    {
        /// <summary>
        /// 事件类型
        /// </summary>
        public override Event Event
        {
            get { return Event.subscribe; }
        }

        /// <summary>
        /// 事件KEY值,qrscene_为前缀,后面为二维码的参数值(如果不是扫描场景二维码,此参数为空)
        /// </summary>
        public string EventKey { get; set; }
        /// <summary>
        /// 二维码的ticket,可用来换取二维码图片(如果不是扫描场景二维码,此参数为空)
        /// </summary>
        public string Ticket { get; set; }
    }

有些事件则不需要 EventKey,如取消订阅事件:

    /// <summary>
    /// 事件之取消订阅
    /// </summary>
    public class RequestMessageEvent_Unsubscribe : RequestMessageEventBase, IRequestMessageEventBase
    {
        /// <summary>
        /// 事件类型
        /// </summary>
        public override Event Event
        {
            get { return Event.unsubscribe; }
        }
    }

注意:实现 EventKey 参数类型都是字符串,但是在不同的事件中会具有不同的含义,有些官方已经规定了参数格式(如:扫描带参数二维码事件,都是以 qrscene_ 为前缀),而有些可以自己定义(如:菜单点击事件),在开发的过程中需要注意。


注意:事件推送消息的触发大部分是通过用户的某个微信界面的操作触发的(如:菜单点击、发送语音等),也有通过其他操作触发的(如:微小店订单付款通知、多客服转接会话等),因此在“人肉测试”的时候要格外注意结合参考微信官方的文档,了解其触发流程,避免误认为程序问题而没有触发事件的错误判断。

响应消息
响应消息和请求消息的设计原理类似,所有响应消息的类型都继承自ResponseMessageBase,ResponseMessageBase 同样继承自 MessageBase。 Senparc.Weixin.dll 下的 ResponseMessageBase (Weixin.Entities.ResponseMessageBase)代码如下:

 /// <summary>
 /// 响应回复消息
 /// </summary>
 public abstract class ResponseMessageBase : MessageBase, IResponseMessageBase
 {
 }

以上 Weixin.Entities.ResponseMessageBase 适用于包含微信公众号、企业号、开发平台等在内的多个平台,作为响应消息的基类。

为了增强 MessageHandler 的便捷性,Senparc.Weixin.MP.dll 提供了专门用于微信公众号的 ResponseMessageBase (Senparc.Weixin.MP.Entities. ResponseMessageBase,继承自 Senparc.Weixin. Entities.ResponseMessageBase),代码如下:

    /// <summary>
    /// 微信公众号响应回复消息基类
    /// </summary>
    public class ResponseMessageBase : Weixin.Entities.ResponseMessageBase, IResponseMessageBase
    {
        public virtual ResponseMsgType MsgType
        {
            get { return ResponseMsgType.Text; }
        }

        /// <summary>
        /// 获取响应类型实例,并初始化
        /// </summary>
        /// <param name="requestMessage">请求</param>
        /// <param name="msgType">响应类型</param>
        /// <returns></returns>
        [Obsolete("建议使用CreateFromRequestMessage<T>(IRequestMessageBase requestMessage)取代此方法")]
        private static ResponseMessageBase CreateFromRequestMessage(IRequestMessageBase requestMessage, ResponseMsgType msgType)
        {
            //略
        }

        /// <summary>
        /// 获取响应类型实例,并初始化
        /// </summary>
        /// <typeparam name="T">需要返回的类型</typeparam>
        /// <param name="requestMessage">请求数据</param>
        /// <returns></returns>
        public static T CreateFromRequestMessage<T>(IRequestMessageBase requestMessage) where T : ResponseMessageBase
        {
            //略        }

        /// <summary>
        /// 从返回结果XML转换成IResponseMessageBase实体类
        /// </summary>
        /// <param name="xml">返回给服务器的Response Xml</param>
        /// <returns></returns>
        public static IResponseMessageBase CreateFromResponseXml(string xml)
        {
            //略        }
    }

通过上述的两个 ResponseMessageBase,我们可以发现,和 RequestMessageBase 不同的是,ResponseMessageBase 没有提供 MsgId,这就意味着:应用程序服务器回复微信服务器的消息,必须一次成功,微信没有提供和响应消息一样的容错机制(数秒内收不到响应则连续发送几条带有 MsgId 的消息到应用服务器,确保消息可以到达)。也就是说,如果这条响应消息因为各种原因没有发送成功(网络问题或格式错误),客户端将收不到正确的消息回复,通常还会显示一条“该公众号暂时无法提供服务,请稍后再试(Official account services unavailable. Try again later)”的错误信息,如图2所示。

图片描述

图2

此错误可以在“盛派网络小助手”发送文字“错误”进行测试。

注意:对于多条带有相同 MsgId 的请求消息进行多次回复,客户端也只能收到微信服务器最后一次重发所对应的这条响应消息。

例如由于应用程序服务器没有及时响应,微信服务器连续发送了 3 条MsgId都为 6224799151644543291 的消息到应用程序服务器,这三次请求分别为 A、B、C,应用程序服务器由于没有对消息去重,分别响应了 A1、B1、C1,此时,客户端只会收到一条 C1 的回复。此错误可以在“盛派网络小助手”发送文字“容错”进行测试。

响应消息的类型比请求消息要少许多,如表5所示。

图片描述

上述响应类型中,有部分包含复杂的属性,我们将其独立创建类,分别有:Image、Voice、Video、Music、Article 这些类型。
以比较常用的图文消息为例,ResponseMessageNews 定义如下:

/// <summary>
    /// 图文消息
    /// </summary>
    public class ResponseMessageNews : ResponseMessageBase, IResponseMessageBase
    {
        new public virtual ResponseMsgType MsgType
        {
            get { return ResponseMsgType.News; }
        }

        public int ArticleCount
        {
            get
            {
                return Articles == null ? 0 : Articles.Count;
            }
            set
            {
                //这里开放set只为了逆向从Response的XML转成实体的操作一致性,没有实际意义。
            }
        }

        /// <summary>
        /// 文章列表,微信客户端只能输出前10条(可能未来数字会有变化,出于视觉效果考虑,建议控制在8条以内)
        /// </summary>
        public List<Article> Articles { get; set; }

        public ResponseMessageNews()
        {
            Articles = new List<Article>();
        }
    }

由于工厂模式自动初始化的需要,必须提供一个不带参数的构造函数,因此Articles参数无法通过构造函数设置,必须分成两步:

    var reponseMessage = CreateResponseMessage<ResponseMessageNews>();
    reponseMessage.Articles.Add(new Article()
    {
        Title = "你点击了子菜单图文按钮",
        Description = "你点击了子菜单图文按钮,这是一条图文信息。",
        PicUrl = "http://weixin.senparc.com/Images/qrcode.jpg",
        Url = "http://weixin.senparc.com"
    });

当 Articles 列表中只有 1 个 Article 对象的时候,显示为单图文,如图3所示。当 Article 大于 1 个时,显示为多图文,如图4所示。Articles 中最多允许有 10 个 Article 对象,即 10 条图文信息。

图片描述

使用MessageHandler

下面来看一下 MessageHandler 是如何让你爱上微信开发的。

在上述例子中,你只需要做三步:

  • 第一步:通过 Nuget 安装 Senparc.Weixin.MP。
  • 第二步:创建你自己的 MessageHandler(大部分代码只需要复制)。
  • 第三步:写 3 行关键代码(同样只需要复制)。

下面我们来跟随这三步,略为详细地展开一下 MessageHandler 最基础的一些用法,随后我们将学习 MessageHandler 的内部实现。

第一步:通过Nuget安装Senparc.Weixin.MP

第二步:创建你自己的MessageHandler

创建新文件 CustomMessageHandler.cs。CustomMessageHandle.cs 需要继承 Senparc.Weixin.MP. MessageHandlers 这个抽象类,并实现部分方法。最初步的 CustomMessageHandle.cs 代码可以如下。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using Senparc.Weixin.MP.Context;
using Senparc.Weixin.MP.Entities;
using Senparc.Weixin.MP.MessageHandlers;

namespace Senparc.Weixin.MP.Sample.Weixin
{
    public class CustomMessageHandler : MessageHandler<CustomMessageContext>
    {
        public CustomMessageHandler(Stream inputStream, PostModel postModel)
            : base(inputStream, postModel)
        {

        }

        public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)
        {
            var responseMessage = base.CreateResponseMessage<ResponseMessageText>(); //ResponseMessageText也可以是News等其他类型
            responseMessage.Content = "这条消息来自DefaultResponseMessage。";
            return responseMessage;
        }
    }
}

我们可以看到必须要重写实现的抽象方法名为 DefaultResponseMessage(),这一条信息用于返回一条默认(替补)消息,假如对应类型(如语音)的微信消息没有被代码处理,那么默认会返回这里的结果。

在 DefaultResponseMessage() 方法中,有这样一句:

var responseMessage = base.CreateResponseMessage<ResponseMessageText>(); //ResponseMessageText也可以是News等其他类型

这里的 CreateResponseMessage 方法即创建一个返回对象,T 可以为以下类型的任意一个,分别对应了不同的返回类型,具体类型及说明请参考表5。

关于上述所有类型参数的设置方法,可以看 Senparc.Weixin SDK 的官方 Demo,这里不再重复。

接下来,我们了解下如何处理微信服务器发过来的文字信息。

很简单——在 CustomMessageHandler 里面重写一个 OnTextRequest 方法即可 :


public override IResponseMessageBase OnTextRequest(RequestMessageText requestMessage)
{
    if (requestMessage.Content == "你好")
    {
        var responseMessage = base.CreateResponseMessage<ResponseMessageNews>();
        var title = "Title";
        var description = "Description";
        var picUrl = "PicUrl";
        var url = "Url";
        responseMessage.Articles.Add(new Article()
        {
            Title = title,
            Description = description,
            PicUrl = picUrl,
            Url = url
        });
        return responseMessage;
    }
    else if (requestMessage.Content == "Senparc")
    {
        //相似处理逻辑
    }
    else
    {
        //...
    }
}

这个方法中可以自由发挥,比如读取数据库、判断关键字,甚至返回不同的ResponseMessageXX 类型(只要最终的类型都是在 IResponseMessageBase 接口下的即可)。

与 OnTextRequest 对应,如果我们要处理语音、地理位置、菜单等类型的消息,只需要重写对应的方法,可以重写的方法如下:

  • 接收消息方法
#region 接收消息方法

/// <summary>
/// 默认返回消息(当任何OnXX消息没有被重写,都将自动返回此默认消息)
/// </summary>
public abstract IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage);

/// <summary>
/// 预处理文字或事件类型请求。
/// 这个请求是一个比较特殊的请求,通常用于统一处理来自文字或菜单按钮的同一个执行逻辑,
/// 会在执行OnTextRequest或OnEventRequest之前触发,具有以下一些特征:
/// 1、如果返回null,则继续执行OnTextRequest或OnEventRequest
/// 2、如果返回不为null,则终止执行OnTextRequest或OnEventRequest,返回最终ResponseMessage
/// 3、如果是事件,则会将RequestMessageEvent自动转为RequestMessageText类型,其中RequestMessageText.Content就是RequestMessageEvent.EventKey
/// </summary>
public virtual IResponseMessageBase OnTextOrEventRequest(RequestMessageText requestMessage){...}

/// <summary>
/// 文字类型请求
/// </summary>
public virtual IResponseMessageBase OnTextRequest(RequestMessageText requestMessage) {...}

/// <summary>
/// 位置类型请求
/// </summary>
public virtual IResponseMessageBase OnLocationRequest(RequestMessageLocation requestMessage) {...}

/// <summary>
/// 图片类型请求
/// </summary>
public virtual IResponseMessageBase OnImageRequest(RequestMessageImage requestMessage) {...}

/// <summary>
/// 语音类型请求
/// </summary>
public virtual IResponseMessageBase OnVoiceRequest(RequestMessageVoice requestMessage) {...}

/// <summary>
/// 视频类型请求
/// </summary>
public virtual IResponseMessageBase OnVideoRequest(RequestMessageVideo requestMessage) {...}

/// <summary>
/// 链接消息类型请求
/// </summary>
public virtual IResponseMessageBase OnLinkRequest(RequestMessageLink requestMessage) {...}

/// <summary>
/// 小视频类型请求
/// </summary>
public virtual IResponseMessageBase OnShortVideoRequest(RequestMessageShortVideo requestMessage) {...}

#endregion
  • 接收事件方法
#region Event下属分类,接收事件方法

/// <summary>
/// Event事件类型请求之ENTER
/// </summary>
public virtual IResponseMessageBase OnEvent_EnterRequest(RequestMessageEvent_ Enter requestMessage){...}

/// <summary>
/// Event事件类型请求之LOCATION
/// </summary>
public virtual IResponseMessageBase OnEvent_LocationRequest(RequestMessageEvent_ Location requestMessage) {...}

//依此类推还有:
// Event事件类型请求之subscribe:OnEvent_SubscribeRequest()
// Event事件类型请求之unsubscribe:OnEvent_UnsubscribeRequest()
// Event事件类型请求之CLICK:OnEvent_ClickRequest()
// Event事件类型请求之scan:OnEvent_ScanRequest()
// 事件之URL跳转视图(View):OnEvent_ViewRequest()
// 事件推送群发结果:OnEvent_MassSendJobFinishRequest()
// 发送模板消息返回结果:OnEvent_TemplateSendJobFinishRequest()
// 弹出拍照或者相册发图:OnEvent_PicPhotoOrAlbumRequest()
// 扫码推事件:OnEvent_ScancodePushRequest()
// 扫码推事件且弹出“消息接收中”提示框:OnEvent_ScancodeWaitmsgRequest()
// 弹出地理位置选择器:OnEvent_LocationSelectRequest()
// 弹出微信相册发图器: OnEvent_PicWeixinRequest()
// 弹出系统拍照发图:OnEvent_PicSysphotoRequest()
// 卡券通过审核:OnEvent_Card_Pass_CheckRequest()
// 卡券未通过审核: OnEvent_Card_Not_Pass_CheckRequest()
// 领取卡券:OnEvent_User_Get_CardRequest()
// 删除卡券:OnEvent_User_Del_CardRequest()
// 多客服接入会话:OnEvent_Kf_Create_SessionRequest()
// 多客服关闭会话:OnEvent_Kf_Close_SessionRequest()
// 多客服转接会话:OnEvent_Kf_Switch_SessionRequest()
// Event事件类型请求之审核结果事件推送:OnEvent_Poi_Check_NotifyRequest()
// Event事件类型请求之Wi-Fi连网成功:OnEvent_WifiConnected()
// Event事件类型请求之卡券核销:OnEvent_User_Consume_Card()
// Event事件类型请求之从卡券进入公众号会话:OnEvent_User_Enter_Session_From_Card()
// Event事件类型请求之进入会员卡:OnEvent_User_View_Card()
// Event事件类型请求之微小店订单付款通知:OnEvent_Merchant_Order()
// Event事件类型请求之接收会员信息事件通知:OnEvent_Submit_Membercard_User_Info()
// Event事件类型请求之摇一摇事件通知:OnEvent_ShakearoundUserShake()

#endregion

其中 OnEvent_XX 对应的都是 Event 请求的子类型。

第三步:写3行关键代码

在已经创建好的 WeixinController.cs 中,加入如下代码:

[HttpPost]
[ActionName("Index")]
public ActionResult Post(PostModel postModel)
{
    if (!CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel. Nonce, Token))
    {
        return Content("参数错误!");
    }

    postModel.Token = Token;
    postModel.EncodingAESKey = EncodingAESKey;//根据自己后台的设置保持一致
    postModel.AppId = AppId;//根据自己后台的设置保持一致

    var messageHandler = new CustomMessageHandler(Request.InputStream, postModel); //接收消息(第一步)

    messageHandler.Execute();//执行微信处理过程(第二步)

    return new FixWeixinBugWeixinResult(messageHandler);//返回(第三步)
}

如果你不需要进行保存消息日志等操作,这一步也几乎可以通过复制完成,不需要修改任何东西。当然即使需要保存日志,我们的 Demo 中也已经有相关案例可以直接使用。

上述代码中的 FixWeixinBugWeixinResult 用于提供给 MVC 一个处理一些微信官方疏忽的问题或者由于跨平台导致的字符识别问题的修正方法,使所有手机平台都可以得到一致、稳定的结果。这个方法需要使用到 Senparc.Weixin.MP.MvcExtension.dll。

至此我们已经使用 MassageHandler 处理所有微信用户发送过来的请求。

点击订购:《微信开发深度解析:微信公众号、小程序高效开发秘籍

图片描述

评论