微信公衆號開發C#系列-八、自定義菜單及菜單響應事件的處理

一、概述

自定義菜單可以幫助公衆號豐富界面,讓用戶更好更快地理解公衆號的功能。菜單分爲默認菜單與個性化菜單。個性化菜單接口是爲了幫助公衆號實現靈活的業務運營,開發者能夠經過該接口,讓公衆號的不一樣用戶羣體看到不同的自定義菜單。該接口開放給已認證訂閱號和已認證服務號。本文主要介紹微信自定義菜單的建立、查詢、刪除、各菜單類型事件的響應方法,以及菜單應用的界面整合參考。因爲篇幅有限不可能面面俱到,只能拋磚迎玉,你們就能夠據此擴展作深刻的應用。git

二、自定義菜單的規則

  1. 自定義菜單分爲一級菜單和二級菜單。
  2. 一級菜單數量爲1-3個,即打開公衆帳號直接能夠看到排列在最下方的最多3個按鈕。一級菜單的文字最多不能4個漢字,多出來的部分將會以「...」代替。
  3. 二級菜單從屬於一級菜單,數量爲1-5個。二級菜單的文字不最多不能超過8個漢字,多出來的部分將會以「...」代替。
  4. 不管一級菜單仍是二級菜單,都有兩個觸發事件能夠選擇,分別是:點擊(click,值不能超過128字節)和打開網址(view,url不能超過256個字節)。
  5. 當一個一級菜單下有二級菜單存在的時候,這個一級菜單按鈕被點擊不會有任何事件發生。
  6. 建立自定義菜單後,菜單的刷新策略是,在用戶進入公衆號會話頁或公衆號profile頁時,若是發現上一次拉取菜單的請求在5分鐘之前,就會拉取一下菜單,若是菜單有更新,就會刷新客戶端的菜單。測試時能夠嘗試取消關注公衆帳號後再次關注,則能夠看到建立後的效果。

三、自定義菜單接口可實現按鈕類型的種類

自定義菜單接口可實現多種類型按鈕,以下:github

一、click:點擊推事件用戶點擊click類型按鈕後,微信服務器會經過消息接口推送消息類型爲event的結構給開發者(參考消息接口指南),而且帶上按鈕中開發者填寫的key值,開發者能夠經過自定義的key值與用戶進行交互;小程序

二、view:跳轉URL用戶點擊view類型按鈕後,微信客戶端將會打開開發者在按鈕中填寫的網頁URL,可與網頁受權獲取用戶基本信息接口結合,得到用戶基本信息。api

三、scancode_push:掃碼推事件用戶點擊按鈕後,微信客戶端將調起掃一掃工具,完成掃碼操做後顯示掃描結果(若是是URL,將進入URL),且會將掃碼的結果傳給開發者,開發者能夠下發消息。數組

四、scancode_waitmsg:掃碼推事件且彈出「消息接收中」提示框用戶點擊按鈕後,微信客戶端將調起掃一掃工具,完成掃碼操做後,將掃碼的結果傳給開發者,同時收起掃一掃工具,而後彈出「消息接收中」提示框,隨後可能會收到開發者下發的消息。服務器

五、pic_sysphoto:彈出系統拍照發圖用戶點擊按鈕後,微信客戶端將調起系統相機,完成拍照操做後,會將拍攝的相片發送給開發者,並推送事件給開發者,同時收起系統相機,隨後可能會收到開發者下發的消息。微信

六、pic_photo_or_album:彈出拍照或者相冊發圖用戶點擊按鈕後,微信客戶端將彈出選擇器供用戶選擇「拍照」或者「從手機相冊選擇」。用戶選擇後即走其餘兩種流程。app

七、pic_weixin:彈出微信相冊發圖器用戶點擊按鈕後,微信客戶端將調起微信相冊,完成選擇操做後,將選擇的相片發送給開發者的服務器,並推送事件給開發者,同時收起相冊,隨後可能會收到開發者下發的消息。微信公衆平臺

八、location_select:彈出地理位置選擇器用戶點擊按鈕後,微信客戶端將調起地理位置選擇工具,完成選擇操做後,將選擇的地理位置發送給開發者的服務器,同時收起位置選擇工具,隨後可能會收到開發者下發的消息。框架

九、media_id:下發消息(除文本消息)用戶點擊media_id類型按鈕後,微信服務器會將開發者填寫的永久素材id對應的素材下發給用戶,永久素材類型能夠是圖片、音頻、視頻、圖文消息。請注意:永久素材id必須是在「素材管理/新增永久素材」接口上傳後得到的合法id。

