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

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

1、概述

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

我们在上一篇微信公众号开发C#系列-6、消息管理-普通消息接受处理中讲到,微信的消息可以大体分为两种类型,一种是包括:文本,语音,图片等的普通消息,另一种就是本篇要将的事件类型。包括:关注/取消关注事件,扫描带参数二维码事件,上报地理位置事件,自定义菜单相关事件等,本篇一一进行讲解。介于偏于内容过多易产生阅读疲劳,对于自定义菜单相关事件的处理我们放在下一篇中讲解。

这里的消息指的是传统的微信公众平台消息交互,微信用户向公众号发送消息后,公众号回复消息给微信用户。包括以下类型:

  1. 关注/取消关注事件:subscribe/unsubscribe
  2. 扫描带参数二维码事件:scan
  3. 上报地理位置事件:location
  4. 自定义菜单事件
  5. 点击菜单拉取消息时的事件推送
  6. 点击菜单跳转链接时的事件推送

本篇主要介绍前三种。

2、实现方式

使用Senparc.Weixin框架来快速处理各种接收事件推送,实现非常简单,自定义一个继承MessageHandler的类,重写这些类型的方法即可。注意:DefaultResponseMessage必须重写,用于返回没有处理过的消息类型(也可以用于默认消息,如帮助信息等);其中所有原OnXX的抽象方法已经都改为虚方法,可以不必每个都重写。若不重写,默认返回DefaultResponseMessage方法中的结果。

自定义消息处理类:

public partial class CustomMessageHandler : MessageHandler<MessageContext<IRequestMessageBase, IResponseMessageBase>>
{
public CustomMessageHandler(Stream inputStream, int maxRecordCount = 0)
: base(inputStream, null, maxRecordCount)
{
WeixinContext.ExpireMinutes = 3;
}
public override void OnExecuting()
{
//测试MessageContext.StorageData
if (CurrentMessageContext.StorageData == null)
{
CurrentMessageContext.StorageData = 0;
}
base.OnExecuting();
}
public override void OnExecuted()
{
base.OnExecuted();
CurrentMessageContext.StorageData = ((int)CurrentMessageContext.StorageData) + 1;
}
}

定义好事件处理类后,分别重写上面提到几种接收事件推送的事件即可。

我们可以通过重写MessageHandler里的这几种类型方法来处理我们的业务,当然也可以只重写需要的部分类型,不需要的类型可以不重写,只需要定义一个统一的DefaultResponseMessage

public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)
{
//所有没有被处理的消息会默认返回这里的结果
var responseMessage = this.CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "这条消息来自DefaultResponseMessage。";
return responseMessage;
}

3、消息的去重的重要性

上一篇我们就已经提到过微信服务器在5秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。如此以来,我们模拟有这样一个场景:当用户关注微信账号时,获取当前用户信息,然后将信息写到数据库中,类似网站的注册。假设这个关注事件中,我们需要处理比较复杂的业务逻辑。如送积分,写用户日志,分配用户组等等一系列的逻辑需要执行,或者网络环境比较复杂,无法保证5秒内响应当前用户的操作,那如果当操作尚未完成,微信服务器又给我们的服务器推送了一条相同的关注事件,我们将再次执行我们的那些逻辑,这样就有可能导致数据库中出现重复的数据(有的童鞋就会说了,我在插入数据之前先判断当前是否已经存在了,如果存在了就不执行插入的操作。我想说的是,我当初也是这样想的,但真实的运行环境和我们的调试环境还是有差距的,直到发现数据库中有不少重复的用户信息时,我才发现消息去重的重要性。)。

消息的去重普通消息和事件消息是有区别的。普通消息使用msgid,而事件消息使用FromUserName + CreateTime。

4、关注/取消关注事件

用户在关注与取消关注公众号时,微信会把这个事件推送到开发者填写的URL。方便开发者给用户下发欢迎消息或者做帐号的解绑。

假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。

关注或取消事件推送XML数据包示例:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
</xml>

参数说明:

参数				描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,event
Event 事件类型,subscribe(订阅)、unsubscribe(取消订阅)

4.1 关注事件

关注事件我们只需要重写OnEvent_SubscribeRequest事件代码即可,如下我们返回了一个文本消息,实现代码参考:

