微信公衆號開發系列教程一(調試環境部署續:vs遠程調試)json
C#微信公衆號開發系列教程五(接收事件推送與消息排重)微信開發
C#微信公衆號開發系列教程六(被動回覆與上傳下載多媒體文件)app
第四,第五章已經講了怎麼處理用戶發送的消息,本章就來說講怎麼響應用戶的請求。想必新手看到這個標題也就懵了,千萬別懵,微信的接口就這樣,在回覆圖片、音樂、語音等都須要將咱們的媒體文件上傳到微信的服務器才能使用。不知道這樣的作法是出於什麼考慮的,並且同是給用戶回覆消息,客服接口和羣發接口發送的消息體格式居然是不一樣的。估計是這幾處接口不是同一我的寫的,沒有作好代碼的統一,我們屌絲開發者只能無力吐槽了。微信公衆平臺
在講上傳下載接口前,須要先將下先來說講access_token獲取方法。在微信接口開發的過程access_token是相當重要的,是公衆號的全局惟一票據,公衆號調用各接口時都需使用access_token。開發者須要進行妥善保存。access_token的存儲至少要保留512個字符空間。access_token的有效期目前爲2個小時,需定時刷新,重複獲取將致使上次獲取的access_token失效。須要注意的時,一個公衆號同時只存在一個有效的access_token,開發者須要在access_token過時前,刷新access_token。在刷新的過程當中,公衆平臺後臺會保證在刷新短期內,新老access_token均可用,這保證了第三方業務的平滑過渡。dom
公衆號可使用AppID和AppSecret調用本接口來獲取access_token。AppID和AppSecret可在微信公衆平臺官網-開發者中心頁中得到(須要已經成爲開發者,且賬號沒有異常狀態)。以下圖:curl
獲取access_token的接口地址是:
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
將appid和secret替換成你本身的。
發送get請求到這個地址,返回的數據以下:
{"access_token":"eEd6dhp0s24JfWwDyGBbrvJxnhqHTSYZ8MKdQ7MuCGBKxAjHv-tEIwhFZzn102lGvIWxnjZZreT6C1NCT9fpS7NREOkEX42yojVnqKVaicg","expires_in":7200}
咱們只需解析這個json,便可獲取到咱們所需的access_token.代碼以下:
AccessToken實體類:
public class AccessToken { public string token { get; set; } public DateTime expirestime { get; set; } }
獲取access token
/// <summary> /// 獲取access token /// </summary> /// <param name="appid">第三方用戶惟一憑證</param> /// <param name="secret">第三方用戶惟一憑證密鑰,即appsecret</param> /// <returns>AccessToken對象,expirestime是過時時間</returns> public static AccessToken GetAccessToken(string appid, string secret) { try { string url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", appid, secret); string retdata = Utils.HttpGet(url); if (retdata.Contains("access_token")) { JObject obj = (JObject)JsonConvert.DeserializeObject(retdata); string token = obj.Value<string>("access_token"); int expirestime = obj.Value<int>("expires_in"); return new AccessToken { token = token, expirestime = DateTime.Now.AddSeconds(expirestime) }; } else { WriteBug(retdata);//寫錯誤日誌 } return null; } catch (Exception e) { WriteBug(e.ToString());//寫錯誤日誌 return null; } }
access_token獲取成功後,下面來說上傳下載多媒體文件吧。官方說,公衆號在使用接口時,對多媒體文件、多媒體消息的獲取和調用等操做,是經過media_id來進行的(咱讀書少,不明白爲何不能使用url,而要畫蛇添足先上傳到服務器在發送)。經過本接口,公衆號能夠上傳或下載多媒體文件。但請注意,每一個多媒體文件(media_id)會在上傳、用戶發送到微信服務器3天后自動刪除,以節省服務器資源。
上傳多媒體的接口地址是:
http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
其中access_token爲調用接口憑證,type是媒體文件類型,分別有圖片(image)、語音(voice)、視頻(video)和縮略圖(thumb)
注意事項:
上傳的多媒體文件有格式和大小限制,以下:
媒體文件在後臺保存時間爲3天,即3天后media_id失效。
爲了方便調用,將媒體文件的類型定義爲枚舉,代碼以下:
public enum MediaType { /// <summary> /// 圖片(image): 1M,支持JPG格式 /// </summary> image, /// <summary> /// 語音(voice):2M,播放長度不超過60s,支持AMR\MP3格式 /// </summary> voice, /// <summary> /// 視頻(video):10MB,支持MP4格式 /// </summary> video, /// <summary> /// 縮略圖(thumb):64KB,支持JPG格式 /// </summary> thumb }
而後定義返回值的類型:
public class UpLoadInfo { /// <summary> /// 媒體文件類型,分別有圖片(image)、語音(voice)、視頻(video)和縮略圖(thumb,主要用於視頻與音樂格式的縮略圖) /// </summary> public string type { get; set; } /// <summary> /// 媒體文件上傳後,獲取時的惟一標識 /// </summary> public string media_id { get; set; } /// <summary> /// 媒體文件上傳時間戳 /// </summary> public string created_at { get; set; } }
最後使用WebClient類來上傳文件,並讀出返回值,代碼以下:
/// <summary> /// 微信上傳多媒體文件 /// </summary> /// <param name="filepath">文件絕對路徑</param> public static ReceiveModel.UpLoadInfo WxUpLoad(string filepath, string token, MediaType mt) { using (WebClient client = new WebClient()) { byte[] b = client.UploadFile(string.Format("http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token={0}&type={1}", token, mt.ToString()), filepath);//調用接口上傳文件 string retdata = Encoding.Default.GetString(b);//獲取返回值 if (retdata.Contains("media_id"))//判斷返回值是否包含media_id,包含則說明上傳成功,而後將返回的json字符串轉換成json { return JsonConvert.DeserializeObject<UpLoadInfo>(retdata); } else {//不然,寫錯誤日誌 WriteBug(retdata);//寫錯誤日誌 return null; } } }
至此,在講回覆消息以前,插入了兩個基礎支持接口,因爲各位整理概括能力太爛了,各位看官請多包涵,若有問題就留言和我交流。 下面正式開始講回覆消息。在看下面內容的時候,請你們結合第四,第五章進行閱讀。
前面兩章講述了接收並處理用戶發送的消息,講到了一個消息基類BaseMessage,而無論咱們接收到什麼類型的消息,都須要能夠調用方法,進行響應用戶的請求,因此,用戶回覆用戶請求的方法須要封裝到基類中。下面先簡單瞭解下公衆號能夠回覆的消息類型,以及消息格式。
注意:
一旦遇到如下狀況,微信都會在公衆號會話中,向用戶下發系統提示「該公衆號暫時沒法提供服務,請稍後再試」:
一、開發者在5秒內未回覆任何內容 二、開發者回覆了異常數據,好比JSON數據等
<xml> <ToUserName><![CDATA[接收方賬號(收到的OpenID)]]></ToUserName> <FromUserName><![CDATA[開發者微信號]]></FromUserName> <CreateTime>消息建立時間 (整型)</CreateTime> <MsgType><![CDATA[image]]></MsgType> <Content><![CDATA[回覆的消息內容(換行:在content中可以換行,微信客戶端就支持換行顯示)]]></Content> </xml>
<xml> <ToUserName><![CDATA[接收方賬號(收到的OpenID)]]></ToUserName> <FromUserName><![CDATA[開發者微信號]]></FromUserName> <CreateTime>消息建立時間 (整型)</CreateTime> <MsgType><![CDATA[image]]></MsgType> <Image> <MediaId><![CDATA[經過上傳多媒體文件,獲得的id。]]></MediaId> </Image> </xml>
<xml> <ToUserName><![CDATA[接收方賬號(收到的OpenID)]]></ToUserName> <FromUserName><![CDATA[開發者微信號]]></FromUserName> <CreateTime>消息建立時間 (整型)</CreateTime> <MsgType><![CDATA[voice]]></MsgType> <Voice> <MediaId><![CDATA[經過上傳多媒體文件,獲得的id。]]></MediaId> </Voice> </xml>
<xml> <ToUserName><![CDATA[接收方賬號(收到的OpenID)]]></ToUserName> <FromUserName><![CDATA[開發者微信號]]></FromUserName> <CreateTime>消息建立時間 (整型)</CreateTime> <MsgType><![CDATA[video]]></MsgType> <Video> <MediaId><![CDATA[經過上傳多媒體文件,獲得的id。]]></MediaId> <Title><![CDATA[視頻消息的標題]]></Title>
<Description><![CDATA[視頻消息的描述]]></Description>
</Video> </xml>
<xml> <ToUserName><![CDATA[接收方賬號(收到的OpenID)]]></ToUserName> <FromUserName><![CDATA[開發者微信號]]></FromUserName> <CreateTime>消息建立時間 (整型)</CreateTime> <MsgType><![CDATA[music]]></MsgType> <Music> <ThumbMediaId><![CDATA[縮略圖的媒體id,經過上傳多媒體文件,獲得的id。]]></ThumbMediaId> <Title><![CDATA[視頻消息的標題]]></Title>
<Description><![CDATA[視頻消息的描述]]></Description>
<MusicURL><![CDATA[音樂連接]]></MusicURL>
<HQMusicUrl><![CDATA[高質量音樂連接,WIFI環境優先使用該連接播放音樂]]></HQMusicUrl>
</Music> </xml>
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>12345678</CreateTime> <MsgType><![CDATA[news]]></MsgType> <ArticleCount>2</ArticleCount> <Articles> <item> <Title><![CDATA[title1]]></Title> <Description><![CDATA[description1]]></Description> <PicUrl><![CDATA[picurl]]></PicUrl> <Url><![CDATA[url]]></Url> </item> <item> <Title><![CDATA[title]]></Title> <Description><![CDATA[description]]></Description> <PicUrl><![CDATA[picurl]]></PicUrl> <Url><![CDATA[url]]></Url> </item> </Articles> </xml>
回覆圖文中,item是一個項,一個item代碼一個圖文。在響應的時候,咱們只需根據數據格式,替換掉對應的屬性,而後Response.Write(s)便可。結合前兩章的講解,BaseMessage的最終代碼以下:
/// <summary> /// 消息體基類 /// </summary> 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) { StringBuilder resxml = new StringBuilder(string.Format("<xml><ToUserName><![CDATA[{0}]]></ToUserName><FromUserName><![CDATA[{1}]]></FromUserName><CreateTime>{2}</CreateTime>", FromUserName, ToUserName, Utils.ConvertDateTimeInt(DateTime.Now))); resxml.AppendFormat("<MsgType><![CDATA[text]]></MsgType><Content><![CDATA[{0}]]></Content><FuncFlag>0</FuncFlag></xml>", content); Response(param, resxml.ToString()); } /// <summary> /// 回覆消息(音樂) /// </summary> public void ResMusic(EnterParam param, Music mu) { StringBuilder resxml = new StringBuilder(string.Format("<xml><ToUserName><![CDATA[{0}]]></ToUserName><FromUserName><![CDATA[{1}]]></FromUserName><CreateTime>{2}</CreateTime>",FromUserName,ToUserName, Utils.ConvertDateTimeInt(DateTime.Now))); resxml.Append(" <MsgType><![CDATA[music]]></MsgType>"); resxml.AppendFormat("<Music><Title><![CDATA[{0}]]></Title><Description><![CDATA[{1}]]></Description>", mu.Title, mu.Description); resxml.AppendFormat("<MusicUrl><![CDATA[http://{0}{1}]]></MusicUrl><HQMusicUrl><![CDATA[http://{2}{3}]]></HQMusicUrl></Music><FuncFlag>0</FuncFlag></xml>", VqiRequest.GetCurrentFullHost(), mu.MusicUrl, VqiRequest.GetCurrentFullHost(), mu.HQMusicUrl); Response(param, resxml.ToString()); } public void ResVideo(EnterParam param, Video v) { StringBuilder resxml = new StringBuilder(string.Format("<xml><ToUserName><![CDATA[{0}]]></ToUserName><FromUserName><![CDATA[{1}]]></FromUserName><CreateTime>{2}</CreateTime>",FromUserName,ToUserName, Utils.ConvertDateTimeInt(DateTime.Now))); resxml.Append(" <MsgType><![CDATA[video]]></MsgType>"); resxml.AppendFormat("<Video><MediaId><![CDATA[{0}]]></MediaId>", v.media_id); resxml.AppendFormat("<Title><![CDATA[{0}]]></Title>", v.title); resxml.AppendFormat("<Description><![CDATA[{0}]]></Description></Video></xml>", v.description); Response(param, resxml.ToString()); } /// <summary> /// 回覆消息(圖片) /// </summary> public void ResPicture(EnterParam param, Picture pic, string domain) { StringBuilder resxml = new StringBuilder(string.Format("<xml><ToUserName><![CDATA[{0}]]></ToUserName><FromUserName><![CDATA[{1}]]></FromUserName><CreateTime>{2}</CreateTime>",FromUserName,ToUserName, Utils.ConvertDateTimeInt(DateTime.Now))); resxml.Append(" <MsgType><![CDATA[image]]></MsgType>"); resxml.AppendFormat("<PicUrl><![CDATA[{0}]]></PicUrl></xml>", domain + pic.PictureUrl); Response(param, resxml.ToString()); } /// <summary> /// 回覆消息(圖文列表) /// </summary> /// <param name="param"></param> /// <param name="art"></param> public void ResArticles(EnterParam param, List<Articles> art) { StringBuilder resxml = new StringBuilder(string.Format("<xml><ToUserName><![CDATA[{0}]]></ToUserName><FromUserName><![CDATA[{1}]]></FromUserName><CreateTime>{2}</CreateTime>",FromUserName,ToUserName, Utils.ConvertDateTimeInt(DateTime.Now))); resxml.AppendFormat("<MsgType><![CDATA[news]]></MsgType><ArticleCount>{0}</ArticleCount><Articles>", art.Count); for (int i = 0; i < art.Count; i++) { resxml.AppendFormat("<item><Title><![CDATA[{0}]]></Title> <Description><![CDATA[{1}]]></Description>", art[i].Title, art[i].Description); resxml.AppendFormat("<PicUrl><![CDATA[{0}]]></PicUrl><Url><![CDATA[{1}]]></Url></item>", art[i].PicUrl.Contains("http://") ? art[i].PicUrl : "http://" + VqiRequest.GetCurrentFullHost() + art[i].PicUrl, art[i].Url.Contains("http://") ? art[i].Url : "http://" + VqiRequest.GetCurrentFullHost() + art[i].Url); } resxml.Append("</Articles><FuncFlag>0</FuncFlag></xml>"); Response(param, resxml.ToString()); } /// <summary> /// 多客服轉發 /// </summary> /// <param name="param"></param> public void ResDKF(EnterParam param) { StringBuilder resxml = new StringBuilder(); resxml.AppendFormat("<xml><ToUserName><![CDATA[{0}]]></ToUserName>",FromUserName); resxml.AppendFormat("<FromUserName><![CDATA[{0}]]></FromUserName><CreateTime>{1}</CreateTime>",ToUserName,CreateTime); resxml.AppendFormat("<MsgType><![CDATA[transfer_customer_service]]></MsgType></xml>"); Response(param, resxml.ToString()); } /// <summary> /// 多客服轉發若是指定的客服沒有接入能力(不在線、沒有開啓自動接入或者自動接入已滿),該用戶會一直等待指定客服有接入能力後纔會被接入,而不會被其餘客服接待。建議在指定客服時,先查詢客服的接入能力指定到有能力接入的客服,保證客戶可以及時獲得服務。 /// </summary> /// <param name="param">用戶發送的消息體</param> /// <param name="KfAccount">多客服帳號</param> public void ResDKF(EnterParam param, string KfAccount) { StringBuilder resxml = new StringBuilder(); resxml.AppendFormat("<xml><ToUserName><![CDATA[{0}]]></ToUserName>",FromUserName); resxml.AppendFormat("<FromUserName><![CDATA[{0}]]></FromUserName><CreateTime>{1}</CreateTime>",ToUserName,CreateTime); resxml.AppendFormat("<MsgType><![CDATA[transfer_customer_service]]></MsgType><TransInfo><KfAccount>{0}</KfAccount></TransInfo></xml>", KfAccount); Response(param, resxml.ToString()); } private void Response(EnterParam param, string data) { if (param.IsAes) { var wxcpt = new MsgCrypt(param.token, param.EncodingAESKey, param.appid); wxcpt.EncryptMsg(data, Utils.ConvertDateTimeInt(DateTime.Now).ToString(), Utils.GetRamCode(), ref data); } Utils.ResponseWrite(data); } }
上面的代碼中,public void ResDKF(EnterParam param),public void ResDKF(EnterParam param, string KfAccount)兩個方法時多客服中,用戶轉發用戶發送的消息的,多客服將在後期的博文中進行更新,敬請期待。
public void ResMusic(EnterParam param, Music mu)方法中的Music類的定義以下:
public class Music { #region 屬性 /// <summary> /// 音樂連接 /// </summary> public string MusicUrl { get; set; } /// <summary> /// 高質量音樂連接,WIFI環境優先使用該連接播放音樂 /// </summary> public string HQMusicUrl { get; set; } /// <summary> /// 標題 /// </summary> public string Title { get; set; } /// <summary> /// 描述 /// </summary> public string Description { get; set; } #endregion }
public void ResVideo(EnterParam param, Video v)方法中的Video類的定義以下:
public class Video { public string title { get; set; } public string media_id { get; set; } public string description { get; set; } }
public void ResArticles(EnterParam param, List<Articles> art)中的Articles定義以下:
public class Articles { #region 屬性 /// <summary> /// 圖文消息標題 /// </summary> public string Title { get; set; } /// <summary> /// 圖文消息描述 /// </summary> public string Description { get; set; } /// <summary> /// 圖片連接,支持JPG、PNG格式,較好的效果爲大圖640*320,小圖80*80。 /// </summary> public string PicUrl { get; set; } /// <summary> /// 點擊圖文消息跳轉連接 /// </summary> public string Url { get; set; } #endregion }
至此,此章結束。
時間倉促,若有不明白的,請留言,若是你以爲本篇博文對你有幫助,請點擊一下推薦,推薦給更多的朋友的。
各位有建議或者意見可留言給我哦,或者加如QQ羣一塊兒進行交流。
若是你是土豪,能夠掃描下面的二維碼懸賞一下,你的支持是筆者繼續更新下去的動力。