C#開發(fā)微信門戶及應(yīng)用(6)--微信門戶菜單的管理操作

前面幾篇繼續(xù)了我自己對于C#開發(fā)微信門戶及應(yīng)用的技術(shù)探索和相關(guān)的經(jīng)驗(yàn)總結(jié),繼續(xù)探索微信API并分享相關(guān)的技術(shù),一方面是為了和大家對這方面進(jìn)行互動(dòng)溝通,另一方面也是專心做好微信應(yīng)用的底層技術(shù)開發(fā),把基礎(chǔ)模塊夯實(shí),在未來的應(yīng)用中派上用途。本隨筆繼續(xù)介紹微信門戶菜單的管理操作。

1、菜單的基礎(chǔ)信息

微信門戶的菜單,一般服務(wù)號和訂閱號都可以擁有這個(gè)模塊的開發(fā),但是訂閱號好像需要認(rèn)證后才能擁有,而服務(wù)號則不需要認(rèn)證就可以擁有了。這個(gè)菜單可以有編輯模式和開發(fā)模式,編輯模式主要就是在微信門戶的平臺上,對菜單進(jìn)行編輯;而開發(fā)模式,就是用戶可以通過調(diào)用微信的API對菜單進(jìn)行定制開發(fā),通過POST數(shù)據(jù)到微信服務(wù)器,從而生成對應(yīng)的菜單內(nèi)容。本文主要介紹基于開發(fā)模式的菜單管理操作。
自定義菜單能夠幫助公眾號豐富界面,讓用戶更好更快地理解公眾號的功能。目前自定義菜單最多包括3個(gè)一級菜單,每個(gè)一級菜單最多包含5個(gè)二級菜單。一級菜單最多4個(gè)漢字,二級菜單最多7個(gè)漢字,多出來的部分將會(huì)以“...”代替。目前自定義菜單接口可實(shí)現(xiàn)兩種類型按鈕,如下:

click:用戶點(diǎn)擊click類型按鈕后,微信服務(wù)器會(huì)通過消息接口推送消息類型為event 的結(jié)構(gòu)給開發(fā)者(參考消息接口指南),并且?guī)习粹o中開發(fā)者填寫的key值,開發(fā)者可以通過自定義的key值與用戶進(jìn)行交互;view:用戶點(diǎn)擊view類型按鈕后,微信客戶端將會(huì)打開開發(fā)者在按鈕中填寫的url值 (即網(wǎng)頁鏈接),達(dá)到打開網(wǎng)頁的目的,建議與網(wǎng)頁授權(quán)獲取用戶基本信息接口結(jié)合,獲得用戶的登入個(gè)人信息。
菜單提交的數(shù)據(jù),本身是一個(gè)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類型,有不同的字段內(nèi)容,如type為view的有url屬性,而type為click的,則有key屬性。而菜單可以有子菜單sub_button屬性,總得來說,為了構(gòu)造好對應(yīng)的菜單實(shí)體類信息,不是一下就能分析的出來。

2、菜單的實(shí)體類定義

我看過一些微信接口的開發(fā)代碼,把菜單的分為了好多個(gè)實(shí)體類,指定了繼承關(guān)系,然后分別對他們進(jìn)行屬性的配置,大概的關(guān)系如下所示。



這種多層關(guān)系的繼承方式能解決問題,不過我覺得并不是優(yōu)雅的解決方案。其實(shí)結(jié)合Json.NET自身的Attribute屬性配置,可以指定那些為空的內(nèi)容在序列號為Json字符串的時(shí)候,不顯示出來的。

[JsonProperty( NullValueHandling = NullValueHandling.Ignore)]

有了這個(gè)屬性,我們就可以統(tǒng)一定義菜單的實(shí)體類信息更多的屬性了,可以把View類型和Click類型的菜單屬性的url和key合并在一起。

/// <summary>
/// 菜單基本信息
/// </summary>
public class MenuInfo
{
    /// <summary>
    /// 按鈕描述,既按鈕名字,不超過16個(gè)字節(jié),子菜單不超過40個(gè)字節(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)頁鏈接,用戶點(diǎn)擊按鈕可打開鏈接,不超過256字節(jié)
    /// </summary>
    [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
    public string url { get; set; }

    /// <summary>
    /// 子按鈕數(shù)組,按鈕個(gè)數(shù)應(yīng)為2~5個(gè)
    /// </summary>
    [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
    public List<MenuInfo> sub_button { get; set; }

.......

但是,這么多信息,不同的類型我需要指定不同的屬性類型,那不是挺麻煩,萬一我在View類型的菜單里面,把key屬性設(shè)置了,那怎么辦?

解決方法就是我們定義幾個(gè)構(gòu)造函數(shù),分別用來構(gòu)造不同的菜單信息,如下所示是對菜單不同的類型,賦值給不同的屬性的構(gòu)造函數(shù)。

/// <summary>
/// 參數(shù)化構(gòu)造函數(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;
    }
}

好了,還有另外一個(gè)問題,子菜單也就是屬性sub_button是可有可無的東西,有的話,需要指定Name屬性,并添加它的sub_button集合對象就可以了,那么我們在增加一個(gè)構(gòu)造子菜單的對象信息的構(gòu)造函數(shù)。

/// <summary>
/// 參數(shù)化構(gòu)造函數(shù),用于構(gòu)造子菜單
/// </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的屬性內(nèi)容,其他內(nèi)容為null的話,自然構(gòu)造出來的Json就沒有包含它們,非常完美!

為了獲取菜單的信息,我們還需要定義兩個(gè)實(shí)體對象,如下所示。

/// <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; }
}

3、菜單管理操作的接口實(shí)現(xiàn)

我們從微信的定義里面,可以看到,我們通過API可以獲取菜單信息、創(chuàng)建菜單、刪除菜單,那么我們來定義它們的接口如下。

/// <summary>
/// 菜單的相關(guān)操作
/// </summary>
public interface IMenuApi
{              
    /// <summary>
    /// 獲取菜單數(shù)據(jù)
    /// </summary>
    /// <param name="accessToken">調(diào)用接口憑證</param>
    /// <returns></returns>
    MenuJson GetMenu(string accessToken);
                   
    /// <summary>
    /// 創(chuàng)建菜單
    /// </summary>
    /// <param name="accessToken">調(diào)用接口憑證</param>
    /// <param name="menuJson">菜單對象</param>
    /// <returns></returns>
    CommonResult CreateMenu(string accessToken, MenuJson menuJson);
                   
    /// <summary>
    /// 刪除菜單
    /// </summary>
    /// <param name="accessToken">調(diào)用接口憑證</param>
    /// <returns></returns>
    CommonResult DeleteMenu(string accessToken);
}

具體的獲取菜單信息的實(shí)現(xiàn)如下。

/// <summary>
/// 獲取菜單數(shù)據(jù)
/// </summary>
/// <param name="accessToken">調(diào)用接口憑證</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)一轉(zhuǎn)換為我們需要的實(shí)體信息了,一步到位。

調(diào)用代碼如下所示。

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)建和刪除菜單對象的操作實(shí)現(xiàn)如下所示。

/// <summary>
/// 創(chuàng)建菜單
/// </summary>
/// <param name="accessToken">調(diào)用接口憑證</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">調(diào)用接口憑證</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);
}

看到這里,有些人可能會(huì)問,實(shí)體類你簡化了,那么創(chuàng)建菜單是不是挺麻煩的,特別是構(gòu)造對應(yīng)的信息應(yīng)該如何操作呢?前面不是介紹了不同的構(gòu)造函數(shù)了嗎,通過他們簡單就搞定了,不用記下太多的實(shí)體類及它們的繼承關(guān)系來處理菜單信息。

private void btnCreateMenu_Click(object sender, EventArgs e)
{                       
    MenuInfo productInfo = new MenuInfo("軟件產(chǎn)品", new MenuInfo[] { 
        new MenuInfo("病人資料管理系統(tǒng)", ButtonType.click, "patient"), 
        new MenuInfo("客戶關(guān)系管理系統(tǒng)", ButtonType.click, "crm"), 
        new MenuInfo("酒店管理系統(tǒng)", ButtonType.click, "hotel"), 
        new MenuInfo("送水管理系統(tǒng)", ButtonType.click, "water")
    });                                    

    MenuInfo frameworkInfo = new MenuInfo("框架產(chǎn)品", new MenuInfo[] { 
        new MenuInfo("Win開發(fā)框架", ButtonType.click, "win"),
        new MenuInfo("WCF開發(fā)框架", ButtonType.click, "wcf"),
        new MenuInfo("混合式框架", ButtonType.click, "mix"), 
        new MenuInfo("Web開發(fā)框架", ButtonType.click, "web"),
        new MenuInfo("代碼生成工具", ButtonType.click, "database2sharp")
    });

    MenuInfo relatedInfo = new MenuInfo("相關(guān)鏈接", new MenuInfo[] { 
        new MenuInfo("公司介紹", ButtonType.click, "Event_Company"),
        new MenuInfo("官方網(wǎng)站", ButtonType.view, "http://www.iqidi.com"),
        new MenuInfo("提點(diǎn)建議", ButtonType.click, "Event_Suggestion"),
        new MenuInfo("聯(lián)系客服", ButtonType.click, "Event_Contact"),
        new MenuInfo("發(fā)郵件", ButtonType.view, "http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=S31yfX15fn8LOjplKCQm")
    });

    MenuJson menuJson = new MenuJson();
    menuJson.button.AddRange(new MenuInfo[] { productInfo, frameworkInfo, relatedInfo });

    //Console.WriteLine(menuJson.ToJson());

    if (MessageUtil.ShowYesNoAndWarning("您確認(rèn)要?jiǎng)?chuàng)建菜單嗎") == System.Windows.Forms.DialogResult.Yes)
    {
        IMenuApi menuBLL = new MenuApi();
        CommonResult result = menuBLL.CreateMenu(token, menuJson);
        Console.WriteLine("創(chuàng)建菜單:" + (result.Success ? "成功" : "失敗:" + result.ErrorMessage));
    }
}

這個(gè)就是我微信門戶里面的菜單操作了,具體效果可以關(guān)注我的微信門戶:廣州愛奇迪,也可以掃描下面二維碼進(jìn)行關(guān)注了解。



菜單的效果如下:


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,460評論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,067評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,467評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,468評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,184評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,582評論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,616評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,794評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,343評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,096評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,291評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,863評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,513評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,941評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,190評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,026評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,253評論 2 375

推薦閱讀更多精彩內(nèi)容