前面几篇继续了我自己对于C#开发微信门户及应用的技术探索和相关的经验总结,继续探索微信API并分享相关的技术,一方面是为了和大家对这方面进行互动沟通,另一方面也是专心做好微信应用的底层技术开发,把基础模块夯实,在未来的应用中派上用途。本随笔继续介绍微信门户菜单的管理操作。

1、菜单的基础信息

微信门户的菜单,一般服务号和订阅号都可以拥有这个模块的开发,但是订阅号好像需要认证后才能拥有,而服务号则不需要认证就可以拥有了。这个菜单可以有编辑模式和开发模式,编辑模式主要就是在微信门户的平台上,对菜单进行编辑;而开发模式,就是用户可以通过调用微信的API对菜单进行定制开发,通过POST数据到微信服务器,从而生成对应的菜单内容。本文主要介绍基于开发模式的菜单管理操作。

自定义菜单能够帮助公众号丰富界面,让用户更好更快地理解公众号的功能。目前自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。目前自定义菜单接口可实现两种类型按钮,如下:

  1. click
  2. 用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event 的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;
  3. view
  4. 用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的url (即网页链接),达到打开网页的目的,建议与网页授权获取用户基本信息接口结合,获得用户的登入个人信息。

菜单提交的数据,本身是一个Json的数据字符串,它的官方例子数据如下所示。

  1. {
  2. "button":[
  3. {
  4. "type":"click",
  5. "name":"今日歌曲",
  6. "key":"V1001_TODAY_MUSIC"
  7. },
  8. {
  9. "type":"click",
  10. "name":"歌手简介",
  11. "key":"V1001_TODAY_SINGER"
  12. },
  13. {
  14. "name":"菜单",
  15. "sub_button":[
  16. {
  17. "type":"view",
  18. "name":"搜索",
  19. "url":"http://www.soso.com/"
  20. },
  21. {
  22. "type":"view",
  23. "name":"视频",
  24. "url":"http://v.qq.com/"
  25. },
  26. {
  27. "type":"click",
  28. "name":"赞一下我们",
  29. "key":"V1001_GOOD"
  30. }]
  31. }]
  32. }

从上面我们可以看到,菜单不同的type类型,有不同的字段内容,如type为view的有url属性,而type为click的,则有key属性。而菜单可以有子菜单sub_button属性,总得来说,为了构造好对应的菜单实体类信息,不是一下就能分析的出来。

2、菜单的实体类定义

我看过一些微信接口的开发代码,把菜单的分为了好多个实体类,指定了继承关系,然后分别对他们进行属性的配置,大概的关系如下所示。

这种多层关系的继承方式能解决问题,不过我觉得并不是优雅的解决方案。其实结合Json.NET自身的Attribute属性配置,可以指定那些为空的内容在序列号为Json字符串的时候,不显示出来的。

  1. [JsonProperty( NullValueHandling = NullValueHandling.Ignore)]