/// <summary>
/// 订阅(关注)事件
/// </summary>
/// <returns></returns>
public override IResponseMessageBase OnEvent_SubscribeRequest(RequestMessageEvent_Subscribe requestMessage)
{
var responseMessage = CreateResponseMessage<ResponseMessageNews>();
foreach (var model in messageList)
{
responseMessage.Articles.Add(new Article()
{
Title = "国思公众号",
Description = "欢迎关注国思软件公众号,更多内容稳步官网,多谢!",
PicUrl = "http://www.rdiframework.net/WeiXin.png",
Url = "http://www.rdiframework.net/"
});
}
return responseMessage;
}

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

在上面的关注事件中,用户关注公众号就会自动执行上面的事件代码,我们就可以在事件代码中做相关的业务处理,如绑定用户分组、增加用户到本地等等。同时推送一条欢迎消息返回到用户手机上。

4.2 取消关注事件

取消关注事件与关注事件类似,主要是事件变成了unsubscribe(取消关注)。取消关注事件-unsubscribe的主要意义在于及时删除网站应用中已经记录的OpenID绑定,消除冗余数据,并且关注用户流失的情况。

取消关注事件我们只需要重写OnEvent_UnsubscribeRequest事件代码即可,如下我们返回了一个文本消息,实现代码参考:

/// <summary>
/// 退订/取消关注
/// 实际上用户无法收到非订阅账号的消息,所以这里可以随便写。
/// unsubscribe事件的意义在于及时删除网站应用中已经记录的OpenID绑定,消除冗余数据。并且关注用户流失的情况。
/// </summary>
/// <returns></returns>
public override IResponseMessageBase OnEvent_UnsubscribeRequest(RequestMessageEvent_Unsubscribe requestMessage)
{
int returnValue = RDIFrameworkService.Instance.WeixinBasicService.UserUnsubscribeByOpenId(Id,requestMessage.FromUserName);//退
var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "有空再来";
return responseMessage;
}

上面的代码在用户取消公众号的关注时就会自动执行,可以看到我们有一行代码针对用户取消关注时执行的业务逻辑,同时返回了一个文本消息。实际用户已经取消关注,返回的消息也返回不到用户手机上的。

5、扫描带参数二维码事件

用户扫描带场景值二维码时,可能推送以下两种事件:

  1. 如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。
  2. 如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。

5.1 接口展示与实现方式

对于第一种上面已经讲了,这里就只说明下第二种。

推送XML数据包示例:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[SCAN]]></Event>
<EventKey><![CDATA[SCENE_VALUE]]></EventKey>
<Ticket><![CDATA[TICKET]]></Ticket>
</xml>

参数说明:

参数				描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,event
Event 事件类型,SCAN
EventKey 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
Ticket 二维码的ticket,可用来换取二维码图片

对于生成带参数的二维码我们会在后面的文章中专门介绍,这儿我们了解一个这个概念。为了满足用户渠道推广分析和用户帐号绑定等场景的需要,公众平台提供了生成带参数二维码的接口。使用该接口可以获得多个带不同场景值的二维码,用户扫描后,公众号可以接收到事件推送。具体官方技术文档可参考:生成带参数的二维码

目前有2种类型的二维码:

1、临时二维码,是有过期时间的,最长可以设置为在二维码生成后的30天(即2592000秒)后过期,但能够生成较多数量。临时二维码主要用于帐号绑定等不要求二维码永久保存的业务场景

2、永久二维码,是无过期时间的,但数量较少(目前为最多10万个)。永久二维码主要用于适用于帐号绑定、用户来源统计等场景。

扫描带参数二维码事件只需要重写OnEvent_ScanRequest事件代码即可,如下我们返回了一个文本消息,实现代码参考:

public override IResponseMessageBase OnEvent_ScanRequest(RequestMessageEvent_Scan requestMessage)
{
//通过扫描关注
var responseMessage = CreateResponseMessage<ResponseMessageText>(); responseMessage.Content = responseMessage.Content ?? string.Format("欢迎关注国思软件,通过扫描二维码进入,场景值:{0}", requestMessage.EventKey); return responseMessage;
}

在上面的代码中用户扫描了带场景值的二维码进入公众号后我们返回了一个提示的文本消息。这是非常有用的功能,常用途推广,可以根据不同的二维码场景值分别做不同的业务处理,如可以统计关注的每一个粉丝从哪里来的,做到渠道推广分析,但是关注的都是同一个公众号。

