微信公眾平臺開發(fā)教程(五)詳解自定義菜單
一、概述:
如果只有輸入框,可能太簡單,感覺像命令行。自定義菜單,給我們提供了很大的靈活性,更符合用戶的操作習慣。在一個小小的微信對話頁面,可以實現(xiàn)更多的功能。菜單直觀明了,不僅能提供事件響應,還支持URL跳轉(zhuǎn),如果需要的功能比較復雜,我們大可以使用URL跳轉(zhuǎn),跳轉(zhuǎn)至我們的網(wǎng)頁即可。
注意:自定義菜單,只有服務號才有此功能
接著我們詳細介紹,如何實現(xiàn)自定義菜單?
二、詳細步驟:
1、首先獲取access_token
access_token是公眾號的全局唯一票據(jù),公眾號調(diào)用各接口時都需使用access_token。正常情況下access_token有效期為7200秒,重復獲取將導致上次獲取的access_token失效。
公眾號可以使用AppID和AppSecret調(diào)用本接口來獲取access_token。AppID和AppSecret可在開發(fā)模式中獲得(需要已經(jīng)成為開發(fā)者,且?guī)ぬ枦]有異常狀態(tài))。注意調(diào)用所有微信接口時均需使用https協(xié)議。
接口調(diào)用請求說明
http請求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
參數(shù)說明
參數(shù) | 是否必須 | 說明 |
---|---|---|
grant_type | 是 | 獲取access_token填寫client_credential |
appid | 是 | 第三方用戶唯一憑證 |
secret | 是 | 第三方用戶唯一憑證密鑰,既appsecret |
返回說明
正常情況下,微信會返回下述JSON數(shù)據(jù)包給公眾號:
{"access_token":"ACCESS_TOKEN","expires_in":7200}
參數(shù) | 說明 |
---|---|
access_token | 獲取到的憑證 |
expires_in | 憑證有效時間,單位:秒 |
返回說明
正常情況下,微信會返回下述JSON數(shù)據(jù)包給公眾號:
{"access_token":"ACCESS_TOKEN","expires_in":7200}
參數(shù) | 說明 |
---|---|
access_token | 獲取到的憑證 |
expires_in | 憑證有效時間,單位:秒 |
錯誤時微信會返回錯誤碼等信息,JSON數(shù)據(jù)包示例如下(該示例為AppID無效錯誤):
{"errcode":40013,"errmsg":"invalid appid"}
2、創(chuàng)建自定義菜單
自定義菜單能夠幫助公眾號豐富界面,讓用戶更好更快地理解公眾號的功能。開啟自定義菜單后,公眾號界面如圖所示:
目前自定義菜單最多包括3個一級菜單,每個一級菜單最多包含5個二級菜單。一級菜單最多4個漢字,二級菜單最多7個漢字,多出來的部分將會以“...”代替。請注意,創(chuàng)建自定義菜單后,由于微信客戶端緩存,需要24小時微信客戶端才會展現(xiàn)出來。建議測試時可以嘗試取消關注公眾賬號后再次關注,則可以看到創(chuàng)建后的效果。
目前自定義菜單接口可實現(xiàn)兩種類型按鈕,如下:
click:
用戶點擊click類型按鈕后,微信服務器會通過消息接口推送消息類型為event 的結構給開發(fā)者(參考消息接口指南),并且?guī)习粹o中開發(fā)者填寫的key值,開發(fā)者可以通過自定義的key值與用戶進行交互;
view:
用戶點擊view類型按鈕后,微信客戶端將會打開開發(fā)者在按鈕中填寫的url值 (即網(wǎng)頁鏈接),達到打開網(wǎng)頁的目的,建議與網(wǎng)頁授權獲取用戶基本信息接口結合,獲得用戶的登入個人信息。
接口調(diào)用請求說明
http請求方式:POST(請使用https協(xié)議) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
請求示例
{ "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" }] }] }
參數(shù)說明
參數(shù) | 是否必須 | 說明 |
---|---|---|
button | 是 | 一級菜單數(shù)組,個數(shù)應為1~3個 |
sub_button | 否 | 二級菜單數(shù)組,個數(shù)應為1~5個 |
type | 是 | 菜單的響應動作類型,目前有click、view兩種類型 |
name | 是 | 菜單標題,不超過16個字節(jié),子菜單不超過40個字節(jié) |
key | click類型必須 | 菜單KEY值,用于消息接口推送,不超過128字節(jié) |
url | view類型必須 | 網(wǎng)頁鏈接,用戶點擊菜單可打開鏈接,不超過256字節(jié) |
返回結果
正確時的返回JSON數(shù)據(jù)包如下:
{"errcode":0,"errmsg":"ok"}
錯誤時的返回JSON數(shù)據(jù)包如下(示例為無效菜單名長度):
{"errcode":40018,"errmsg":"invalid button name size"}
3、查詢菜單
使用接口創(chuàng)建自定義菜單后,開發(fā)者還可使用接口查詢自定義菜單的結構。
請求說明
http請求方式:GET
https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN
返回說明
對應創(chuàng)建接口,正確的Json返回結果:
{"menu":{"button":[{"type":"click","name":"今日歌曲","key":"V1001_TODAY_MUSIC","sub_button":[]},{"type":"click","name":"歌手簡介","key":"V1001_TODAY_SINGER","sub_button":[]},{"name":"菜單","sub_button":[{"type":"view","name":"搜索","url":"http://www.soso.com/","sub_button":[]},{"type":"view","name":"視頻","url":"http://v.qq.com/","sub_button":[]},{"type":"click","name":"贊一下我們","key":"V1001_GOOD","sub_button":[]}]}]}}
4、刪除菜單
使用接口創(chuàng)建自定義菜單后,開發(fā)者還可使用接口刪除當前使用的自定義菜單。
請求說明
http請求方式:GET
https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN
返回說明
對應創(chuàng)建接口,正確的Json返回結果:
{"errcode":0,"errmsg":"ok"}
5、事件處理
用戶點擊自定義菜單后,如果菜單按鈕設置為click類型,則微信會把此次點擊事件推送給開發(fā)者,注意view類型(跳轉(zhuǎn)到URL)的菜單點擊不會上報。
推送XML數(shù)據(jù)包示例:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[FromUser]]></FromUserName> <CreateTime>123456789</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[CLICK]]></Event> <EventKey><![CDATA[EVENTKEY]]></EventKey> </xml>
參數(shù)說明:
參數(shù) | 描述 |
---|---|
ToUserName | 開發(fā)者微信號 |
FromUserName | 發(fā)送方帳號(一個OpenID) |
CreateTime | 消息創(chuàng)建時間 (整型) |
MsgType | 消息類型,event |
Event | 事件類型,CLICK |
EventKey | 事件KEY值,與自定義菜單接口中KEY值對應 |
三、實例講解
還接著上一篇文章講。微信公眾賬號開發(fā)教程(三) 實例入門:機器人(附源碼)
我們將在上一篇文章基礎上,添加自定義菜單功能。
1、獲取access_token
首先需要得到AppId和AppSecret
當你成為開發(fā)者后,自然能夠在,開發(fā)者模式,便可看到這兩個值,可以重置。
然后調(diào)用按照二.1中所示,進行操作。
注意:access_token有過期時間,如果過期,需要重新獲取。
代碼如下:
private static DateTime GetAccessToken_Time; /// <summary> /// 過期時間為7200秒 /// </summary> private static int Expires_Period = 7200; /// <summary> /// /// </summary> private static string mAccessToken; /// <summary> /// /// </summary> public static string AccessToken { get { //如果為空,或者過期,需要重新獲取 if (string.IsNullOrEmpty(mAccessToken) || HasExpired()) { //獲取 mAccessToken = GetAccessToken(AppID, AppSecret); } return mAccessToken; } } /// <summary> /// /// </summary> /// <param name="appId"></param> /// <param name="appSecret"></param> /// <returns></returns> private static string GetAccessToken(string appId, string appSecret) { string url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", appId, appSecret); string result = HttpUtility.GetData(url); XDocument doc = XmlUtility.ParseJson(result, "root"); XElement root = doc.Root; if (root != null) { XElement access_token = root.Element("access_token"); if (access_token != null) { GetAccessToken_Time = DateTime.Now; if (root.Element("expires_in")!=null) { Expires_Period = int.Parse(root.Element("expires_in").Value); } return access_token.Value; } else { GetAccessToken_Time = DateTime.MinValue; } } return null; } /// <summary> /// 判斷Access_token是否過期 /// </summary> /// <returns>bool</returns> private static bool HasExpired() { if (GetAccessToken_Time != null) { //過期時間,允許有一定的誤差,一分鐘。獲取時間消耗 if (DateTime.Now > GetAccessToken_Time.AddSeconds(Expires_Period).AddSeconds(-60)) { return true; } } return false; }
2、設置菜單
菜單需根據(jù)需要,按照實際要求進行設定。
這里我們提供天氣查詢功能,將常用的城市列出來,點擊即可查詢。
然后還提供了友情鏈接,這里提供了view類型的菜單,直接可以跳轉(zhuǎn)至URL頁面,為跳轉(zhuǎn)做個好的演示。
具體菜單如下:
{ "button": [ { "name": "鏈接", "sub_button": [ { "type": "view", "name": "搜索", "url": "http://www.baidu.com/" }, { "type": "view", "name": "視頻", "url": "http://v.qq.com/" }, { "type": "click", "name": "贊一下我們", "key": "BTN_GOOD" } ] }, { "name": "查詢天氣", "sub_button": [ { "type": "click", "name": "武漢", "key": "BTN_TQ_WUHAN" }, { "type": "click", "name": "上海", "key": "BTN_TQ_SHANGHAI" }, { "type": "click", "name": "北京", "key": "BTN_TQ_BEIJING" } ] }, { "type": "click", "name": "幫助", "key": "BTN_HELP" } ] }
3、管理菜單
因為菜單的變更沒有那么頻繁,因此通過txt文件來設置菜單,并通過管理界面來管理菜單。
主要的管理功能:
1)從文件加載菜單
2)創(chuàng)建菜單。即將菜單通知微信服務端,并更新至微信客戶端
3)查詢菜單。獲取當前系統(tǒng)的菜單。
4)刪除菜單。從微信服務器刪除菜單,也可以刪除后再創(chuàng)建。
實現(xiàn)代碼如下:
public class MenuManager { /// <summary> /// 菜單文件路徑 /// </summary> private static readonly string Menu_Data_Path = System.AppDomain.CurrentDomain.BaseDirectory + "/Data/menu.txt"; /// <summary> /// 獲取菜單 /// </summary> public static string GetMenu() { string url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/get?access_token={0}", Context.AccessToken); return HttpUtility.GetData(url); } /// <summary> /// 創(chuàng)建菜單 /// </summary> public static void CreateMenu(string menu) { string url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}", Context.AccessToken); //string menu = FileUtility.Read(Menu_Data_Path); HttpUtility.SendHttpRequest(url, menu); } /// <summary> /// 刪除菜單 /// </summary> public static void DeleteMenu() { string url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={0}", Context.AccessToken); HttpUtility.GetData(url); } /// <summary> /// 加載菜單 /// </summary> /// <returns></returns> public static string LoadMenu() { return FileUtility.Read(Menu_Data_Path); } }
4、基本方法
上面的代碼,其實我們對一些公共功能做了封裝。如進行get請求、POST提交等操作,讀取文件等。
這里我們提供進行get、Post提交的方法案例代碼,如果使用,建議優(yōu)化。
using System; using System.IO; using System.Net; using System.Text; using System.Web; namespace Yank.WeiXin.Robot.Utility { /// <summary> /// Http幫助類 /// </summary> class HttpUtility { /// <summary> /// 發(fā)送請求 /// </summary> /// <param name="url">Url地址</param> /// <param name="data">數(shù)據(jù)</param> public static string SendHttpRequest(string url, string data) { return SendPostHttpRequest(url,"application/x-www-form-urlencoded",data); } /// <summary> /// /// </summary> /// <param name="url"></param> /// <returns></returns> public static string GetData(string url) { return SendGetHttpRequest(url,"application/x-www-form-urlencoded"); } /// <summary> /// 發(fā)送請求 /// </summary> /// <param name="url">Url地址</param> /// <param name="method">方法(post或get)</param> /// <param name="method">數(shù)據(jù)類型</param> /// <param name="requestData">數(shù)據(jù)</param> public static string SendPostHttpRequest(string url,string contentType,string requestData) { WebRequest request = (WebRequest)HttpWebRequest.Create(url); request.Method = "POST"; byte[] postBytes = null; request.ContentType = contentType; postBytes = Encoding.UTF8.GetBytes(requestData); request.ContentLength = postBytes.Length; using (Stream outstream = request.GetRequestStream()) { outstream.Write(postBytes, 0, postBytes.Length); } string result = string.Empty; using (WebResponse response = request.GetResponse()) { if (response != null) { using (Stream stream = response.GetResponseStream()) { using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) { result = reader.ReadToEnd(); } } } } return result; } /// <summary> /// 發(fā)送請求 /// </summary> /// <param name="url">Url地址</param> /// <param name="method">方法(post或get)</param> /// <param name="method">數(shù)據(jù)類型</param> /// <param name="requestData">數(shù)據(jù)</param> public static string SendGetHttpRequest(string url, string contentType) { WebRequest request = (WebRequest)HttpWebRequest.Create(url); request.Method = "GET"; request.ContentType = contentType; string result = string.Empty; using (WebResponse response = request.GetResponse()) { if (response != null) { using (Stream stream = response.GetResponseStream()) { using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) { result = reader.ReadToEnd(); } } } } return result; } } }
using System; using System.Xml.Linq; using Newtonsoft.Json; namespace Yank.WeiXin.Robot.Utility { class XmlUtility { /// <summary> /// /// </summary> /// <param name="json"></param> /// <param name="rootName"></param> /// <returns></returns> public static XDocument ParseJson(string json,string rootName) { return JsonConvert.DeserializeXNode(json, rootName); } } }
5、事件處理
設置了菜單,這下需要處理事件了。跟我們之前設計ASPX或者WinForm一樣,都要綁定按鈕的事件。這里只是通過XML消息將請求傳遞過來。
通過“2、設置菜單"中具體的菜單內(nèi)容,我們便已經(jīng)知道需要進行哪些事件處理了。對于按鈕類型為view的,無須處理,它會自動跳轉(zhuǎn)至指定url.
需要處理的點擊事件:
1)贊一下
2)查詢某城市的天氣,北京、上海、武漢
3)幫助
這個還要沿用上章中的事件處理器EventHandler來擴展處理。
具體的實現(xiàn)代碼吧:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Yank.WeiXin.Robot.Messages; namespace Yank.WeiXin.Robot.Handlers { class EventHandler : IHandler { /// <summary> /// 請求的xml /// </summary> private string RequestXml { get; set; } /// <summary> /// 構造函數(shù) /// </summary> /// <param name="requestXml"></param> public EventHandler(string requestXml) { this.RequestXml = requestXml; } /// <summary> /// 處理請求 /// </summary> /// <returns></returns> public string HandleRequest() { string response = string.Empty; EventMessage em = EventMessage.LoadFromXml(RequestXml); if (em != null) { switch (em.Event.ToLower()) { case ("subscribe"): response = SubscribeEventHandler(em); break; case "click": response = ClickEventHandler(em); break; } } return response; } /// <summary> /// 關注 /// </summary> /// <param name="em"></param> /// <returns></returns> private string SubscribeEventHandler(EventMessage em) { //回復歡迎消息 TextMessage tm = new TextMessage(); tm.ToUserName = em.FromUserName; tm.FromUserName = em.ToUserName; tm.CreateTime = Common.GetNowTime(); tm.Content = "歡迎您關注***,我是大哥大,有事就問我,呵呵!\n\n"; return tm.GenerateContent(); } /// <summary> /// 處理點擊事件 /// </summary> /// <param name="em"></param> /// <returns></returns> private string ClickEventHandler(EventMessage em) { string result = string.Empty; if (em != null && em.EventKey != null) { switch (em.EventKey.ToUpper()) { case "BTN_GOOD": result = btnGoodClick(em); break; case "BTN_TQ_BEIJING": result = searchWeather("beijing", em); break; case "BTN_TQ_SHANGHAI": result = searchWeather("shanghai", em); break; case "BTN_TQ_WUHAN": result = searchWeather("wuhai", em); break; case "BTN_HELP": result = btnHelpClick(em); break; } } return result; } /// <summary> /// 贊一下 /// </summary> /// <param name="em"></param> /// <returns></returns> private string btnGoodClick(EventMessage em) { //回復歡迎消息 TextMessage tm = new TextMessage(); tm.ToUserName = em.FromUserName; tm.FromUserName = em.ToUserName; tm.CreateTime = Common.GetNowTime(); tm.Content = @"謝謝您的支持!"; return tm.GenerateContent(); } /// <summary> /// 幫助 /// </summary> /// <param name="em"></param> /// <returns></returns> private string btnHelpClick(EventMessage em) { //回復歡迎消息 TextMessage tm = new TextMessage(); tm.ToUserName = em.FromUserName; tm.FromUserName = em.ToUserName; tm.CreateTime = Common.GetNowTime(); tm.Content = @"查詢天氣,輸入tq 城市名稱\拼音\首字母"; return tm.GenerateContent(); } /// <summary> /// 查詢天氣 /// </summary> /// <param name="cityName"></param> /// <param name="em"></param> /// <returns></returns> private string searchWeather(string cityName, EventMessage em) { TextMessage tm = new TextMessage(); tm.Content = WeatherHelper.GetWeather(cityName); //進行發(fā)送者、接收者轉(zhuǎn)換 tm.ToUserName = em.FromUserName; tm.FromUserName = em.ToUserName; tm.CreateTime = Common.GetNowTime(); return tm.GenerateContent(); } } }
6、效果圖
終于大工告成,最后來看下效果圖吧
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
JavaScript實現(xiàn)url參數(shù)轉(zhuǎn)成json形式
這篇文章主要介紹了JavaScript實現(xiàn)url參數(shù)轉(zhuǎn)成json形式的相關代碼,有喜歡的小伙伴可以參考下2016-09-09JavaScript如何監(jiān)測數(shù)組的變化
最近在造輪子的時候遇到了這么一個問題,那就是數(shù)組在調(diào)用內(nèi)部方法的時候怎么才可以監(jiān)聽到數(shù)組發(fā)生了變化,這篇文章主要給大家介紹了關于JavaScript如何監(jiān)測數(shù)組變化的相關資料,需要的朋友可以參考下2021-07-07