.net微信公众号开发——消息与事件
作者:王先荣
本文介绍如何处理微信公众号开发中的消息与事件,包括:(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 发送其他消息
除了被动回复消息之外,我们还可以发送客服消息、群发消息、发送模板消息,这些内容将在后续文章中一一道来。
感谢您看完本文,希望对您有所帮助。
.net微信公众号开发——消息与事件的更多相关文章
- 转:C#微信公众号开发之接收事件推送与消息排重的方法
本文实例讲述了C#微信公众号开发之接收事件推送与消息排重的方法.分享给大家供大家参考.具体分析如下: 微信服务器在5秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次.这样的话,问题就来了.有这 ...
- PHP 微信公众号开发 - 消息推送
项目微信公众号开发,需要做用户消息推送,记录下来以便日后使用 1,接上一篇文章,可以查看如何获取用户openid PHP 微信公众号开发 - 获取用户信息 2,添加模板消息 3,查看模板详情 根据模板 ...
- C#微信公众号开发系列教程五(接收事件推送与消息排重)
微信公众号开发系列教程一(调试环境部署) 微信公众号开发系列教程一(调试环境部署续:vs远程调试) C#微信公众号开发系列教程二(新手接入指南) C#微信公众号开发系列教程三(消息体签名及加解密) C ...
- 微信公众号开发C#系列-7、消息管理-接收事件推送
1.概述 在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址,从而开发者可以获取到该信息.其中,某些事件推送在发生后,是允许 ...
- C#微信公众号开发系列教程三(消息体签名及加解密)
http://www.cnblogs.com/zskbll/p/4139039.html C#微信公众号开发系列教程一(调试环境部署) C#微信公众号开发系列教程一(调试环境部署续:vs远程调试) C ...
- C#微信公众号开发系列教程四(接收普通消息)
微信公众号开发系列教程一(调试环境部署) 微信公众号开发系列教程一(调试环境部署续:vs远程调试) C#微信公众号开发系列教程二(新手接入指南) C#微信公众号开发系列教程三(消息体签名及加解密) C ...
- .net微信公众号开发——群发消息
作者:王先荣 本文将介绍微信公众号开发中用于群发消息的类MassMessage,包括:(1)MassMessage类:(2)群发:(3)删除:(4)预览:(5)查询发送状态:(6)接收推送群发结 ...
- C#微信公众号开发 -- (六)自定义菜单事件之CLICK
微信公众号中当用户手动点击了按钮,微信公众号会被动的向用户发送文字消息或者图文消息. 通过C#微信公众号开发 -- (五)自定义菜单创建 我们知道了如何将CLICK类型的按钮添加到自己的微信公众平台上 ...
- 线程安全使用(四) [.NET] 简单接入微信公众号开发:实现自动回复 [C#]C#中字符串的操作 自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化 自已动手做高性能消息队列 自行实现高性能MVC WebAPI 面试题随笔 字符串反转
线程安全使用(四) 这是时隔多年第四篇,主要是因为身在东软受内网限制,好多文章就只好发到东软内部网站,懒的发到外面,现在一点点把在东软写的文章给转移出来. 这里主要讲解下CancellationT ...
随机推荐
- ASP.NET MVC随想录——创建自定义的Middleware中间件
经过前2篇文章的介绍,相信大家已经对OWIN和Katana有了基本的了解,那么这篇文章我将继续OWIN和Katana之旅——创建自定义的Middleware中间件. 何为Middleware中间件 M ...
- Stealth视频教程学习笔记(第二章)
Stealth视频教程学习笔记(第二章) 本文是对Unity官方视频教程Stealth的学习笔记.在此之前,本人整理了Stealth视频的英文字幕,并放到了优酷上.本文将分别对各个视频进行学习总结,提 ...
- 12小时包你学会基于ReactMix框架的ReactNativeApp开发(一)Hello World!
ReactMixhttps://github.com/xueduany/react-mix自从昨天发布起来,得到了不少小伙伴的热捧,很高兴帮助大家解决了憋在心中很久的问题“如果我只会HTML,Css, ...
- Vue 子组件向父组件传参
直接上代码 <body> <div id="counter-event-example"> <p>{{ total }}</p> & ...
- Andrew Ng机器学习公开课笔记 -- Regularization and Model Selection
网易公开课,第10,11课 notes,http://cs229.stanford.edu/notes/cs229-notes5.pdf Model Selection 首先需要解决的问题是,模型 ...
- 免费在线loading生成。
loading这个在项目中也是经常要使用,这里推荐一个网站http://www.ajaxload.info/可以在线生成loading. 进来页面是这样的. 勾选transparent将会生成透明的g ...
- 利用JSDOC快速生成注释文档,非常棒。
有时往往我们需要建一个文档来记录js中的一些代码注释,比如一些公共的函数,又或者一些类,在团队合作中,文档接口也是必不可少的,传统的方式多少有些不便,这里介绍一个工具,它叫JSDOC,它可以用来将注释 ...
- Oracle 11g系列:视图
视图是数据库中特有的对象,视图用于存储查询,但不会存储数据(物化视图除外).这是视图和数据表的重要区别.Oracle中有4种视图:关系视图.内嵌视图.对象视图和物化视图. 1.关系视图 1>.创 ...
- windows phone listbox的点击事件
前台 <ListBox x:Name="> <ListBox.ItemTemplate> <DataTemplate> <Grid Margin=& ...
- 深入理解PHP内核(十三)类的结构和实现
原文链接:http://www.orlion.ga/1117/ 先看一下类的结构: struct _zend_class_entry { char type; // 类型:ZEND_ ...

