微信开发之接收文本消息
微信中的消息类型有:文本,图片,语音,视频,地理位置,链接和事件消息。除了事件消息外,其他的统称为普通消息。微信中消息的推送与响应都是以xml数据包传输的。在用户发送消息给公众号时,微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。普通消息可以使用msgid排重,以避免重复的消息对业务逻辑的影响。
假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此座任何处理,并且不会发起重试。需要注意的是:这里说的回复空串并不是回复空的文本消息,而是直接Response.Write(“”)即可。
下面简要对各普通消息说明一下。文本消息:<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>图片消息:<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>语音消息:<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>视频消息:<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>地理位置消息:<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>链接消息:<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发送方微信号,若为普通用户,则是一个OpenIDCreateTime消息创建时间MsgType消息类型而消息的类型在文章开头已经讲了,分别是:文本(text),图片(image),语音(voice),视频(video),地理位置(location),链接(link),事件(event)
为了方便管理和代码编写,我们可以把这些消息类型写一个枚举。如下:/// <summary>/// 消息类型枚举/// </summary>public enum MsgType{/// <summary>///文本类型/// </summary>TEXT,/// <summary>/// 图片类型/// </summary>IMAGE,/// <summary>/// 语音类型/// </summary>VOICE,/// <summary>/// 视频类型/// </summary>VIDEO,/// <summary>/// 地理位置类型/// </summary>LOCATION,/// <summary>/// 链接类型/// </summary>LINK,/// <summary>/// 事件类型/// </summary>EVENT}这里说明下,C#中event是关键字,所以event在枚举中就不能使用了,所以为了统一,我这里的枚举全部使用大写的。
既然所有的消息体都有上面的几个字段,那就可以写一个基类,然后不同的消息实体继承这个基类。(一直在纠结一个问题,以前我都是将所有的消息体中的字段写在一个类中,调用起来也很方便,只是类中的字段越来越多,看着都不爽。再加上本人才疏学浅,面向对象也使用的不熟练,所以一直都是在一个类中罗列所有的字段
调用的时候直接var ss = WeiXinRequest.RequestHelper(token, EncodingAESKey, appid);
返回一个WeiXinRequest,然后再对消息类型和事件类型判断,做出响应。
今天重新做了下调整,也就是分了子类基类,代码可读性提高了,调用起来却没有之前方便了,各位朋友给点建议呗。
)
下面是各消息实体
基类:public abstract class BaseMessage{/// <summary>/// 开发者微信号/// </summary>public string ToUserName { get; set; }/// <summary>/// 发送方帐号(一个OpenID)/// </summary>public string FromUserName { get; set; }/// <summary>/// 消息创建时间 (整型)/// </summary>public string CreateTime { get; set; }/// <summary>/// 消息类型/// </summary>public MsgType MsgType { get; set; }public virtual void ResponseNull(){Utils.ResponseWrite("");}public virtual void ResText(EnterParam param, string content){}/// <summary>/// 回复消息(音乐)/// </summary>publicvoid ResMusic(EnterParam param, Music mu){}publicvoid ResVideo(EnterParam param, Video v){}/// <summary>/// 回复消息(图片)/// </summary>publicvoid ResPicture(EnterParam param, Picture pic, string domain){}/// <summary>/// 回复消息(图文列表)/// </summary>/// <param name="param"></param>/// <param name="art"></param>publicvoid ResArticles(EnterParam param, List<Articles> art){}/// <summary>/// 多客服转发/// </summary>/// <param name="param"></param>publicvoid ResDKF(EnterParam param){}/// <summary>/// 多客服转发如果指定的客服没有接入能力(不在线、没有开启自动接入或者自动接入已满),该用户会一直等待指定客服有接入能力后才会被接入,而不会被其他客服接待。建议在指定客服时,先查询客服的接入能力指定到有能力接入的客服,保证客户能够及时得到服务。/// </summary>/// <param name="param">用户发送的消息体</param>/// <param name="KfAccount">多客服账号</param>publicvoid ResDKF(EnterParam param, string KfAccount){}privatevoid Response(EnterParam param, string data){}}基类中定义了消息体的公共字段,以及用于响应用户请求的虚方法(响应消息不是本文重点,所以方法体就没有贴出来,请关注后续文章)。
基类中方法的参数有个是EnterParam类型的,这个类是用户接入时和验证消息真实性需要使用的参数,包括token,加密密钥,appid等。定义如下:/// <summary>/// 微信接入参数/// </summary>public class EnterParam{/// <summary>/// 是否加密/// </summary>public bool IsAes { get; set; }/// <summary>/// 接入token/// </summary>public string token { get; set; }/// <summary>///微信appid/// </summary>public string appid { get; set; }/// <summary>/// 加密密钥/// </summary>public string EncodingAESKey { get; set; }}文本实体:public class TextMessage:BaseMessage{/// <summary>/// 消息内容/// </summary>public string Content { get; set; }/// <summary>/// 消息id,64位整型/// </summary>public string MsgId { get; set; }}图片实体:public class ImgMessage : BaseMessage{/// <summary>/// 图片路径/// </summary>public string PicUrl { get; set; }/// <summary>/// 消息id,64位整型/// </summary>public string MsgId { get; set; }/// <summary>/// 媒体ID/// </summary>public string MediaId { get; set; }}语音实体:public class VoiceMessage : BaseMessage{/// <summary>/// 缩略图ID/// </summary>public string MsgId { get; set; }/// <summary>/// 格式/// </summary>public string Format { get; set; }/// <summary>/// 媒体ID/// </summary>public string MediaId { get; set; }/// <summary>/// 语音识别结果/// </summary>public string Recognition { get; set; }}视频实体:public class VideoMessage : BaseMessage{/// <summary>/// 缩略图ID/// </summary>public string ThumbMediaId { get; set; }/// <summary>/// 消息id,64位整型/// </summary>public string MsgId { get; set; }/// <summary>/// 媒体ID/// </summary>public string MediaId { get; set; }}链接实体:public class LinkMessage : BaseMessage{/// <summary>/// 缩略图ID/// </summary>public string MsgId { get; set; }/// <summary>/// 标题/// </summary>public string Title { get; set; }/// <summary>/// 描述/// </summary>public string Description { get; set; }/// <summary>/// 链接地址/// </summary>public string Url { get; set; }}消息实体定义好了,下一步就是根据微信服务器推送的消息体解析成对应的实体。本打算用C#自带的xml序列化发序列化的组件,结果试了下总是报什么xmls的错,索性用反射写了个处理方法:public static T ConvertObj<T>(string xmlstr){XElement xdoc = XElement.Parse(xmlstr);var type = typeof(T);var t = Activator.CreateInstance<T>();foreach (XElement element in xdoc.Elements()){var pr = type.GetProperty(element.Name.ToString());if (element.HasElements){//这里主要是兼容微信新添加的菜单类型。nnd,竟然有子属性,所以这里就做了个子属性的处理foreach (var ele in element.Elements()){pr = type.GetProperty(ele.Name.ToString());pr.SetValue(t, Convert.ChangeType(ele.Value, pr.PropertyType), null);}continue;}if (pr.PropertyType.Name == "MsgType")//获取消息模型{pr.SetValue(t, (MsgType)Enum.Parse(typeof(MsgType), element.Value.ToUpper()), null);continue;}if (pr.PropertyType.Name == "Event")//获取事件类型。{pr.SetValue(t, (Event)Enum.Parse(typeof(Event), element.Value.ToUpper()), null);continue;}pr.SetValue(t, Convert.ChangeType(element.Value, pr.PropertyType), null);}return t;}处理xml的方法定义好后,下面就是讲根据不同的消息类型来解析对应的实体了:public class MessageFactory{public static BaseMessage CreateMessage(string xml){XElement xdoc = XElement.Parse(xml);var msgtype = xdoc.Element("MsgType").Value.ToUpper();MsgType type = (MsgType)Enum.Parse(typeof(MsgType), msgtype);switch (type){case MsgType.TEXT: return Utils.ConvertObj<TextMessage>(xml);case MsgType.IMAGE: return Utils.ConvertObj<ImgMessage>(xml);case MsgType.VIDEO: return Utils.ConvertObj<VideoMessage>(xml);case MsgType.VOICE: return Utils.ConvertObj<VoiceMessage>(xml);case MsgType.LINK:return Utils.ConvertObj<LinkMessage>(xml);case MsgType.LOCATION:return Utils.ConvertObj<LocationMessage>(xml);case MsgType.EVENT://事件类型{} break;default:return Utils.ConvertObj<BaseMessage>(xml);}}}CreateMessage方法传入数据包(如加密,需解密后传入),以基类的形式返回对应的实体。
讲到这里普通消息的接收就差不多讲完了,结合上一篇博文,现在把修改后的接入代码贴出来如下:public class WxRequest{public static BaseMessage Load(EnterParam param, bool bug = true){string postStr = "";Stream s = VqiRequest.GetInputStream();//此方法是对System.Web.HttpContext.Current.Request.InputStream的封装,可直接代码byte[] b = new byte[s.Length];s.Read(b, 0, (int)s.Length);postStr = Encoding.UTF8.GetString(b);//获取微信服务器推送过来的字符串var timestamp = VqiRequest.GetQueryString("timestamp");var nonce = VqiRequest.GetQueryString("nonce");var msg_signature = VqiRequest.GetQueryString("msg_signature");var encrypt_type = VqiRequest.GetQueryString("encrypt_type");string data = "";if (encrypt_type=="aes")//加密模式处理{param.IsAes = true;var ret = new MsgCrypt(param.token, param.EncodingAESKey, param.appid);int r = ret.DecryptMsg(msg_signature, timestamp, nonce, postStr, ref data);if (r != 0){WxApi.Base.WriteBug("消息解密失败");return null;}}else{param.IsAes = false;data = postStr;}if (bug){Utils.WriteTxt(data);}return MessageFactory.CreateMessage(data);}}【相关推荐】
1.微信公众号平台源码下载
2.微信啦啦外卖2.2.4解密开源版 微信魔方源码以上就是微信开发之接收文本消息的详细内容,更多请关注小潘博客其它相关文章!