程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#微信公眾號開發系列教程四(接收普通消息)

C#微信公眾號開發系列教程四(接收普通消息)

編輯:C#入門知識

C#微信公眾號開發系列教程四(接收普通消息)


微信中的消息類型有:文本,圖片,語音,視頻,地理位置,鏈接和事件消息。除了事件消息外,其他的統稱為普通消息。微信中消息的推送與響應都是以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. 下一頁:
Copyright © 程式師世界 All Rights Reserved