微信公众账号开发教程(四)自定义菜单(含实例源码)——转自http://www.cnblogs.com/yank/p/3418194.html

微信公众账号开发教程(四)自定义菜单

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

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

一、概述:

如果只有输入框,可能太简单,感觉像命令行。自定义菜单,给我们提供了很大的灵活性,更符合用户的操作习惯。在一个小小的微信对话页面,可以实现更多的功能。菜单直观明了,不仅能提供事件响应,还支持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": "view", "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、效果图

终于大工告成,最后来看下效果图吧

posted on 2013-12-01 00:09 向北方 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/China3S/p/3451998.html

微信公众账号开发教程(四)自定义菜单(含实例源码)——转自http://www.cnblogs.com/yank/p/3418194.html...相关推荐

  1. 微信公众号开发教程(四)自定义菜单

    作者:陈惠,叩丁狼教育高级讲师.原创文章,转载请注明出处. 本篇文章,我们来做一个最常见的,也是用户最喜欢使用的功能--自定义菜单. 因为菜单只需要点一下就可以获取需要的信息,无需用户手动输入关键字, ...

  2. 微信公众号开发教程[011]-自定义菜单以及个性化菜单

    自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单.类型分别有click,view,scancode_push,scancode_waitmsg,pic_sysphoto,pic_pho ...

  3. java 微信自定义菜单开发_微信公众号开发之设置自定义菜单实例代码【java版】...

    本实例是为了实现在管理后台实现微信菜单的添加删除管理. 1.首先我们需要新建一个数据库表用于存放menu菜单项 可包含的字段有id.父类id.name.排序.是否显示.类型(view.click).链 ...

  4. 微信公众平台开发教程第19篇-应用实例之人脸检测

    人脸检测属于人脸识别的范畴,它是一个复杂的具有挑战性的模式匹配问题,国内外许多组织.科研机构都在专门研究该问题.国内的Face++团队专注于研发人脸检测.识别.分析和重建技术,并且向广大开发者开放了人 ...

  5. 微信公众账号开发教程之微信浏览器HTTP_USER_AGENT判断

    在微信公众平台的开发过程中,我们有时需要开发网页并判断是否是是来自微信浏览器访问,本文介绍如何做出这一判断. 一.$_SERVER数组 $_SERVER 是一个包含了诸如头信息(header).路径( ...

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

    请尊重作者版权,如需转载,请标明出处. 应大家强烈要求,将自定义菜单功能课程提前. 一.概述: 如果只有输入框,可能太简单,感觉像命令行.自定义菜单,给我们提供了很大的灵活性,更符合用户的操作习惯.在 ...

  7. 微信公众平台开发教程--自定义菜单

    微信公众平台开发教程(五)自定义菜单 请尊重作者版权,如需转载,请标明出处. 应大家强烈要求,将自定义菜单功能课程提前. 一.概述: 如果只有输入框,可能太简单,感觉像命令行.自定义菜单,给我们提供了 ...

  8. 微信公众平台开发教程(四)

    微信公众平台开发教程(五)自定义菜单 请尊重作者版权,如需转载,请标明出处. 应大家强烈要求,将自定义菜单功能课程提前. 一.概述: 如果只有输入框,可能太简单,感觉像命令行.自定义菜单,给我们提供了 ...

  9. 微信公众平台开发教程(四) 实例入门:机器人(附源码)

    微信公众平台开发教程(四) 实例入门:机器人(附源码) 上一篇文章,写了基本框架,可能很多人会觉得晕头转向,这里提供一个简单的例子来予以说明,希望能帮你解开谜团. 一.功能介绍 通过微信公众平台实现在 ...

  10. 微信公众平台开发教程目录

    1.微信公众平台开发教程(一) 微信公众账号注册流程 2.微信公众平台开发教程(二) 基本原理及消息接口 3.微信公众账号开发教程(三) 基础框架搭建 4.微信公众平台开发教程(四) 实例入门:机器人 ...

最新文章

  1. 用Java Instrumentation 在类加载时添加记录
  2. Django系列教程:三、动态视图和动态Url
  3. 包头昆区多大面积_两套楼房,放了整整19车垃圾!包头这对母女的“特殊癖好”,让邻居彻底不能忍了.........
  4. openstack中的server
  5. 《中国人工智能学会通讯》——4.7 当前研究热点
  6. 计算机是一种在什么控制下 自动高速,计算机是一种能对数字化信息进行自动高速运算的通用处....ppt...
  7. R学习-- 数组和矩阵
  8. Spring Boot 使用模板引擎
  9. 字符串里面的单词反转
  10. vaniglia 源码学习(一)
  11. 福利来了,axure8.1注册码
  12. 【WPF】命令 --《深入浅出WPF》
  13. EXCEL中汉字转变拼音的技巧~代码实现
  14. 这个轮子让SpringBoot实现api加密So Easy!
  15. js高级面试题总结(es6)
  16. 升级电脑系统或者重装WIN10后,PADS软件卡死
  17. Oier们的幸运数字
  18. cocos2d-x的初步学习二十之坦克大战七
  19. Failed to declare queue(s):[XXX]问题解决
  20. web中的$多种意思

热门文章

  1. 怎么用计算机技术预测蛋白质结构,如何利用同源建模预测蛋白质三级结构
  2. 蚂蚁金服 Service Mesh 技术风险思考和实践
  3. 同一网络俩台计算机连接,两台电脑如何连接在同一个局域网呢
  4. 摘要认证 java_摘要认证及实现HTTP digest authentication
  5. html基本标记练习钱塘湖春行,《钱塘湖春行》练习题及答案
  6. STM32驱动模数转换芯片ADS1120(PT100铂电阻测温度)第2篇
  7. 【经典产品思维】引领“用户消费”的产品怎么做?
  8. Maven常用命令_飘云羽逸_新浪博客
  9. 史上最全Unity3D游戏开发教程,从入门到精通(含学习路线图)
  10. LOJ-10105(欧拉回路模板,套圈法,递归)