博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
微信公众平台开发教程(五)自定义菜单
阅读量:6709 次
发布时间:2019-06-25

本文共 15676 字,大约阅读时间需要 52 分钟。

微信公众平台开发教程(五)自定义菜单

请尊重作者版权,如需转载,请标明出处。

应大家强烈要求,将自定义菜单功能课程提前。

一、概述:

如果只有输入框,可能太简单,感觉像命令行。自定义菜单,给我们提供了很大的灵活性,更符合用户的操作习惯。在一个小小的微信对话页面,可以实现更多的功能。菜单直观明了,不仅能提供事件响应,还支持URL跳转,如果需要的功能比较复杂,我们大可以使用URL跳转,跳转至我们的网页即可。

注意:自定义菜单,只有服务号才有此功能

如何注册,见第一章:

效果如下,

 

 

接着我们详细介绍,如何实现自定义菜单?

二、详细步骤:

1、首先获取access_token 

access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。正常情况下access_token有效期为7200秒,重复获取将导致上次获取的access_token失效。

公众号可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在开发模式中获得(需要已经成为开发者,且帐号没有异常状态)。注意调用所有微信接口时均需使用https协议。

接口调用请求说明

http请求方式: GET

参数说明

参数 是否必须 说明
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协议) 

请求示例

{     "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

返回说明

对应创建接口,正确的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":"","sub_button":[]},{"type":"view","name":"视频","url":"","sub_button":[]},{"type":"click","name":"赞一下我们","key":"V1001_GOOD","sub_button":[]}]}]}}

4、删除菜单

使用接口创建自定义菜单后,开发者还可使用接口删除当前使用的自定义菜单。

请求说明

http请求方式:GET

返回说明

对应创建接口,正确的Json返回结果:{"errcode":0,"errmsg":"ok"}

5、事件处理

用户点击自定义菜单后,如果菜单按钮设置为click类型,则微信会把此次点击事件推送给开发者,注意view类型(跳转到URL)的菜单点击不会上报。

推送XML数据包示例:

123456789

参数说明:

 

参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,event
Event 事件类型,CLICK
EventKey 事件KEY值,与自定义菜单接口中KEY值对应

三、实例讲解

还接着上一篇文章讲。

我们将在上一篇文章基础上,添加自定义菜单功能。

1、获取access_token

首先需要得到AppId和AppSecret

当你成为开发者后,自然能够在,开发者模式,便可看到这两个值,可以重置。

然后调用按照二.1中所示,进行操作。

注意:access_token有过期时间,如果过期,需要重新获取。

代码如下:

private static DateTime GetAccessToken_Time;        ///         /// 过期时间为7200秒        ///         private static int Expires_Period = 7200;        ///         ///         ///         private static string mAccessToken;        ///         ///         ///         public static string AccessToken        {            get            {                //如果为空,或者过期,需要重新获取                if (string.IsNullOrEmpty(mAccessToken) || HasExpired())                {                    //获取                    mAccessToken = GetAccessToken(AppID, AppSecret);                }                return mAccessToken;            }        }        ///         ///         ///         ///         ///         /// 
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; } /// /// 判断Access_token是否过期 /// ///
bool
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    {        ///         /// 菜单文件路径        ///         private static readonly string Menu_Data_Path = System.AppDomain.CurrentDomain.BaseDirectory + "/Data/menu.txt";        ///         /// 获取菜单        ///         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);        }        ///         /// 创建菜单        ///         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);        }        ///         /// 删除菜单        ///         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);        }        ///         /// 加载菜单        ///         /// 
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{    ///     /// Http帮助类    ///     class HttpUtility    {        ///         /// 发送请求        ///         /// Url地址        /// 数据        public static string SendHttpRequest(string url, string data)        {            return SendPostHttpRequest(url,"application/x-www-form-urlencoded",data);         }        ///         ///         ///         ///         /// 
public static string GetData(string url) { return SendGetHttpRequest(url,"application/x-www-form-urlencoded"); } /// /// 发送请求 /// /// Url地址 /// 方法(post或get) /// 数据类型 /// 数据 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; } /// /// 发送请求 /// /// Url地址 /// 方法(post或get) /// 数据类型 /// 数据 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    {        ///         ///         ///         ///         ///         /// 
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    {        ///         /// 请求的xml        ///         private string RequestXml { get; set; }        ///         /// 构造函数        ///         ///         public EventHandler(string requestXml)        {            this.RequestXml = requestXml;        }        ///         /// 处理请求        ///         /// 
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; } /// /// 关注 /// /// ///
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(); } /// /// 处理点击事件 /// /// ///
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; } /// /// 赞一下 /// /// ///
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(); } /// /// 帮助 /// /// ///
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(); } /// /// 查询天气 /// /// /// ///
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.5 习题解答
查看>>
积水成渊——数据中心用水效率分析
查看>>
重新定义云数据库 阿里云POLARDB 9月21日发布
查看>>
物联网安全威胁剧增 如何拓展移动化能力
查看>>
工业物联网:创造价值 转换业务模式
查看>>
思科若要加入超融合大战:需启用你的现金
查看>>
程序员如何既不耽误工作又有时间干业余项目?
查看>>
王胤:我是怎么把体温计变成助孕计的
查看>>
Linux下如何定制SSH来简化远程访问
查看>>
空与非空 EMPTY_LOB和NULL的区别
查看>>
未来的主角是公有云还是私有云?哪些云安全企业能在行业洗牌中脱颖而出
查看>>
可能吞噬硬件的无服务器云
查看>>
如何自行搭建一个威胁感知大脑 SIEM?| 硬创公开课
查看>>
安全圈老司机为什么会在这个游戏里翻车?(内附详细解谜攻略)
查看>>
大数据将带来哪些“健康红利”?
查看>>
技术派的梦想旅行,用大数据推动旅游2.0时代到来
查看>>
高校 WiFi 9 大谬论
查看>>
CyrusOne计划在美国德克萨斯建设大型数据中心园区
查看>>
暴风热点 要的不仅仅是免费WIFI
查看>>
MSR路由器的未来之路
查看>>