前面幾篇繼續(xù)了我自己對于C#開發(fā)微信門戶及應用的技術探索和相關的經驗總結,繼續(xù)探索微信API并分享相關的技術,一方面是為了和大家對這方面進行互動溝通,另一方面也是專心做好微信應用的底層技術開發(fā),把基礎模塊夯實,在未來的應用中派上用途。本隨筆繼續(xù)介紹微信門戶菜單的管理操作。
微信門戶的菜單,一般服務號和訂閱號都可以擁有這個模塊的開發(fā),但是訂閱號好像需要認證后才能擁有,而服務號則不需要認證就可以擁有了。這個菜單可以有編輯模式和開發(fā)模式,編輯模式主要就是在微信門戶的平臺上,對菜單進行編輯;而開發(fā)模式,就是用戶可以通過調用微信的API對菜單進行定制開發(fā),通過POST數(shù)據(jù)到微信服務器,從而生成對應的菜單內容。本文主要介紹基于開發(fā)模式的菜單管理操作。
自定義菜單能夠幫助公眾號豐富界面,讓用戶更好更快地理解公眾號的功能。目前自定義菜單最多包括3個一級菜單,每個一級菜單最多包含5個二級菜單。一級菜單最多4個漢字,二級菜單最多7個漢字,多出來的部分將會以“...”代替。目前自定義菜單接口可實現(xiàn)兩種類型按鈕,如下:
click:用戶點擊click類型按鈕后,微信服務器會通過消息接口推送消息類型為event 的結構給開發(fā)者(參考消息接口指南),并且?guī)习粹o中開發(fā)者填寫的key值,開發(fā)者可以通過自定義的key值與用戶進行交互;view:用戶點擊view類型按鈕后,微信客戶端將會打開開發(fā)者在按鈕中填寫的url值 (即網(wǎng)頁鏈接),達到打開網(wǎng)頁的目的,建議與網(wǎng)頁授權獲取用戶基本信息接口結合,獲得用戶的登入個人信息。
菜單提交的數(shù)據(jù),本身是一個Json的數(shù)據(jù)字符串,它的官方例子數(shù)據(jù)如下所示。
{ "button":[ { "type":"click", "name":"今日歌曲", "key":"V1001_TODAY_MUSIC" }, { "type":"click", "name":"歌手簡介", "key":"V1001_TODAY_SINGER" }, { "name":"菜單", "sub_button":[ { "type":"view", "name":"搜索", "url":"http://www.soso.com/" }, { "type":"view", "name":"視頻", "url":"http://v.QQ.com/" }, { "type":"click", "name":"贊一下我們", "key":"V1001_GOOD" }] }] }從上面我們可以看到,菜單不同的type類型,有不同的字段內容,如type為view的有url屬性,而type為click的,則有key屬性。而菜單可以有子菜單sub_button屬性,總得來說,為了構造好對應的菜單實體類信息,不是一下就能分析的出來。
我看過一些微信接口的開發(fā)代碼,把菜單的分為了好多個實體類,指定了繼承關系,然后分別對他們進行屬性的配置,大概的關系如下所示。

