6、消息管理-普通消息接受处理
1、概述
通过前面章节的学习,我们已经对微信的开发有了基本的掌握与熟悉,基本可以上手做复杂的应用了。本篇我们将详细讲解微信消息管理中普通消息的接收与处理。当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。接收普通消息微信官方文档参考:接收普通消息文档API
消息接收后,就有一个处理或回复的过程,单单发送消息了没有响应也是不人性化的,下面我们就对接收到微信各类型消息分别讲解处理的方法。
2、消息接收
当普通微信用户向公众账号发消息时,微信服务器会先接收到用户发送的消息,然后将用户消息按照指定的XML格式组装好数据,最后POST消息的XML数据包到开发者填写的URL上。
接收到的普通消息的消息类型目前有以下几种:
- 文本消息
- 图片消息
- 语音消息
- 视频消息
- 小视频消息
- 地理位置消息
- 链接消息
每一种消息类型都有其指定的XML数据格式,这7种消息的xml格式请到官方文档查看,有具体的格式定义和属性说明。格式很简单,基本共有属性包括ToUserName、FromUserName、CreateTime、MsgType、MsgId,并且每种类型有自己特殊的属性。
接收消息的过程其实就是获取post请求的这个xml,然后对这个xml进行分析的过程。post请求的入口还是之前提到的微信公众号接入的那个地址,整个公众号的所有请求都会走这个入口,只是接入时是get请求,其它情况下是post请求。
3、消息回复
微信服务器在将用户的消息发给公众号的开发者服务器地址后,会等待开发者服务器回复响应消息。微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。
假如服务器无法保证在五秒内处理并回复,必须做出下述回复,这样微信服务器才不会对此作任何处理,并且不会发起重试(这种情况下,可以使用客服消息接口进行异步回复),否则,将出现严重的错误提示。详见下面说明:
1、(推荐方式)直接回复success
2、直接回复空串(指字节长度为0的空字符串,而不是XML结构体中content字段的内容为空)
一旦遇到以下情况,微信都会在公众号会话中,向用户下发系统提示“该公众号暂时无法提供服务,请稍后再试”:
1、开发者在5秒内未回复任何内容
2、开发者回复了异常数据,比如JSON数据等
另外,请注意,回复图片等多媒体消息时需要预先通过素材管理接口上传临时素材到微信服务器,可以使用素材管理中的临时素材,也可以使用永久素材。
消息回复目前支持回复文本、图片、图文、语音、视频、音乐,每一种类型的消息都有特定的XML数据格式。这几种回复消息的xml数据格式请参考官方文档,有具体的格式定义和属性说明。格式很简单,基本共有属性包括ToUserName、FromUserName、CreateTime、MsgType,并且每种类型有自己特殊的属性。
4、各类型消息的接收与回复
使用Senparc.Weixin框架来快速处理各种接收的普通消息,实现非常简单,自定义一个继承MessageHandler的类,重写这7种类型的方法即可。注意:DefaultResponseMessage必须重写,用于返回没有处理过的消息类型(也可以用于默认消息,如帮助信息等);其中所有原OnXX的抽象方法已经都改为虚方法,可以不必每个都重写。若不重写,默认返回DefaultResponseMessage方法中的结果。
CustomMessageHandle.cs需要继承Senparc.Weixin.MP.MessageHandlers这个抽象类,并实现部分方法。最初步的CustomMessageHandle.cs代码
可能如下:
public class CustomMessageHandler : MessageHandler<CustomMessageContext>
{
public CustomMessageHandler(Stream inputStream, PostModel postModel)
: base(inputStream, postModel)
{
}
public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)
{
var responseMessage = base.CreateResponseMessage<ResponseMessageText>(); //ResponseMessageText也可以是News等其他类型
responseMessage.Content = "这条消息来自DefaultResponseMessage。";
return responseMessage;
}
}
我们可以看到必须要重写实现的抽象方法名为DefaultResponseMessage(),这一条信息用于返回一条的消息,假如对应类型(如语音)的微信消息没有被代码处理,那么默认会返回这里的结果。在DefaultResponseMessage()方法中,我们看到这样一句:
var responseMessage = base.CreateResponseMessage<ResponseMessageText>(); //ResponseMessageText也可以是News等其他类型
这里的CreateResponseMessage方法即创建一个返回对象,T可以为以下类型的任意一个,分别对应了不同的返回类型:
- ResponseMessageText - 对应文本消息
- ResponseMessageNews - 对应图文消息
- ResponseMessageMusic - 对应音乐消息
- ResponseMessageXXX - 其他类型以此类推
各种类型的消息我们可以根据我们自己的业务要求进行重写回复,如果没重写就会返回默认的消息(重写默认响应的基础上)。
4.1、文本消息的接收与回复
文本消息对应的数据包XML格式如下:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
参数说明:
参数 描述
ToUserName 开发者微信号(直接把它当做你的公众号的微信号即可)
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,文本为text
Content 文本消息内容
MsgId 消息id,64位整型
处理文本消息参考代码:
/// <summary>
/// 处理文字请求
/// </summary>
/// <returns></returns>
public override IResponseMessageBase OnTextRequest(RequestMessageText requestMessage)
{
//注意:下面泛型ResponseMessageText即返回给客户端的类型,可以根据自己的需要填写ResponseMessageNews等不同类型。
var responseMessage = CreateResponseMessage<ResponseMessageText>();
var result = new StringBuilder();
result.AppendFormat("您刚发送了文本信息:{0}\r\n\r\n", requestMessage.Content);
if (CurrentMessageContext.RequestMessages.Count > 1)
{
result.AppendFormat("您刚还发送了如下消息({0}/{1}):\r\n", CurrentMessageContext.RequestMessages.Count, CurrentMessageContext.StorageData);
for (int i = CurrentMessageContext.RequestMessages.Count - 2; i >= 0; i--)
{
var historyMessage = CurrentMessageContext.RequestMessages[i];
result.AppendFormat("{0} 【{1}】{2}\r\n",
historyMessage.CreateTime.ToShortTimeString(),
historyMessage.MsgType.ToString(),
(historyMessage is RequestMessageText)
? (historyMessage as RequestMessageText).Content
: "[非文字类型]"
);
}
result.AppendLine("\r\n");
}
result.AppendFormat("如果您在{0}分钟内连续发送消息,记录将被自动保留(当前设置:最多记录{1}条)。过期后记录将会自动清除。\r\n", WeixinContext.ExpireMinutes, WeixinContext.MaxRecordCount);
result.AppendLine("\r\n");
result.AppendLine("您还可以发送【位置】【图片】【语音】【视频】等类型的信息(注意是这几种类型,不是这几个文字),查看不同格式的回复。");
responseMessage.Content = result.ToString();
return responseMessage;
}
4.2、图片消息的接收与回复
图片消息对应的数据包XML格式如下:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<PicUrl><![CDATA[this is a url]]></PicUrl>
<MediaId><![CDATA[media_id]]></MediaId>
<MsgId>1234567890123456</MsgId>
</xml>
参数说明:
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,图片为image
PicUrl 图片链接(由系统生成)
MediaId 图片消息媒体id,可以调用获取临时素材接口拉取数据。
MsgId 消息id,64位整型
处理图片消息参考代码:
/// <summary>
/// 处理图片请求
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnImageRequest(RequestMessageImage requestMessage)
{
var responseMessage = CreateResponseMessage<ResponseMessageNews>();
responseMessage.Articles.Add(new Article()
{
Title = "您刚才发送了图片信息",
Description = "您发送的图片将会显示在边上",
PicUrl = requestMessage.PicUrl,
Url = "http://blog.rdiframework.net/"
});
responseMessage.Articles.Add(new Article()
{
Title = "第二条",
Description = "第二条带连接的内容",
PicUrl = requestMessage.PicUrl,
Url = "http://blog.rdiframework.net/"
});
return responseMessage;
}
在上面代码中我们返回了用户发送的图片消息,同时加上了链接地址,用户单击消息会自动跳转到指定的URL地址。
4.3、语音消息的接收与回复
语音消息对应的数据包XML格式如下:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1357290913</CreateTime>
<MsgType><![CDATA[voice]]></MsgType>
<MediaId><![CDATA[media_id]]></MediaId>
<Format><![CDATA[Format]]></Format>
<MsgId>1234567890123456</MsgId>
</xml>
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 语音为voice
MediaId 语音消息媒体id,可以调用获取临时素材接口拉取数据。
Format 语音格式,如amr,speex等
MsgID 消息id,64位整型
参数说明:
处理语音消息参考代码:
/// <summary>
/// 处理语音请求
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnVoiceRequest(RequestMessageVoice requestMessage)
{
//获得当前公众号
WeixinOfficialAccountEntity account = RDIFrameworkService.Instance.WeixinBasicService.GetOfficialAccountEntity(Id);
var responseMessage = CreateResponseMessage<ResponseMessageMusic>();
//上传缩略图
var uploadResult = Senparc.Weixin.MP.AdvancedAPIs.MediaApi.UploadTemporaryMedia(account.AccessToken, UploadMediaFileType.image,Server.GetMapPath("~/Content/Images/weixing-ma.png"));
//设置音乐信息
responseMessage.Music.Title = "天籁之音";
responseMessage.Music.Description = "播放您上传的语音";
responseMessage.Music.MusicUrl = "http://www.rdiframework.net/resource/25375532.mp3";
responseMessage.Music.HQMusicUrl = "http://www.rdiframework.net/Media/GetVoice?mediaId=" + requestMessage.MediaId;
responseMessage.Music.ThumbMediaId = uploadResult.media_id;
return responseMessage;
}
4.4、视频消息的接收与回复
视频消息对应的数据包XML格式如下:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1357290913</CreateTime>
<MsgType><![CDATA[video]]></MsgType>
<MediaId><![CDATA[media_id]]></MediaId>
<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>
<MsgId>1234567890123456</MsgId>
</xml>
参数说明:
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 视频为video
MediaId 视频消息媒体id,可以调用获取临时素材接口拉取数据。
ThumbMediaId 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
MsgId 消息id,64位整型
处理视频消息参考代码:
/// <summary>
/// 处理视频请求
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnVideoRequest(RequestMessageVideo requestMessage)
{
var responseMessage = CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "您发送了一条视频信息,ID:" + requestMessage.MediaId;
return responseMessage;
}
4.5、小视频消息的接收与回复
视频与小视频主要区别是在MsgType上,其他的都一样。
小视频消息对应的数据包XML格式如下:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1357290913</CreateTime>
<MsgType><![CDATA[shortvideo]]></MsgType>
<MediaId><![CDATA[media_id]]></MediaId>
<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>
<MsgId>1234567890123456</MsgId>
</xml>
参数说明:
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 小视频为shortvideo
MediaId 视频消息媒体id,可以调用获取临时素材接口拉取数据。
ThumbMediaId 视频消息缩略图的媒体id,可以调用获取临时素材接口拉取数据。
MsgId 消息id,64位整型
处理小视频消息参考代码:
public override IResponseMessageBase OnShortVideoRequest(RequestMessageShortVideo requestMessage)
{
var responseMessage = this.CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "您刚才发送的是小视频";
return responseMessage;
}
4.6、地理位置消息的接收与回复
地理位置消息对应的数据包XML格式如下:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1351776360</CreateTime>
<MsgType><![CDATA[location]]></MsgType>
<Location_X>23.134521</Location_X>
<Location_Y>113.358803</Location_Y>
<Scale>20</Scale>
<Label><![CDATA[位置信息]]></Label>
<MsgId>1234567890123456</MsgId>
</xml>
参数说明:
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,地理位置为location
Location_X 地理位置维度
Location_Y 地理位置经度
Scale 地图缩放大小
Label 地理位置信息
MsgId 消息id,64位整型
处理地理位置消息参考代码:
/// <summary>
/// 处理位置请求
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnLocationRequest(RequestMessageLocation requestMessage)
{
//返回的是图文消息,是关于地址的图文消息。
var responseLocation = base.CreateResponseMessage<ResponseMessageNews>();
var markersList = new List<BaiduMarkers>();
markersList.Add(new BaiduMarkers()
{
Size = BaiduMarkerSize.m,
Color = "red",
Label = "A",
Latitude = requestMessage.Location_X,
Longitude = requestMessage.Location_Y,
});
var mapUrl = BaiduMapHelper.GetBaiduStaticMap(requestMessage.Location_Y, requestMessage.Location_X, 1, 13, markersList);
responseLocation.Articles.Add(new Article()
{
Description = string.Format("您刚才发送了地理位置信息。Location_X:{0},Location_Y:{1},Scale:{2},标签:{3}", requestMessage.Location_X, requestMessage.Location_Y, requestMessage.Scale, requestMessage.Label),
PicUrl = SystemInfo.WeChatSiteUrl +"/Content/Images/toplogo.png",
Title = "国思软件快速开发框架-地图返回",
Url = mapUrl
});
return responseLocation;
}
对于回复的消息,我们还可以单击弹出百度地图返回的位置详情,具体应用可据此扩展。
4.7、链接消息的接收与回复
链接消息对应的数据包XML格式如下:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1351776360</CreateTime>
<MsgType><![CDATA[link]]></MsgType>
<Title><![CDATA[公众平台官网链接]]></Title>
<Description><![CDATA[公众平台官网链接]]></Description>
<Url><![CDATA[url]]></Url>
<MsgId>1234567890123456</MsgId>
</xml>
参数说明:
参数 描述
ToUserName 接收方微信号
FromUserName 发送方微信号,若为普通用户,则是一个OpenID
CreateTime 消息创建时间
MsgType 消息类型,链接为link
Title 消息标题
Description 消息描述
Url 消息链接
MsgId 消息id,64位整型
链接位置消息参考代码:
/// <summary>
/// 处理链接消息请求
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnLinkRequest(RequestMessageLink requestMessage)
{
var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
responseMessage.Content = string.Format(@"您发送了一条连接信息:
Title:{0}
Description:{1}
Url:{2}", requestMessage.Title, requestMessage.Description, requestMessage.Url);
return responseMessage;
}