.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 ...
随机推荐
- java算法(二)
四.最小公倍数最大公约数问题: 分析:两个数的最小公倍数等于两个数相乘再除以他们的最大公约数,因此只要求出最大公约数就可以啦. package JingDian; public class yuebe ...
- [ZigBee] 15、Zigbee协议栈应用(一)——Zigbee协议栈介绍及简单例子(长文,OSAL及Zigbee入门知识)
1.Zigbee协议栈简介 协议是一系列的通信标准,通信双方需要按照这一标准进行正常的数据发射和接收.协议栈是协议的具体实现形式,通俗讲协议栈就是协议和用户之间的一个接口,开发人员通过使用协议栈来使用 ...
- IOS 其它语言比较-Objc与JAVA的比较
1. Objc是一门编译型语言,JAVA是解析型语言 编译型语言:把做好的源程序全部编译成二进制代码的可运行程序.然后,可直接运行这个程序. 编译型语言,执行速度快.效率高:依赖编译器.跨平台性差些. ...
- Redis数据库的使用场景介绍(避免误用Redis)
转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/122.html?1455854235 Redis 是目前 NoSQL 领域 ...
- PDO事务处理
PDO事务处理 2014-9-3 10:44:19 By jiancaigege==================================== 概要:将多条sql操作(增删改)作为一个操作单 ...
- display的理解
display可把框内显示的内容改变(自我理解) none 此元素不会被显示. block 此元素将显示为块级元素,此元素前后会带有换行符. inline 默认.此元素会被显示为内联元素,元素前后没有 ...
- Maven项目环境搭建实例.
前言:最近下班比较早, 总是不愿意让自己闲着, 此时刚好从网上找到了一些项目的资源, 结合自己在公司做的项目, 所以拿来一起学习加复习一些平常用到和没接触过的新知识.做的这个项目的名称叫做babasp ...
- Cocos2d-x 3.X手游开发实例详解
Cocos2d-x 3.X手游开发实例详解(最新最简Cocos2d-x手机游戏开发学习方法,以热门游戏2048.卡牌为例,完整再现手游的开发过程,实例丰富,代码完备,Cocos2d-x作者之一林顺和泰 ...
- rabbitmq消息队列——"工作队列"
二."工作队列" 在第一节中我们发送接收消息直接从队列中进行.这节中我们会创建一个工作队列来分发处理多个工作者中的耗时性任务. 工作队列主要是为了避免进行一些必须同步等待的资源密集 ...
- 手把手教你实现一个完整的 Promise
用过 Promise,但是总是有点似懂非懂的感觉,也看过很多文章,还是搞不懂 Promise的 实现原理,后面自己边看文章,边调试代码,终于慢慢的有感觉了,下面就按自己的理解来实现一个 Promise ...

