微信中的消息类型有:文本,图片,语音,视频,地理位置,链接和事件消息。除了事件消息外,其他的统称为普通消息。微信中消息的推送与响应都是以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 发送方微信号,若为普通用户,则是一个openid
createtime 消息创建时间
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>
public void resmusic(enterparam param, music mu)
{
} public void resvideo(enterparam param, video v)
{ } /// <summary>
/// 回复消息(图片) /// </summary>
public void respicture(enterparam param, picture pic, string domain)
{
} /// <summary>
/// 回复消息(图文列表) /// </summary>
/// <param name="param"></param>
/// <param name="art"></param>
public void resarticles(enterparam param, list<articles> art)
{ } /// <summary>
/// 多客服转发 /// </summary>
/// <param name="param"></param>
public void resdkf(enterparam param)
{
} /// <summary>
/// 多客服转发如果指定的客服没有接入能力(不在线、没有开启自动接入或者自动接入已满),该用户会一直等待指定客服有接入能力后才会被接入,而不会被其他客服接待。建议在指定客服时,先查询客服的接入能力指定到有能力接入的客服,保证客户能够及时得到服务。 /// </summary>
/// <param name="param">用户发送的消息体</param>
/// <param name="kfaccount">多客服账号</param>
public void resdkf(enterparam param, string kfaccount)
{ } private void 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解密开源版 微信魔方源码
以上就是微信开发之接收文本消息的详细内容。