作者:王先荣
本文介绍如何处理微信公众号开发中的消息与事件,包括:(1)消息(事件)概况;(2)验证消息的真实性;(3)解析消息;(4)被动回复消息;(5)发送其他消息。
开源项目地址:http://git.oschina.net/xrwang2/xrwang.weixin.PublicAccount
本文源代码比较分散,主要在:
http://git.oschina.net/xrwang2/xrwang.weixin.PublicAccount/blob/master/xrwang.net/WeixinInterface.ashx
http://git.oschina.net/xrwang2/xrwang.weixin.PublicAccount/blob/master/xrwang.net/Example/ParseRequestMessage.aspx.cs
http://git.oschina.net/xrwang2/xrwang.weixin.PublicAccount/tree/master/PublicAccount/RequestMessage
http://git.oschina.net/xrwang2/xrwang.weixin.PublicAccount/tree/master/PublicAccount/ResponseMessage
本文的演示地址:
(1)http://xrwang.net/Example/ParseRequestMessage.aspx
(2)关注我的公众号,并发送消息
测试号 |
测试号权限多,几乎可以测试公众平台的所有功能。 |
|
我的公众号 xrwang |
个人订阅号,功能较少,不过我会特别优化。 |
1 消息(事件)概况
当普通微信用户向公众号发消息或者微信服务器向公众号推送事件时,微信服务器将POST消息(事件)的XML数据包到开发者填写的公众号服务器URL上;公众号服务器然后对消息作出响应。
1.1 消息的流转过程
为了便于区分,我们将微信服务器发往公众号服务器的消息称为请求(Request)消息;将公众号服务器发往微信服务器的消息称为响应(Response)消息;将推送事件看成特殊的请求消息。
请求与响应消息的流转过程如下图所示:
1.2 请求消息
请求消息有很多种,我们为其一一建立了对应的类,类层次结构如下图所示:
有些请求消息,我们可以做出响应,有些则不能,详见下表:
消息类型 | 是否事件 | 能够被动回复 | 备注 |
文本 | × | √ | |
图片 | × | √ | |
声音 | × | √ | |
视频 | × | 未知 | 接收不到视频消息,不知道是否能被动回复 |
地理位置 | × | √ | |
链接 | × | √ | |
订阅 | √ | √ | |
取消订阅 | √ | × | |
扫描二维码 | √ | × | |
上报地理位置 | √ | × | |
点击菜单拉取消息 | √ | √ | |
点击菜单跳转链接 | √ | × | |
点击菜单扫码推 | √ | × | |
点击菜单扫码等待回复 | √ | √ | |
点击菜单系统发图 | √ | 未知 | 接收不到系统发图事件;微信服务器会发送图片消息,可回复 |
点击菜单拍照或相册发图 | √ | × | 微信服务器会发送图片消息,可回复 |
点击菜单微信发图 | √ | × | 微信服务器会发送图片消息,可回复 |
点击菜单选择地理位置 | √ | × | 微信服务器会发送地理位置消息,可回复 |
推送群发消息结果 | √ | × | |
推送发送模板消息结果 | √ | × |
1.3 响应消息
响应消息的类层次结构如下图所示:
2 验证消息的真实性
公众号服务器接收到微信服务器的请求之后,第一件事情是验证消息的真实性。
Utility.CheckSignature方法用于验证消息签名是否正确。
- /// <summary>
- /// 验证消息的有效性
- /// </summary>
- /// <param name="context"></param>
- /// <returns>如果消息有效,返回true;否则返回false。</returns>
- private bool Validate(HttpContext context)
- {
- string username = RequestEx.TryGetQueryString("username"); //在接口配置的URL中加入了username参数,表示哪个微信公众号
- AccountInfo account = AccountInfoCollection.GetAccountInfo(username);
- if (account == null)
- return false;
- string token = account.Token;
- string signature = RequestEx.TryGetQueryString("signature");
- string timestamp = RequestEx.TryGetQueryString("timestamp");
- string nonce = RequestEx.TryGetQueryString("nonce");
- if (string.IsNullOrWhiteSpace(signature) || string.IsNullOrWhiteSpace(timestamp) || string.IsNullOrWhiteSpace(nonce))
- return false;
- return xrwang.weixin.PublicAccount.Utility.CheckSignature(signature, token, timestamp, nonce);
- }
/// <summary>
/// 验证消息的有效性
/// </summary>
/// <param name="context"></param>
/// <returns>如果消息有效,返回true;否则返回false。</returns>
private bool Validate(HttpContext context)
{
string username = RequestEx.TryGetQueryString("username"); //在接口配置的URL中加入了username参数,表示哪个微信公众号
AccountInfo account = AccountInfoCollection.GetAccountInfo(username);
if (account == null)
return false;
string token = account.Token;
string signature = RequestEx.TryGetQueryString("signature");
string timestamp = RequestEx.TryGetQueryString("timestamp");
string nonce = RequestEx.TryGetQueryString("nonce");
if (string.IsNullOrWhiteSpace(signature) || string.IsNullOrWhiteSpace(timestamp) || string.IsNullOrWhiteSpace(nonce))
return false;
return xrwang.weixin.PublicAccount.Utility.CheckSignature(signature, token, timestamp, nonce);
}
3 解析消息
如果消息签名通过验证,我们需要将XML格式的消息文本解析成请求消息对象,RequestMessageHelper类用于完成这项工作。
RequestMessageHelper helper = new RequestMessageHelper(context.Request);
if(helper.Message != null)
{
//消息解析成功,对它进行处理
}
消息解析成功之后,helper.Message为消息基类RequestBaseMessage,我们可以根据属性MsgType及Event判断到底是哪种消息(事件),并转换成适当的子类型。例如:
- RequestBaseMessage bm=helper.Message;
- switch(bm.MsgType)
- {
- case RequestMessageTypeEnum.text: //文本消息
- HandleTextMessage((RequestTextMessage)bm);
- break;
- case RequestMessageTypeEnum.image: //图片消息
- HandleImageMessage((RequestImageMessage)bm);
- break;
- //处理其他消息
- case RequestMessageTypeEnum.event: //事件
- RequestEventMessage ev=(RequestEventMessage)bm;
- switch(ev.Event)
- {
- case RequestEventTypeEnum.subscribe: //订阅
- HandleSubscribeMessage((RequestSubscribeMessage)ev);
- break;
- case RequestEventTypeEnum.unsubscribe: //取消订阅
- HandleUnsubscribeMessage((RequestUnsubscribeMessage)ev);
- break;
- //处理其他事件
- }
- break;
- default:
- break;
- }
RequestBaseMessage bm=helper.Message;
switch(bm.MsgType)
{
case RequestMessageTypeEnum.text: //文本消息
HandleTextMessage((RequestTextMessage)bm);
break;
case RequestMessageTypeEnum.image: //图片消息
HandleImageMessage((RequestImageMessage)bm);
break;
//处理其他消息
case RequestMessageTypeEnum.event: //事件
RequestEventMessage ev=(RequestEventMessage)bm;
switch(ev.Event)
{
case RequestEventTypeEnum.subscribe: //订阅
HandleSubscribeMessage((RequestSubscribeMessage)ev);
break;
case RequestEventTypeEnum.unsubscribe: //取消订阅
HandleUnsubscribeMessage((RequestUnsubscribeMessage)ev);
break;
//处理其他事件
}
break;
default:
break;
}
解析消息的细节请参看源代码:http://git.oschina.net/xrwang2/xrwang.weixin.PublicAccount/blob/master/PublicAccount/RequestMessage/RequestMessageHelper.cs
4 被动回复消息
从微信服务器接收到消息(事件)之后,我们可以在5秒之内直接(被动)回复消息;也可以先直接回复空字符串,然后再在48小时内回复客服消息。
先初始化ResponseXxxMessage,然后用ToXml方法得到响应消息内容。
被动回复消息的示例如下:
- /// <summary>
- /// 处理微信的POST请求
- /// </summary>
- /// <param name="context"></param>
- /// <returns>返回xml响应</returns>
- private string HandlePost(HttpContext context)
- {
- RequestMessageHelper helper = new RequestMessageHelper(context.Request);
- if (helper.Message != null)
- {
- ResponseBaseMessage responseMessage = HandleRequestMessage(helper.Message);
- return responseMessage.ToXml(helper.EncryptType);
- }
- else
- return string.Empty;
- }
- /// <summary>
- /// 处理请求消息,返回响应消息
- /// </summary>
- /// <returns>返回响应消息</returns>
- private ResponseBaseMessage HandleRequestMessage(RequestBaseMessage requestMessage)
- {
- ResponseTextMessage response = new ResponseTextMessage(requestMessage.FromUserName, requestMessage.ToUserName,
- DateTime.Now, string.Format("自动回复,请求内容如下:\r\n{0}", requestMessage));
- return response;
- }
/// <summary>
/// 处理微信的POST请求
/// </summary>
/// <param name="context"></param>
/// <returns>返回xml响应</returns>
private string HandlePost(HttpContext context)
{
RequestMessageHelper helper = new RequestMessageHelper(context.Request);
if (helper.Message != null)
{
ResponseBaseMessage responseMessage = HandleRequestMessage(helper.Message);
return responseMessage.ToXml(helper.EncryptType);
}
else
return string.Empty;
} /// <summary>
/// 处理请求消息,返回响应消息
/// </summary>
/// <returns>返回响应消息</returns>
private ResponseBaseMessage HandleRequestMessage(RequestBaseMessage requestMessage)
{
ResponseTextMessage response = new ResponseTextMessage(requestMessage.FromUserName, requestMessage.ToUserName,
DateTime.Now, string.Format("自动回复,请求内容如下:\r\n{0}", requestMessage));
return response;
}
5 发送其他消息
除了被动回复消息之外,我们还可以发送客服消息、群发消息、发送模板消息,这些内容将在后续文章中一一道来。
感谢您看完本文,希望对您有所帮助。