這種多層關系的繼承方式能解決問題,不過我覺得并不是優(yōu)雅的解決方案。其實結合Json.NET自身的Attribute屬性配置,可以指定那些為空的內容在序列號為Json字符串的時候,不顯示出來的。
[JsonPRoperty( NullValueHandling = NullValueHandling.Ignore)]
有了這個屬性,我們就可以統(tǒng)一定義菜單的實體類信息更多的屬性了,可以把View類型和Click類型的菜單屬性的url和key合并在一起。
/// <summary> /// 菜單基本信息 /// </summary> public class MenuInfo { /// <summary> /// 按鈕描述,既按鈕名字,不超過16個字節(jié),子菜單不超過40個字節(jié) /// </summary> public string name { get; set; } /// <summary> /// 按鈕類型(click或view) /// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string type { get; set; } /// <summary> /// 按鈕KEY值,用于消息接口(event類型)推送,不超過128字節(jié) /// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string key { get; set; } /// <summary> /// 網(wǎng)頁鏈接,用戶點擊按鈕可打開鏈接,不超過256字節(jié) /// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string url { get; set; } /// <summary> /// 子按鈕數(shù)組,按鈕個數(shù)應為2~5個 /// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public List<MenuInfo> sub_button { get; set; }.......但是,這么多信息,不同的類型我需要指定不同的屬性類型,那不是挺麻煩,萬一我在View類型的菜單里面,把key屬性設置了,那怎么辦?
解決方法就是我們定義幾個構造函數(shù),分別用來構造不同的菜單信息,如下所示是對菜單不同的類型,賦值給不同的屬性的構造函數(shù)。
/// <summary> /// 參數(shù)化構造函數(shù) /// </summary> /// <param name="name">按鈕名稱</param> /// <param name="buttonType">菜單按鈕類型</param> /// <param name="value">按鈕的鍵值(Click),或者連接URL(View)</param> public MenuInfo(string name, ButtonType buttonType, string value) { this.name = name; this.type = buttonType.ToString(); if (buttonType == ButtonType.click) { this.key = value; } else if(buttonType == ButtonType.view) { this.url = value; } }好了,還有另外一個問題,子菜單也就是屬性sub_button是可有可無的東西,有的話,需要指定Name屬性,并添加它的sub_button集合對象就可以了,那么我們在增加一個構造子菜單的對象信息的構造函數(shù)。
/// <summary> /// 參數(shù)化構造函數(shù),用于構造子菜單 /// </summary> /// <param name="name">按鈕名稱</param> /// <param name="sub_button">子菜單集合</param> public MenuInfo(string name, IEnumerable<MenuInfo> sub_button) { this.name = name; this.sub_button = new List<MenuInfo>(); this.sub_button.AddRange(sub_button); }由于只指定Name和sub_button的屬性內容,其他內容為null的話,自然構造出來的Json就沒有包含它們,非常完美!
為了獲取菜單的信息,我們還需要定義兩個實體對象,如下所示。
/// <summary> /// 菜單的Json字符串對象 /// </summary> public class MenuJson { public List<MenuInfo> button { get; set; } public MenuJson() { button = new List<MenuInfo>(); } } /// <summary> /// 菜單列表的Json對象 /// </summary> public class MenuListJson { public MenuJson menu { get; set; } }我們從微信的定義里面,可以看到,我們通過API可以獲取菜單信息、創(chuàng)建菜單、刪除菜單,那么我們來定義它們的接口如下。
/// <summary> /// 菜單的相關操作 /// </summary> public interface IMenuApi { /// <summary> /// 獲取菜單數(shù)據(jù) /// </summary> /// <param name="accessToken">調用接口憑證</param> /// <returns></returns> MenuJson GetMenu(string accessToken); /// <summary> /// 創(chuàng)建菜單 /// </summary> /// <param name="accessToken">調用接口憑證</param> /// <param name="menuJson">菜單對象</param> /// <returns></returns> CommonResult CreateMenu(string accessToken, MenuJson menuJson); /// <summary> /// 刪除菜單 /// </summary> /// <param name="accessToken">調用接口憑證</param> /// <returns></returns> CommonResult DeleteMenu(string accessToken); }具體的獲取菜單信息的實現(xiàn)如下。
/// <summary> /// 獲取菜單數(shù)據(jù) /// </summary> /// <param name="accessToken">調用接口憑證</param> /// <returns></returns> public MenuJson GetMenu(string accessToken) { MenuJson menu = null; var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/get?access_token={0}", accessToken); MenuListJson list = JsonHelper<MenuListJson>.ConvertJson(url); if (list != null) { menu = list.menu; } return menu; }這里就是把返回的Json數(shù)據(jù),統(tǒng)一轉換為我們需要的實體信息了,一步到位。
調用代碼如下所示。
private void btnGetMenuJson_Click(object sender, EventArgs e) { IMenuApi menuBLL = new MenuApi(); MenuJson menu = menuBLL.GetMenu(token); if (menu != null) { Console.WriteLine(menu.ToJson()); } }創(chuàng)建和刪除菜單對象的操作實現(xiàn)如下所示。
/// <summary> /// 創(chuàng)建菜單 /// </summary> /// <param name="accessToken">調用接口憑證</param> /// <param name="menuJson">菜單對象</param> /// <returns></returns> public CommonResult CreateMenu(string accessToken, MenuJson menuJson) { var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}", accessToken); string postData = menuJson.ToJson(); return Helper.GetExecuteResult(url, postData); } /// <summary> /// 刪除菜單 /// </summary> /// <param name="accessToken">調用接口憑證</param> /// <returns></returns> public CommonResult DeleteMenu(string accessToken) { var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={0}", accessToken); return Helper.GetExecuteResult(url); }看到這里,有些人可能會問,實體類你簡化了,那么創(chuàng)建菜單是不是挺麻煩的,特別是構造對應的信息應該如何操作呢?前面不是介紹了不同的構造函數(shù)了嗎,通過他們簡單就搞定了,不用記下太多的實體類及它們的繼承關系來處理菜單信息。
private void btnCreateMenu_Click(object sender, EventArgs e) { MenuInfo p
新聞熱點
疑難解答