有了这个属性,我们就可以统一定义菜单的实体类信息更多的属性了,可以把View类型和Click类型的菜单属性的url和key合并在一起。

  1. /// <summary>
  2. /// 菜单基本信息
  3. /// </summary>
  4. public class MenuInfo
  5. {
  6. /// <summary>
  7. /// 按钮描述,既按钮名字,不超过16个字节,子菜单不超过40个字节
  8. /// </summary>
  9. public string name { get; set; }
  10.  
  11. /// <summary>
  12. /// 按钮类型(click或view)
  13. /// </summary>
  14. [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
  15. public string type { get; set; }
  16.  
  17. /// <summary>
  18. /// 按钮KEY值,用于消息接口(event类型)推送,不超过128字节
  19. /// </summary>
  20. [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
  21. public string key { get; set; }
  22.  
  23. /// <summary>
  24. /// 网页链接,用户点击按钮可打开链接,不超过256字节
  25. /// </summary>
  26. [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
  27. public string url { get; set; }
  28.  
  29. /// <summary>
  30. /// 子按钮数组,按钮个数应为2~5个
  31. /// </summary>
  32. [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
  33. public List<MenuInfo> sub_button { get; set; }
  34.  
  35. .......

但是,这么多信息,不同的类型我需要指定不同的属性类型,那不是挺麻烦,万一我在View类型的菜单里面,把key属性设置了,那怎么办?

解决方法就是我们定义几个构造函数,分别用来构造不同的菜单信息,如下所示是对菜单不同的类型,赋值给不同的属性的构造函数。

  1. /// <summary>
  2. /// 参数化构造函数
  3. /// </summary>
  4. /// <param name="name">按钮名称</param>
  5. /// <param name="buttonType">菜单按钮类型</param>
  6. /// <param name="value">按钮的键值(Click),或者连接URL(View)</param>
  7. public MenuInfo(string name, ButtonType buttonType, string value)
  8. {
  9. this.name = name;
  10. this.type = buttonType.ToString();
  11.  
  12. if (buttonType == ButtonType.click)
  13. {
  14. this.key = value;
  15. }
  16. else if(buttonType == ButtonType.view)
  17. {
  18. this.url = value;
  19. }
  20. }

好了,还有另外一个问题,子菜单也就是属性sub_button是可有可无的东西,有的话,需要指定Name属性,并添加它的sub_button集合对象就可以了,那么我们在增加一个构造子菜单的对象信息的构造函数。

  1. /// <summary>
  2. /// 参数化构造函数,用于构造子菜单
  3. /// </summary>
  4. /// <param name="name">按钮名称</param>
  5. /// <param name="sub_button">子菜单集合</param>
  6. public MenuInfo(string name, IEnumerable<MenuInfo> sub_button)
  7. {
  8. this.name = name;
  9. this.sub_button = new List<MenuInfo>();
  10. this.sub_button.AddRange(sub_button);
  11. }

由于只指定Name和sub_button的属性内容,其他内容为null的话,自然构造出来的Json就没有包含它们,非常完美!

为了获取菜单的信息,我们还需要定义两个实体对象,如下所示。

  1. /// <summary>
  2. /// 菜单的Json字符串对象
  3. /// </summary>
  4. public class MenuJson
  5. {
  6. public List<MenuInfo> button { get; set; }
  7.  
  8. public MenuJson()
  9. {
  10. button = new List<MenuInfo>();
  11. }
  12. }
  13.  
  14. /// <summary>
  15. /// 菜单列表的Json对象
  16. /// </summary>
  17. public class MenuListJson
  18. {
  19. public MenuJson menu { get; set; }
  20. }

3、菜单管理操作的接口实现

我们从微信的定义里面,可以看到,我们通过API可以获取菜单信息、创建菜单、删除菜单,那么我们来定义它们的接口如下。

  1. /// <summary>
  2. /// 菜单的相关操作
  3. /// </summary>
  4. public interface IMenuApi
  5. {
  6. /// <summary>
  7. /// 获取菜单数据
  8. /// </summary>
  9. /// <param name="accessToken">调用接口凭证</param>
  10. /// <returns></returns>
  11. MenuJson GetMenu(string accessToken);
  12.  
  13. /// <summary>
  14. /// 创建菜单
  15. /// </summary>
  16. /// <param name="accessToken">调用接口凭证</param>
  17. /// <param name="menuJson">菜单对象</param>
  18. /// <returns></returns>
  19. CommonResult CreateMenu(string accessToken, MenuJson menuJson);
  20.  
  21. /// <summary>
  22. /// 删除菜单
  23. /// </summary>
  24. /// <param name="accessToken">调用接口凭证</param>
  25. /// <returns></returns>
  26. CommonResult DeleteMenu(string accessToken);
  27. }

具体的获取菜单信息的实现如下。

  1. /// <summary>
  2. /// 获取菜单数据
  3. /// </summary>
  4. /// <param name="accessToken">调用接口凭证</param>
  5. /// <returns></returns>
  6. public MenuJson GetMenu(string accessToken)
  7. {
  8. MenuJson menu = null;
  9.  
  10. var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/get?access_token={0}", accessToken);
  11. MenuListJson list = JsonHelper<MenuListJson>.ConvertJson(url);
  12. if (list != null)
  13. {
  14. menu = list.menu;
  15. }
  16. return menu;
  17. }

这里就是把返回的Json数据,统一转换为我们需要的实体信息了,一步到位。

调用代码如下所示。

  1. private void btnGetMenuJson_Click(object sender, EventArgs e)
  2. {
  3. IMenuApi menuBLL = new MenuApi();
  4. MenuJson menu = menuBLL.GetMenu(token);
  5. if (menu != null)
  6. {
  7. Console.WriteLine(menu.ToJson());
  8. }
  9. }

创建和删除菜单对象的操作实现如下所示。

  1. /// <summary>
  2. /// 创建菜单
  3. /// </summary>
  4. /// <param name="accessToken">调用接口凭证</param>
  5. /// <param name="menuJson">菜单对象</param>
  6. /// <returns></returns>
  7. public CommonResult CreateMenu(string accessToken, MenuJson menuJson)
  8. {
  9. var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}", accessToken);
  10. string postData = menuJson.ToJson();
  11.  
  12. return Helper.GetExecuteResult(url, postData);
  13. }
  14.  
  15. /// <summary>
  16. /// 删除菜单
  17. /// </summary>
  18. /// <param name="accessToken">调用接口凭证</param>
  19. /// <returns></returns>
  20. public CommonResult DeleteMenu(string accessToken)
  21. {
  22. var url = string.Format("https://api.weixin.qq.com/cgi-bin/menu/delete?access_token={0}", accessToken);
  23.  
  24. return Helper.GetExecuteResult(url);
  25. }

看到这里,有些人可能会问,实体类你简化了,那么创建菜单是不是挺麻烦的,特别是构造对应的信息应该如何操作呢?前面不是介绍了不同的构造函数了吗,通过他们简单就搞定了,不用记下太多的实体类及它们的继承关系来处理菜单信息。

  1. private void btnCreateMenu_Click(object sender, EventArgs e)
  2. {
  3. MenuInfo productInfo = new MenuInfo("软件产品", new MenuInfo[] {
  4. new MenuInfo("病人资料管理系统", ButtonType.click, "patient"),
  5. new MenuInfo("客户关系管理系统", ButtonType.click, "crm"),
  6. new MenuInfo("酒店管理系统", ButtonType.click, "hotel"),
  7. new MenuInfo("送水管理系统", ButtonType.click, "water")
  8. });
  9.  
  10. MenuInfo frameworkInfo = new MenuInfo("框架产品", new MenuInfo[] {
  11. new MenuInfo("Win开发框架", ButtonType.click, "win"),
  12. new MenuInfo("WCF开发框架", ButtonType.click, "wcf"),
  13. new MenuInfo("混合式框架", ButtonType.click, "mix"),
  14. new MenuInfo("Web开发框架", ButtonType.click, "web"),
  15. new MenuInfo("代码生成工具", ButtonType.click, "database2sharp")
  16. });
  17.  
  18. MenuInfo relatedInfo = new MenuInfo("相关链接", new MenuInfo[] {
  19. new MenuInfo("公司介绍", ButtonType.click, "Event_Company"),
  20. new MenuInfo("官方网站", ButtonType.view, "http://www.iqidi.com"),
  21. new MenuInfo("提点建议", ButtonType.click, "Event_Suggestion"),
  22. new MenuInfo("联系客服", ButtonType.click, "Event_Contact"),
  23. new MenuInfo("发邮件", ButtonType.view, "http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email=S31yfX15fn8LOjplKCQm")
  24. });
  25.  
  26. MenuJson menuJson = new MenuJson();
  27. menuJson.button.AddRange(new MenuInfo[] { productInfo, frameworkInfo, relatedInfo });
  28.  
  29. //Console.WriteLine(menuJson.ToJson());
  30.  
  31. if (MessageUtil.ShowYesNoAndWarning("您确认要创建菜单吗") == System.Windows.Forms.DialogResult.Yes)
  32. {
  33. IMenuApi menuBLL = new MenuApi();
  34. CommonResult result = menuBLL.CreateMenu(token, menuJson);
  35. Console.WriteLine("创建菜单:" + (result.Success ? "成功" : "失败:" + result.ErrorMessage));
  36. }
  37. }

这个就是我微信门户里面的菜单操作了,具体效果可以关注我的微信门户:广州爱奇迪,也可以扫描下面二维码进行关注了解。

菜单的效果如下:

如果对这个系列感兴趣,可以关注我的其他文章,系列随笔如下所示:

C#开发微信门户及应用(25)-微信企业号的客户端管理功能

C#开发微信门户及应用(24)-微信小店货架信息管理

C#开发微信门户及应用(23)-微信小店商品管理接口的封装和测试

C#开发微信门户及应用(22)-微信小店的开发和使用

C#开发微信门户及应用(21)-微信企业号的消息和事件的接收处理及解密

C#开发微信门户及应用(20)-微信企业号的菜单管理

C#开发微信门户及应用(19)-微信企业号的消息发送(文本、图片、文件、语音、视频、图文消息等)

C#开发微信门户及应用(18)-微信企业号的通讯录管理开发之成员管理

C#开发微信门户及应用(17)-微信企业号的通讯录管理开发之部门管理

C#开发微信门户及应用(16)-微信企业号的配置和使用

C#开发微信门户及应用(15)-微信菜单增加扫一扫、发图片、发地理位置功能

C#开发微信门户及应用(14)-在微信菜单中采用重定向获取用户数据

C#开发微信门户及应用(13)-使用地理位置扩展相关应用

C#开发微信门户及应用(12)-使用语音处理

C#开发微信门户及应用(11)--微信菜单的多种表现方式介绍

C#开发微信门户及应用(10)--在管理系统中同步微信用户分组信息

C#开发微信门户及应用(9)-微信门户菜单管理及提交到微信服务器

C#开发微信门户及应用(8)-微信门户应用管理系统功能介绍

C#开发微信门户及应用(7)-微信多客服功能及开发集成

C#开发微信门户及应用(6)--微信门户菜单的管理操作

C#开发微信门户及应用(5)--用户分组信息管理

C#开发微信门户及应用(4)--关注用户列表及详细信息管理

C#开发微信门户及应用(3)--文本消息和图文消息的应答

C#开发微信门户及应用(2)--微信消息的处理和应答

C#开发微信门户及应用(1)--开始使用微信接口

C#开发微信门户及应用(6)--微信门户菜单的管理操作的更多相关文章

  1. C#-MVC开发微信应用(4)--微信门户菜单的管理操作

    最近对微信接口进行深入的研究,通过把底层接口一步步进行封装后,逐步升级到自动化配置.自动化应答,以及后台处理界面的优化和完善上,力求搭建一个较为完善.适用的微信门户应用管理系统. 在微信门户系统里面, ...

  2. (转)C#开发微信门户及应用(6)--微信门户菜单的管理操作

    http://www.cnblogs.com/wuhuacong/p/3701961.html 前面几篇继续了我自己对于C#开发微信门户及应用的技术探索和相关的经验总结,继续探索微信API并分享相关的 ...

  3. C#开发微信门户及应用(25)-微信企业号的客户端管理功能

    我们知道,微信公众号和企业号都提供了一个官方的Web后台,方便我们对微信账号的配置,以及相关数据的管理功能,对于微信企业号来说,有通讯录中的组织架构管理.标签管理.人员管理.以及消息的发送等功能,其中 ...

  4. C#开发微信门户及应用(38)--微信摇一摇红包功能

    摇一摇周边红包接口是为线下商户提供的发红包功能.用户可以在商家门店等线下场所通过摇一摇周边领取商家发放的红包.我曾经在<C#开发微信门户及应用(28)--微信“摇一摇·周边”功能的使用和接口的实 ...

  5. C#开发微信门户及应用(37)--微信公众号标签管理功能

    微信公众号,仿照企业号的思路,增加了标签管理的功能,对关注的粉丝可以设置标签管理,实现更加方便的分组管理功能.开发者可以使用用户标签管理的相关接口,实现对公众号的标签进行创建.查询.修改.删除等操作, ...

  6. C#开发微信门户及应用(36)--微信卡劵管理的封装操作

    前面几篇介绍了微信支付方面的内容,本篇继续微信接口的一些其他方面的内容:卡劵管理.卡劵管理是微信接口里面非常复杂的一个部分,里面的接口非常多,我花了不少时间对它进行了封装处理,重构优化等等工作,卡劵在 ...

  7. C#开发微信门户及应用(35)--微信支付之企业付款封装操作

    在前面几篇随笔,都是介绍微信支付及红包相关的内容,其实支付部分的内容还有很多,例如企业付款.公众号支付或刷卡支付.摇一摇红包.代金券等方面的内容,这些都是微信接口支持的内容,本篇继续微信支付这一主题, ...

  8. C#开发微信门户及应用(34)--微信裂变红包

    在上篇随笔<C#开发微信门户及应用(33)--微信现金红包的封装及使用>介绍了普通现金红包的封装和使用,这种红包只能单独一次发给一个人,用户获取了红包就完成了,如果我们让用户收到红包后,可 ...

  9. C#开发微信门户及应用(33)--微信现金红包的封装及使用

    我在上篇随笔<C#开发微信门户及应用(32)--微信支付接入和API封装使用>介绍为微信支付的API封装及使用,其中介绍了如何配置好支付环境,并对扫码支付的两种方式如何在C#开发中使用进行 ...

随机推荐

  1. ASP.NET加密和解密数据库连接字符串

    大家知道,在应用程序中进行数据库操作需要连接字符串,而如果没有连接字符串,我们就无法在应用程序中完成检索数据,创建数据等一系列的数据库操作.当有人想要获取你程序中的数据库信息,他首先看到的可能会是We ...

  2. CSS 3学习——box-sizing和背景

    box-sizing 在CSS 2中设置元素的width和height仅仅是设置了元素内容区的宽和高,元素实际的尺寸是margin + border + padding + 内容区. CSS 3(截止 ...

  3. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  4. ASP.NET SignaiR 实现消息的即时推送,并使用Push.js实现通知

    一.使用背景 1. SignalR是什么? ASP.NET SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程.实时 Web 功能是指 ...

  5. NodeJs支付宝移动支付签名及验签

    非常感谢 :http://www.jianshu.com/p/8513e995ff3a?utm_campaign=hugo&utm_medium=reader_share&utm_co ...

  6. NodeJS使用mysql

    1.环境准备 手动添加数据库依赖: 在package.json的dependencies中新增, "mysql" : "latest", { "nam ...

  7. Linux网络属性配置

    目录 IP地址分类 如何将Linux主机接入到网络中 网络接口的命名方式 ifcfg系列命令 如何配置主机名 如何配置DNS服务器指向 iproute2系列命令 Linux管理网络服务 永久生效配置路 ...

  8. Jexus Web Server 完全傻瓜化图文配置教程(基于Ubuntu 12.04.3 64位)[内含Hyper-v 2012虚拟机镜像下载地址]

    1. 前言 近日有感许多新朋友想尝试使用Jexus,不过绝大多数都困惑徘徊在Linux如何安装啊,如何编译Mono啊,如何配置Jexus啊...等等基础问题,于是昨日向宇内流云兄提议,不如搞几个配置好 ...

  9. nginx常用代理配置

    因为业务系统需求,需要对web服务作nginx代理,在不断的尝试过程中,简单总结了一下常见的nginx代理配置. 1. 最简反向代理配置 在http节点下,使用upstream配置服务地址,使用ser ...

  10. Objective-C runtime初识

    Objective-C Runtime Describes the macOS Objective-C runtime library support functions and data struc ...