十、view_limited:跳轉圖文消息URL用戶點擊view_limited類型按鈕後,微信客戶端將打開開發者在按鈕中填寫的永久素材id對應的圖文消息URL,永久素材類型只支持圖文消息。請注意:永久素材id必須是在「素材管理/新增永久素材」接口上傳後得到的合法id。

請注意,3到8的全部事件,僅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用戶,舊版本微信用戶點擊後將沒有迴應,開發者也不能正常接收到事件推送。9和10,是專門給第三方平臺旗下未微信認證(具體而言,是資質認證未經過)的訂閱號準備的事件類型,它們是沒有事件推送的,能力相對受限,其餘類型的公衆號沒必要使用。

四、建立菜單

4.一、建立菜單的接口

微信提供了接口API能夠建立自定義菜單,接口相關說明以下。

http請求方式:POST(請使用https協議)

https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
複製代碼

click和view的請求示例

{
     "button":[
     {    
          "type":"click",
          "name":"今日歌曲",
          "key":"V1001_TODAY_MUSIC"
      },
      {
           "name":"菜單",
           "sub_button":[
           {    
               "type":"view",
               "name":"搜索",
               "url":"http://www.soso.com/"
            },
            {
                 "type":"miniprogram",
                 "name":"wxa",
                 "url":"http://mp.weixin.qq.com",
                 "appid":"wx286b93c14bbf93aa",
                 "pagepath":"pages/lunar/index"
             },
            {
               "type":"click",
               "name":"贊一下咱們",
               "key":"V1001_GOOD"
            }]
       }]
 }
複製代碼

其餘新增按鈕類型的請求示例

{
    "button": [
        {
            "name": "掃碼", 
            "sub_button": [
                {
                    "type": "scancode_waitmsg", 
                    "name": "掃碼帶提示", 
                    "key": "rselfmenu_0_0", 
                    "sub_button": [ ]
                }, 
                {
                    "type": "scancode_push", 
                    "name": "掃碼推事件", 
                    "key": "rselfmenu_0_1", 
                    "sub_button": [ ]
                }
            ]
        }, 
        {
            "name": "發圖", 
            "sub_button": [
                {
                    "type": "pic_sysphoto", 
                    "name": "系統拍照發圖", 
                    "key": "rselfmenu_1_0", 
                   "sub_button": [ ]
                 }, 
                {
                    "type": "pic_photo_or_album", 
                    "name": "拍照或者相冊發圖", 
                    "key": "rselfmenu_1_1", 
                    "sub_button": [ ]
                }, 
                {
                    "type": "pic_weixin", 
                    "name": "微信相冊發圖", 
                    "key": "rselfmenu_1_2", 
                    "sub_button": [ ]
                }
            ]
        }, 
        {
            "name": "發送位置", 
            "type": "location_select", 
            "key": "rselfmenu_2_0"
        },
        {
           "type": "media_id", 
           "name": "圖片", 
           "media_id": "MEDIA_ID1"
        }, 
        {
           "type": "view_limited", 
           "name": "圖文消息", 
           "media_id": "MEDIA_ID2"
        }
    ]
}
複製代碼

參數說明

參數			 是否必須					說明
button		   是						一級菜單數組,個數應爲1~3個
sub_button	否						二級菜單數組,個數應爲1~5個
type		    是						菜單的響應動做類型,view表示網頁類型,click表示點擊類型,miniprogram表示小程序類型
name		是		菜單標題,		不超過16個字節,子菜單不超過60個字節
key			click等點擊類型必須		菜單KEY值,用於消息接口推送,不超過128字節
url			view、miniprogram類型必須	網頁 連接,用戶點擊菜單可打開連接,不超過1024字節。 type爲miniprogram時,不支持小程序的老版本客戶端將打開本url。
media_id	media_id類型和view_limited類型必須	調用新增永久素材接口返回的合法media_id
appid		miniprogram類型必須		小程序的appid(僅認證公衆號可配置)
pagepath	miniprogram類型必須		小程序的頁面路徑
複製代碼

4.二、使用Senparc.Weixin.MP SDK建立自定義菜單

使用微信提供的接口建立菜單編碼量仍是很是大,在此咱們藉助Senparc.Weixin.MP SDK來快速的建立自定義菜單。很是簡單,只需三步:

第一步:獲取AccessToken

var accessToken = AccessTokenContainer.TryGetToken(appId, appSecret).access_token;
複製代碼

PS:若是第三步中使用AppId取代AccessToken,則這一步能夠省略。

第二步:組織菜單內容

ButtonGroup bg = new ButtonGroup();
        
//菜單1
var subButton = new SubButton()
{
    name = "菜單1"
};

bg.button.Add(subButton);
subButton.sub_button.Add(new SingleClickButton()
{
    key = "SubClickRoot_Text",
    name = "返回文本"
});
subButton.sub_button.Add(new SingleClickButton()
{
    key = "SubClickRoot_News",
    name = "返回圖文"
});
subButton.sub_button.Add(new SingleClickButton()
{
    key = "SubClickRoot_Music",
    name = "返回音樂"
});
subButton.sub_button.Add(new SingleViewButton()
{
    url = "http://www.rdiframework.net/",
    name = "Url跳轉"
});

//菜單2
var subButton2 = new SubButton()
{
    name = "菜單2"
};

bg.button.Add(subButton2);
subButton2.sub_button.Add(new SingleClickButton()
{
    key = "SubClickRoot_Text",
    name = "返回文本"
});
subButton2.sub_button.Add(new SingleClickButton()
{
    key = "SubClickRoot_News",
    name = "返回圖文"
});
複製代碼

第三步:提交到微信服務器

var result = CommonApi.CreateMenu(accessToken, bg);
複製代碼

上面SingleClickButton和SingleViewButton分別對應了微信API中的click和view兩種菜單響應方式。

經過執行上述代碼咱們能夠看到建立的菜單以下所示,能夠看到上面咱們經過代碼建立的自定義菜單。

自定義的菜單效果

4.三、查詢已建立的菜單

使用接口建立自定義菜單後,開發者還可以使用接口查詢自定義菜單的結構。另外請注意,在設置了個性化菜單後,使用本自定義菜單查詢接口能夠獲取默認菜單和所有個性化菜單信息。 請求說明

http請求方式:GET
https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN
複製代碼

須要注意的時返回的Json數據分兩種類型,默認菜單與個性化的菜單。 menu爲默認菜單,conditionalmenu爲個性化菜單列表。字段說明請見個性化菜單接口頁的說明。 使用Senparc.Weixin.MP SDK查詢已建立的菜單接口只需一行代碼,以下:

var result = CommonApi.GetMenu(accessToken);
複製代碼

4.四、刪除已建立的菜單

使用接口建立自定義菜單後,開發者還可以使用接口刪除當前使用的自定義菜單。另請注意,在個性化菜單時,調用此接口會刪除默認菜單及所有個性化菜單。

請求說明

http請求方式:GET
https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN
複製代碼

使用Senparc.Weixin.MP SDK刪除已建立的菜單接口只需一行代碼,以下:

var result = CommonApi.DeleteMenu(accessToken);
複製代碼

五、菜單響應事件的處理

不管是click仍是view,服務器都會收到不一樣的事件響應。不一樣的是,click以後客戶端能夠獲得返回信息,而view在收到請求後,不管返回什麼信息,客戶端都沒法收到(直接打開URL了)。用戶點擊自定義菜單後,微信會把點擊事件推送給開發者,請注意,點擊菜單彈出子菜單,不會產生上報。另外第3個到第8個類型的全部事件,僅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用戶,舊版本微信用戶點擊後將沒有迴應,開發者也不能正常接收到事件推送。

要對菜單響應事件的處理,咱們只須要重寫Senparc.Weixin.MP SDK中的對應事件,具體詳情以下。下面給出了各種型事件處理的參考代碼,具體業務應用能夠就此擴展開來作深刻的應用便可。

5.一、點擊菜單拉取消息時的事件推送

public override IResponseMessageBase OnTextOrEventRequest(RequestMessageText requestMessage)
{
    // 預處理文字或事件類型請求。
    // 這個請求是一個比較特殊的請求,一般用於統一處理來自文字或菜單按鈕的同一個執行邏輯,
    // 會在執行OnTextRequest或OnEventRequest以前觸發,具備如下一些特徵:
    // 一、若是返回null,則繼續執行OnTextRequest或OnEventRequest
    // 二、若是返回不爲null,則終止執行OnTextRequest或OnEventRequest,返回最終ResponseMessage
    // 三、若是是事件,則會將RequestMessageEvent自動轉爲RequestMessageText類型,其中RequestMessageText.Content就是RequestMessageEvent.EventKey

    if (requestMessage.Content == "OneClick")
    {
        var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
        strongResponseMessage.Content = "您點擊了底部按鈕。\r\n爲了測試微信軟件換行bug的應對措施,這裏作了一個——\r\n換行";
        return strongResponseMessage;
    }
    return null;//返回null,則繼續執行OnTextRequest或OnEventRequest
}


public override IResponseMessageBase OnEvent_ClickRequest(RequestMessageEvent_Click requestMessage)
{
    //得到當前公衆號
    WeixinOfficialAccountEntity account = RDIFrameworkService.Instance.WeixinBasicService.GetOfficialAccountEntity(Id);

    IResponseMessageBase reponseMessage = null;
    //菜單點擊,須要跟建立菜單時的Key匹配
    switch (requestMessage.EventKey)
    {
        case "OneClick":
            {
                //這個過程實際已經在OnTextOrEventRequest中完成,這裏不會執行到。
                var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
                reponseMessage = strongResponseMessage;
                strongResponseMessage.Content = "您點擊了底部按鈕。\r\n爲了測試微信軟件換行bug的應對措施,這裏作了一個——\r\n換行";
            }
            break;
        case "SubClickRoot_Text":
            {
                var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
                reponseMessage = strongResponseMessage;
                strongResponseMessage.Content = "您點擊了子菜單按鈕。";
            }
            break;
        case "SubClickRoot_News":
            {
                var strongResponseMessage = CreateResponseMessage<ResponseMessageNews>();
                reponseMessage = strongResponseMessage;
                strongResponseMessage.Articles.Add(new Article()
                {
                    Title = "您點擊了子菜單圖文按鈕",
                    Description = "您點擊了子菜單圖文按鈕,這是一條圖文信息。",
                    PicUrl = "http://www.rdiframework.net/img/weixing-ma.png",
                    Url = "http://www.rdiframework.net/"
                });
            }
            break;
        case "SubClickRoot_Music":
            {
                //上傳縮略圖
                var uploadResult = MediaApi.UploadTemporaryMedia(account.AccessToken, UploadMediaFileType.image,Server.GetMapPath("~/Content/Images/weixing-ma.png"));
                //設置音樂信息
                var strongResponseMessage = CreateResponseMessage<ResponseMessageMusic>();
                reponseMessage = strongResponseMessage;
                strongResponseMessage.Music.Title = "天籟之音";
                strongResponseMessage.Music.Description = "真的是天籟之音";
                strongResponseMessage.Music.MusicUrl = "http://www.rdiframework.net/resource/music/music1.mp3";
                strongResponseMessage.Music.HQMusicUrl = "http://www.rdiframework.net/resource/music/music1.mp3";
                strongResponseMessage.Music.ThumbMediaId = uploadResult.media_id;
            }
            break;
        case "SubClickRoot_Image":
            {
                //上傳圖片
                var uploadResult = MediaApi.UploadTemporaryMedia(account.AccessToken, UploadMediaFileType.image, Server.GetMapPath("~/Content/Images/weixing-ma.png"));
                //設置圖片信息
                var strongResponseMessage = CreateResponseMessage<ResponseMessageImage>();
                reponseMessage = strongResponseMessage;
                strongResponseMessage.Image.MediaId = uploadResult.media_id;
            }
            break;	       
        default:
            {
                var strongResponseMessage = CreateResponseMessage<ResponseMessageText>();
                strongResponseMessage.Content = "您點擊了按鈕,EventKey:" + requestMessage.EventKey;
                reponseMessage = strongResponseMessage;
            }
            break;
    }

    return reponseMessage;
}
複製代碼

在4.2小節咱們建立的菜單中,咱們單擊「返回圖文」二級菜單項,因爲咱們爲這個菜單項定義的KEY爲「SubClickRoot_News」,以下圖的調試狀態所示。

返回圖文事件調試狀態

咱們返回到微信查看一下運行效果,能夠看到咱們本身的服務器根據定義已經正確的返回了圖文信息給咱們,其餘的菜單事件操做相似。

返回圖文事件運行效果

又如咱們返回一首音樂,還能夠直接播放返回的音樂,以下圖所示。

返回音樂事件運行效果

音樂事件播放效果

5.二、擊菜單跳轉連接時的事件推送

public override IResponseMessageBase OnEvent_ViewRequest(RequestMessageEvent_View requestMessage)
{
   //說明:這條消息只做爲接收,下面的responseMessage到達不了客戶端,相似OnEvent_UnsubscribeRequest
   var responseMessage = CreateResponseMessage<ResponseMessageText>();
   responseMessage.Content = "您點擊了view按鈕,將打開網頁:" + requestMessage.EventKey;
   return responseMessage;
}
複製代碼

5.三、掃碼推事件的事件推送

/// <summary>
/// 事件之掃碼推事件(scancode_push)
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_ScancodePushRequest(RequestMessageEvent_Scancode_Push requestMessage)
{
    var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
    responseMessage.Content = "事件之掃碼推事件";
    return responseMessage;
}
複製代碼

5.四、掃碼推事件且彈出「消息接收中」提示框的事件推送

/// <summary>
/// 事件之掃碼推事件且彈出「消息接收中」提示框(scancode_waitmsg)
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_ScancodeWaitmsgRequest(RequestMessageEvent_Scancode_Waitmsg requestMessage)
{
    var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
    responseMessage.Content = "事件之掃碼推事件且彈出「消息接收中」提示框";
    return responseMessage;
}
複製代碼

5.五、彈出系統拍照發圖的事件推送

/// <summary>
/// 事件之彈出系統拍照發圖(pic_sysphoto)
/// 實際測試時發現微信並無推送RequestMessageEvent_Pic_Sysphoto消息,只能接收到用戶在微信中發送的圖片消息。
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_PicSysphotoRequest(RequestMessageEvent_Pic_Sysphoto requestMessage)
{
    var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
    responseMessage.Content = "事件之彈出系統拍照發圖";
    return responseMessage;
}
複製代碼

5.六、彈出拍照或者相冊發圖的事件推送

/// <summary>
/// 事件之彈出拍照或者相冊發圖(pic_photo_or_album)
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_PicPhotoOrAlbumRequest(RequestMessageEvent_Pic_Photo_Or_Album requestMessage)
{
   var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
   responseMessage.Content = "事件之彈出拍照或者相冊發圖";
   return responseMessage;
}
複製代碼

5.七、彈出微信相冊發圖器的事件推送

/// <summary>
/// 事件之彈出微信相冊發圖器(pic_weixin)
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_PicWeixinRequest(RequestMessageEvent_Pic_Weixin requestMessage)
{
    var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
    responseMessage.Content = "事件之彈出微信相冊發圖器";
    return responseMessage;
}
複製代碼

5.八、彈出地理位置選擇器的事件推送

/// <summary>
/// 事件之彈出地理位置選擇器(location_select)
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase OnEvent_LocationSelectRequest(RequestMessageEvent_Location_Select requestMessage)
{
    var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
    responseMessage.Content = "事件之彈出地理位置選擇器";
    return responseMessage;
}
複製代碼

六、對菜單應用的整合界面參考

實際應用中菜單隨時均可能在變化,咱們的最終使用者是不可能用編碼的方式去對菜單進行處理的。這時就須要一個集中的界面可對菜單的集中配置。咱們開發了菜單的應用參考界面,以下圖所示。

微信菜單定義界面整合

在上圖中,咱們經過「獲取菜單」按鈕能夠獲得當前已經定義好的菜單及菜單的事件響應的處理,咱們對微信常規菜單以及個性化菜單的定義都進行了處理。各菜單的定義徹底按照微信的要求標準進行了處理。對於界面右側的「按鈕其餘參數」的的設置,針對不一樣的類型分別進行了處理。對於菜單的修改,直接單擊「更新到服務器」便可完成同步,很是的方便。

參考文章

微信公衆平臺技術文檔-官方

Senparc.Weixin SDK + 官網示例源代碼

RDIFramework.NET — 基於.NET的快速信息化系統開發框架 — 系列目錄

RDIFramework.NET ━ .NET快速信息化系統開發框架 ━ 工做流程組件介紹

RDIFramework.NET框架SOA解決方案(集Windows服務、WinForm形式與IIS形式發佈)-分佈式應用

RDIFramework.NET代碼生成器全新V3.5版本發佈-重大升級


一路走來數個年頭,感謝RDIFramework.NET框架的支持者與使用者,你們能夠經過下面的地址瞭解詳情。

RDIFramework.NET官方網站:www.rdiframework.net/

RDIFramework.NET官方博客:blog.rdiframework.net/

同時須要說明的,之後的全部技術文章以官方網站爲準,歡迎你們收藏!

RDIFramework.NET框架由專業團隊長期打造、一直在更新、一直在升級,請放心使用!

歡迎關注RDIFramework.net框架官方公衆微信(微信號:guosisoft),及時瞭解最新動態。

掃描二維碼當即關注

微信號:guosisoft
相關文章
相關標籤/搜索