作者:王先荣
    本文介绍如何处理微信公众号开发中的消息与事件,包括:(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方法用于验证消息签名是否正确。

  1. /// <summary>
  2. /// 验证消息的有效性
  3. /// </summary>
  4. /// <param name="context"></param>
  5. /// <returns>如果消息有效,返回true;否则返回false。</returns>
  6. private bool Validate(HttpContext context)
  7. {
  8. string username = RequestEx.TryGetQueryString("username");  //在接口配置的URL中加入了username参数,表示哪个微信公众号
  9. AccountInfo account = AccountInfoCollection.GetAccountInfo(username);
  10. if (account == null)
  11. return false;
  12. string token = account.Token;
  13. string signature = RequestEx.TryGetQueryString("signature");
  14. string timestamp = RequestEx.TryGetQueryString("timestamp");
  15. string nonce = RequestEx.TryGetQueryString("nonce");
  16. if (string.IsNullOrWhiteSpace(signature) || string.IsNullOrWhiteSpace(timestamp) || string.IsNullOrWhiteSpace(nonce))
  17. return false;
  18. return xrwang.weixin.PublicAccount.Utility.CheckSignature(signature, token, timestamp, nonce);
  19. }
    /// <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判断到底是哪种消息(事件),并转换成适当的子类型。例如:

  1. RequestBaseMessage bm=helper.Message;
  2. switch(bm.MsgType)
  3. {
  4. case RequestMessageTypeEnum.text:  //文本消息
  5. HandleTextMessage((RequestTextMessage)bm);
  6. break;
  7. case RequestMessageTypeEnum.image: //图片消息
  8. HandleImageMessage((RequestImageMessage)bm);
  9. break;
  10. //处理其他消息
  11. case RequestMessageTypeEnum.event:  //事件
  12. RequestEventMessage ev=(RequestEventMessage)bm;
  13. switch(ev.Event)
  14. {
  15. case RequestEventTypeEnum.subscribe:    //订阅
  16. HandleSubscribeMessage((RequestSubscribeMessage)ev);
  17. break;
  18. case RequestEventTypeEnum.unsubscribe:  //取消订阅
  19. HandleUnsubscribeMessage((RequestUnsubscribeMessage)ev);
  20. break;
  21. //处理其他事件
  22. }
  23. break;
  24. default:
  25. break;
  26. }
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方法得到响应消息内容。
    被动回复消息的示例如下:

  1. /// <summary>
  2. /// 处理微信的POST请求
  3. /// </summary>
  4. /// <param name="context"></param>
  5. /// <returns>返回xml响应</returns>
  6. private string HandlePost(HttpContext context)
  7. {
  8. RequestMessageHelper helper = new RequestMessageHelper(context.Request);
  9. if (helper.Message != null)
  10. {
  11. ResponseBaseMessage responseMessage = HandleRequestMessage(helper.Message);
  12. return responseMessage.ToXml(helper.EncryptType);
  13. }
  14. else
  15. return string.Empty;
  16. }
  17. /// <summary>
  18. /// 处理请求消息,返回响应消息
  19. /// </summary>
  20. /// <returns>返回响应消息</returns>
  21. private ResponseBaseMessage HandleRequestMessage(RequestBaseMessage requestMessage)
  22. {
  23. ResponseTextMessage response = new ResponseTextMessage(requestMessage.FromUserName, requestMessage.ToUserName,
  24. DateTime.Now, string.Format("自动回复,请求内容如下:\r\n{0}", requestMessage));
  25. return response;
  26. }
    /// <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微信公众号开发——消息与事件的更多相关文章

  1. 转:C#微信公众号开发之接收事件推送与消息排重的方法

    本文实例讲述了C#微信公众号开发之接收事件推送与消息排重的方法.分享给大家供大家参考.具体分析如下: 微信服务器在5秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次.这样的话,问题就来了.有这 ...

  2. PHP 微信公众号开发 - 消息推送

    项目微信公众号开发,需要做用户消息推送,记录下来以便日后使用 1,接上一篇文章,可以查看如何获取用户openid PHP 微信公众号开发 - 获取用户信息 2,添加模板消息 3,查看模板详情 根据模板 ...

  3. C#微信公众号开发系列教程五(接收事件推送与消息排重)

    微信公众号开发系列教程一(调试环境部署) 微信公众号开发系列教程一(调试环境部署续:vs远程调试) C#微信公众号开发系列教程二(新手接入指南) C#微信公众号开发系列教程三(消息体签名及加解密) C ...

  4. 微信公众号开发C#系列-7、消息管理-接收事件推送

    1.概述 在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址,从而开发者可以获取到该信息.其中,某些事件推送在发生后,是允许 ...

  5. C#微信公众号开发系列教程三(消息体签名及加解密)

    http://www.cnblogs.com/zskbll/p/4139039.html C#微信公众号开发系列教程一(调试环境部署) C#微信公众号开发系列教程一(调试环境部署续:vs远程调试) C ...

  6. C#微信公众号开发系列教程四(接收普通消息)

    微信公众号开发系列教程一(调试环境部署) 微信公众号开发系列教程一(调试环境部署续:vs远程调试) C#微信公众号开发系列教程二(新手接入指南) C#微信公众号开发系列教程三(消息体签名及加解密) C ...

  7. .net微信公众号开发——群发消息

    作者:王先荣    本文将介绍微信公众号开发中用于群发消息的类MassMessage,包括:(1)MassMessage类:(2)群发:(3)删除:(4)预览:(5)查询发送状态:(6)接收推送群发结 ...

  8. C#微信公众号开发 -- (六)自定义菜单事件之CLICK

    微信公众号中当用户手动点击了按钮,微信公众号会被动的向用户发送文字消息或者图文消息. 通过C#微信公众号开发 -- (五)自定义菜单创建 我们知道了如何将CLICK类型的按钮添加到自己的微信公众平台上 ...

  9. 线程安全使用(四) [.NET] 简单接入微信公众号开发:实现自动回复 [C#]C#中字符串的操作 自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化 自已动手做高性能消息队列 自行实现高性能MVC WebAPI 面试题随笔 字符串反转

    线程安全使用(四)   这是时隔多年第四篇,主要是因为身在东软受内网限制,好多文章就只好发到东软内部网站,懒的发到外面,现在一点点把在东软写的文章给转移出来. 这里主要讲解下CancellationT ...

随机推荐

  1. Android多线程分析之一:使用Thread异步下载图像

    Android多线程分析之一:使用Thread异步下载图像 罗朝辉 (http://www.cnblogs.com/kesalin) CC 许可,转载请注明出处   打算整理一下对 Android F ...

  2. C语言再学习之内存对齐

    昨天看Q3的代码,看到有个_INTSAIZEOF的宏,着实晕了一阵.一番google后,终于明白,这个宏的作用是求出变量占用内存空间的大小,先看看_INTSAIZEOF的定义吧: #define _I ...

  3. 这里有个坑---js日期格式yyyy-MM-dd与yyyy/MM/dd

    这里有个坑,---------每一个遇到的坑总结后都是一比财富. 我们写脚本的时候,一般定义一个日期格式会使用“2015-12-21”和“2015/12/21”两种数据格式,由于各取所需日期格式并没有 ...

  4. ios 滚动视图响应touchesBegin,touchesEnd等方法

    能够滚动的控件都不会响应touchesBegin,touchesEnd等方法,这就需要对这个类进行封装 以UITextView为例 1,创建CustomTextView类,继承与UITextView ...

  5. table下属标签与标签中间不能加其他任何标签

    今天设计网页,很想在table下面的caption下面加hr标签,不能正常解析,后来发现hr标签可以显示,但是用浏览器查看解析,发现hr在table上面进行了解析,不会再caption下面显示, 又连 ...

  6. JS图片懒加载

    简介 当页面图片太多时,加载速度就会很慢.尤其是用2G/3G/4G访问页面,不仅页面慢,而且还会用掉很多流量.图片懒加载的原理就是将页面内所有需要加载的图片全部换成一张默认的图片(一般尺寸很小),只有 ...

  7. Atitit 图片 验证码生成attilax总结

    Atitit 图片 验证码生成attilax总结 1.1. 图片验证码总结1 1.2. 镂空文字  打散 干扰线 文字扭曲 粘连2 1.1. 图片验证码总结 因此,CAPTCHA在图片验证码这一应用点 ...

  8. Android笔记——Android五大布局

    一.五大布局 Android的界面是有布局和组件协同完成的,布局好比是建筑里的框架,而组件则相当于建筑里的砖瓦.组件按照布局的要求依次排列,就组成了用户所看见的界面.Android的五大布局分别是Li ...

  9. 阿里云上安装mysql步骤/ 阿里云ECS搭建Java+mysql+tomcat环境

    使用阿里云ECS挺长一段时间了.这两天碰巧朋友小白让我一步一步教他在ECS上搭建Java+mysql+tomcat环境,所以把在这里把步骤在这简单整理了一下,以便需要的人查阅. 我购买的阿里云服务器系 ...

  10. 深入入门系列--Data Structure--04树

    终于有机会重新回头学习一下一直困扰自身多年的数据结构了,赶脚棒棒哒.一直以来,对数据结构的掌握基本局限于线性表,稍微对树有一丢丢了解,而对于图那基本上就是不懂(不可否认,很多的考试中回避了图也是原因之 ...