5.2 生成带参数的二维码用途

微信公众号生成带参数的二维码有何用途?

  1. 可以区分粉丝来源,只需要生成不同的带参数的二维码,把这些二维码分别投放到各个渠道,粉丝通过这些渠道二维码进来就可以区分粉丝来源,微号帮后台渠道粉丝列表中有粉丝数及明细;
  2. 粉丝通过扫描渠道二维码关注公众号,会打标签分组,比如粉丝扫商店A、B的二维码进来的, 在微信公众号后来的用户管理中可查看到商店A/B二维码名下的粉丝明细及分组情况;
  3. 可以生成多个不同的渠道二维码配置不同的营销活动,设置不同的关注回复信息,让粉丝第一时间了解活动动机,是否有兴趣参与等等;
  4. 可以利用渠道二维码生成功能,可以实现微信收款前关注公众号,间接分析粉丝后续消费情况;
  5. 考核推广员完成任务的进度,如以推广名字生成多不个同的二维码,分配给不同的推广员,每个推广员吸引了多少粉丝关注公众号,微号帮后台都可以一一明细;
  6. 带参数的二维码也叫渠道二维码或者场景二维码,生存的数量有限,且是永久二维码。当数量用完后可以删除一些不用的二维码释放出来,二次利用。

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

6、上报地理位置事件

用户同意上报地理位置后,每次进入公众号会话时,都会在进入时上报地理位置,或在进入会话后每5秒上报一次地理位置,公众号可以在公众平台网站中修改以上设置。上报地理位置时,微信会将上报地理位置事件推送到开发者填写的URL。要获取用户地址位置,需要在微信公众平台开发者中心开启上报地理位置功能,开启之后会在用户首次进入公众号时,弹出是否允许上报地理位置选项,如果选择允许则在用户每次进入公众号会话的时候微信会以XML形式将用户的地理位置上报到你开发者中心填写的URL上。

注意:用户地理位置是被动获取的,需用户同意后才会上报,微信公众平台开发不能主动获取用户地理位置。

推送XML数据包示例:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[LOCATION]]></Event>
<Latitude>23.137466</Latitude>
<Longitude>113.352425</Longitude>
<Precision>119.385040</Precision>
</xml>

参数说明:

参数				描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,event
Event 事件类型,LOCATION
Latitude 地理位置纬度
Longitude 地理位置经度
Precision 地理位置精度

上报地理位置事件只需要重写OnEvent_LocationRequest事件代码即可,如下我们返回了一个文本消息,实现代码参考:

public override IResponseMessageBase OnEvent_LocationRequest(RequestMessageEvent_Location requestMessage)
{
//这里是微信客户端(通过微信服务器)自动发送过来的位置信息
var responseMessage = CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "这里写什么都无所谓,比如:上帝爱你!";
return responseMessage;//这里也可以返回null(需要注意写日志时候null的问题)
}

上报地理位置用处非常多,可以用维度和经度获取城市代号,调用天气Api,也可以用来监测企业员工的位置进行微信考勤。在微信运营的时候,用户地理位置还是我们进行营销策划、广告活动投放、用户精准营销的重要依据。

参考文章

微信公众平台技术文档-官方

Senparc.Weixin SDK + 官网示例源代码

RDIFramework.NET — 基于.NET的快速信息化系统开发框架 — 系列目录

RDIFramework.NET ━ .NET快速信息化系统开发框架 ━ 工作流程组件介绍

RDIFramework.NET框架SOA解决方案(集Windows服务、WinForm形式与IIS形式发布)-分布式应用

RDIFramework.NET代码生成器全新V3.5版本发布-重大升级


一路走来数个年头,感谢RDIFramework.NET框架的支持者与使用者,大家可以通过下面的地址了解详情。

RDIFramework.NET官方网站:http://www.rdiframework.net/

RDIFramework.NET官方博客:http://blog.rdiframework.net/

同时需要说明的,以后的所有技术文章以官方网站为准,欢迎大家收藏!

RDIFramework.NET框架由专业团队长期打造、一直在更新、一直在升级,请放心使用!

欢迎关注RDIFramework.net框架官方公众微信(微信号:guosisoft),及时了解最新动态。

扫描二维码立即关注

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

上一篇:C#中"?"(问号)相关语法糖


下一篇:Log4J日志管理类使用详解 (转)