程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> 微信公眾平台開發教程(五)自定義菜單,公眾自定義

微信公眾平台開發教程(五)自定義菜單,公眾自定義

編輯:關於PHP編程

微信公眾平台開發教程(五)自定義菜單,公眾自定義


 

應大家強烈要求,將自定義菜單功能課程提前。

一、概述:

如果只有輸入框,可能太簡單,感覺像命令行。自定義菜單,給我們提供了很大的靈活性,更符合用戶的操作習慣。在一個小小的微信對話頁面,可以實現更多的功能。菜單直觀明了,不僅能提供事件響應,還支持URL跳轉,如果需要的功能比較復雜,我們大可以使用URL跳轉,跳轉至我們的網頁即可。

注意:自定義菜單,只有服務號才有此功能

如何注冊,見第一章:微信公眾賬號開發教程(一) 基本原理及微信公眾賬號注冊

效果如下,

 

 

接著我們詳細介紹,如何實現自定義菜單?

二、詳細步驟:

1、首先獲取access_token 

access_token是公眾號的全局唯一票據,公眾號調用各接口時都需使用access_token。正常情況下access_token有效期為7200秒,重復獲取將導致上次獲取的access_token失效。

公眾號可以使用AppID和AppSecret調用本接口來獲取access_token。AppID和AppSecret可在開發模式中獲得(需要已經成為開發者,且帳號沒有異常狀態)。注意調用所有微信接口時均需使用https協議。

接口調用請求說明

http請求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

參數說明

參數是否必須說明 grant_type 是 獲取access_token填寫client_credential appid 是 第三方用戶唯一憑證 secret 是 第三方用戶唯一憑證密鑰,既appsecret

返回說明

正常情況下,微信會返回下述JSON數據包給公眾號:

{"access_token":"ACCESS_TOKEN","expires_in":7200}
參數說明 access_token 獲取到的憑證 expires_in 憑證有效時間,單位:秒

錯誤時微信會返回錯誤碼等信息,JSON數據包示例如下(該示例為AppID無效錯誤):

{"errcode":40013,"errmsg":"invalid appid"} 

2、創建自定義菜單

自定義菜單能夠幫助公眾號豐富界面,讓用戶更好更快地理解公眾號的功能。開啟自定義菜單後,公眾號界面如圖所示:

 

目前自定義菜單最多包括3個一級菜單,每個一級菜單最多包含5個二級菜單。一級菜單最多4個漢字,二級菜單最多7個漢字,多出來的部分將會以“...”代替。請注意,創建自定義菜單後,由於微信客戶端緩存,需要24小時微信客戶端才會展現出來。建議測試時可以嘗試取消關注公眾賬號後再次關注,則可以看到創建後的效果。

目前自定義菜單接口可實現兩種類型按鈕,如下:

click:
用戶點擊click類型按鈕後,微信服務器會通過消息接口推送消息類型為event	的結構給開發者(參考消息接口指南),並且帶上按鈕中開發者填寫的key值,開發者可以通過自定義的key值與用戶進行交互;
view:
用戶點擊view類型按鈕後,微信客戶端將會打開開發者在按鈕中填寫的url值	(即網頁鏈接),達到打開網頁的目的,建議與網頁授權獲取用戶基本信息接口結合,獲得用戶的登入個人信息。


接口調用請求說明

http請求方式:POST(請使用https協議) 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" }] }] }
復制代碼

 

參數說明

參數是否必須說明 button 是 一級菜單數組,個數應為1~3個 sub_button 否 二級菜單數組,個數應為1~5個 type 是 菜單的響應動作類型,目前有click、view兩種類型 name 是 菜單標題,不超過16個字節,子菜單不超過40個字節 key click類型必須 菜單KEY值,用於消息接口推送,不超過128字節 url view類型必須 網頁鏈接,用戶點擊菜單可打開鏈接,不超過256字節


返回結果

正確時的返回JSON數據包如下:

{"errcode":0,"errmsg":"ok"}

錯誤時的返回JSON數據包如下(示例為無效菜單名長度):

{"errcode":40018,"errmsg":"invalid button name size"}

3、查詢菜單

使用接口創建自定義菜單後,開發者還可使用接口查詢自定義菜單的結構。

請求說明

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

返回說明

對應創建接口,正確的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、刪除菜單

使用接口創建自定義菜單後,開發者還可使用接口刪除當前使用的自定義菜單。

請求說明

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

返回說明

對應創建接口,正確的Json返回結果:
{"errcode":0,"errmsg":"ok"}

5、事件處理

用戶點擊自定義菜單後,如果菜單按鈕設置為click類型,則微信會把此次點擊事件推送給開發者,注意view類型(跳轉到URL)的菜單點擊不會上報。

推送XML數據包示例:

復制代碼
<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>
復制代碼

參數說明:

 

參數描述 ToUserName 開發者微信號 FromUserName 發送方帳號(一個OpenID) CreateTime 消息創建時間 (整型) MsgType 消息類型,event Event 事件類型,CLICK EventKey 事件KEY值,與自定義菜單接口中KEY值對應

三、實例講解

還接著上一篇文章講。微信公眾賬號開發教程(三) 實例入門:機器人(附源碼)

我們將在上一篇文章基礎上,添加自定義菜單功能。

1、獲取access_token

首先需要得到AppId和AppSecret

當你成為開發者後,自然能夠在,開發者模式,便可看到這兩個值,可以重置。

然後調用按照二.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、設置菜單

菜單需根據需要,按照實際要求進行設定。

這裡我們提供天氣查詢功能,將常用的城市列出來,點擊即可查詢。

然後還提供了友情鏈接,這裡提供了view類型的菜單,直接可以跳轉至URL頁面,為跳轉做個好的演示。

具體菜單如下:

復制代碼
{
    "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)創建菜單。即將菜單通知微信服務端,並更新至微信客戶端

3)查詢菜單。獲取當前系統的菜單。

4)刪除菜單。從微信服務器刪除菜單,也可以刪除後再創建。 

實現代碼如下:

復制代碼
    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>
        /// 創建菜單
        /// </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提交的方法案例代碼,如果使用,建議優化。

復制代碼
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>
        /// 發送請求
        /// </summary>
        /// <param name="url">Url地址</param>
        /// <param name="data">數據</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>
        /// 發送請求
        /// </summary>
        /// <param name="url">Url地址</param>
        /// <param name="method">方法(post或get)</param>
        /// <param name="method">數據類型</param>
        /// <param name="requestData">數據</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>
        /// 發送請求
        /// </summary>
        /// <param name="url">Url地址</param>
        /// <param name="method">方法(post或get)</param>
        /// <param name="method">數據類型</param>
        /// <param name="requestData">數據</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、設置菜單"中具體的菜單內容,我們便已經知道需要進行哪些事件處理了。對於按鈕類型為view的,無須處理,它會自動跳轉至指定url.

需要處理的點擊事件:

1)贊一下

2)查詢某城市的天氣,北京、上海、武漢

3)幫助

這個還要沿用上章中的事件處理器EventHandler來擴展處理。

具體的實現代碼吧:

復制代碼
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>
        /// 構造函數
        /// </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);
            //進行發送者、接收者轉換
            tm.ToUserName = em.FromUserName;
            tm.FromUserName = em.ToUserName;
            tm.CreateTime = Common.GetNowTime();
            return tm.GenerateContent();
        }
    }
}
復制代碼

  

6、效果圖

終於大工告成,最後來看下效果圖吧

 

注意:

作者停留的風版權,如需轉載,請標明出處。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved