微信中的消息類型有:文本,圖片,語音,視頻,地理位置,連結和事件訊息。除了事件訊息外,其他的統稱為普通訊息。微信中訊息的推送與回應都是以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解密開源版微信魔術方塊原始碼
#
以上是微信開發之接收文字訊息的詳細內容。更多資訊請關注PHP中文網其他相關文章!