添加自定义菜单,就是在公众号的底部添加菜单
- 自定义菜单最多包含3个一级菜单
- 每个一级菜单最多包含5个二级菜单
- 一级菜单最多4个汉字,二级菜单则最多7个汉字,多出来的会用
...代替 - 创建自定义菜单后,如果上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。
自定义菜单类型如下:
- click:点击推事件 用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event 的结构给开发者,并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;
- view:跳转URL 用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的网页URL,可与网页授权获取用户基本信息接口结合,获得用户基本信息。
- scancode_push:扫码推事件 用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后显示扫描结果(如果是URL,将进入URL),且会将扫码的结果传给开发者,开发者可以下发消息。
- scancode_waitmsg:扫码推事件且弹出“消息接收中”提示框 用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后,将扫码的结果传给开发者,同时收起扫一扫工具,然后弹出“消息接收中”提示框,随后可能会收到开发者下发的消息。
- pic_sysphoto:弹出系统拍照发图 用户点击按钮后,微信客户端将调起系统相机,完成拍照操作后,会将拍摄的相片发送给开发者,并推送事件给开发者,同时收起系统相机,随后可能会收到开发者下发的消息。
- pic_photo_or_album:弹出拍照或者相册发图 用户点击按钮后,微信客户端将弹出选择器供用户选择“拍照”或者“从手机相册选择”。用户选择后即走其他两种流程。
- pic_weixin:弹出微信相册发图器 用户点击按钮后,微信客户端将调起微信相册,完成选择操作后,将选择的相片发送给开发者的服务器,并推送事件给开发者,同时收起相册,随后可能会收到开发者下发的消息。
- location_select:弹出地理位置选择器 用户点击按钮后,微信客户端将调起地理位置选择工具,完成选择操作后,将选择的地理位置发送给开发者的服务器,同时收起位置选择工具,随后可能会收到开发者下发的消息。
- media_id:下发消息(除文本消息) 用户点击media_id类型按钮后,微信服务器会将开发者填写的永久素材id对应的素材下发给用户,永久素材类型可以是图片、音频、视频、图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。
- view_limited:跳转图文消息URL 用户点击view_limited类型按钮后,微信客户端将打开开发者在按钮中填写的永久素材id对应的图文消息URL,永久素材类型只支持图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。
类型注意事项
- 3到8的所有事件,仅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用户,旧版本微信用户点击后将没有回应,开发者也不能正常接收到事件推送。
- 9和10,是专门给第三方平台旗下未微信认证(具体而言,是资质认证未通过)的订阅号准备的事件类型,它们是没有事件推送的,能力相对受限,其他类型的公众号不必使用
- 本次案例添加View和Click类型按钮
- POST请求地址:
[https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN](https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN)注意是https协议
菜单推送实现
因为需要POST数据到微信接口,所以我们需要序列化json文本提交给微信接口 我们可以在序列化菜单对象成json的时候,使用匿名对象
菜单JSON示例:
{
"button": [
{
"type": "click",
"name": "今日歌曲",
"key": "V1001_TODAY_MUSIC"
},
{
"name": "菜单",
"sub_button": [
{
"type": "view",
"name": "搜索",
"url": "[http://www.soso.com/](http://www.soso.com/)"
},
{
"type": "view",
"name": "视频",
"url": "[http://v.qq.com/](http://v.qq.com/)"
},
{
"type": "click",
"name": "赞一下我们",
"key": "V1001_GOOD"
}
]
}
]
}
Code language: JSON / JSON with Comments (json)
序列化菜单方法和使用
protected void Button3_Click(object sender, EventArgs e)
{
Infos.Menu m = new Infos.Menu();
//添加第一个菜单
m.button.Add(new { type = "click",name = "今日歌曲",key = "V1001_TODAY_MUSIC" });
//添加第二个菜单-包含子菜单
ArrayList sub = new ArrayList();
sub.Add( new { name = "搜索", type = "view", url = "[http://www.soso.com/](http://www.soso.com/)" });
sub.Add(new { name = "视频", type = "view", url = "[http://v.qq.com/](http://v.qq.com/)" });
sub.Add(new { name = "赞一下我们", type = "click", key = "V1001_GOOD" });
m.button.Add(new { name = "菜单",sub_button = sub });
//序列化成json文本
string json = Tool.SerializeJson(m);
}
Code language: JavaScript (javascript)
Tool工具类新增序列化方法:
/// <summary>
/// 反序列化JSON成对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="jsonText"></param>
/// <returns></returns>
public static T DeserializeJson<T>(string jsonText)
{
return JavaScriptConvert.DeserializeObject<T>(jsonText);
}
/// <summary>
/// 序列化对象成JSON
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string SerializeJson(object obj)
{
return JavaScriptConvert.SerializeObject(obj);
}
Code language: HTML, XML (xml)
提交POST注意的问题
- 提交的参数是Body参数
- 错误写法:
body=json - 报错:
{"errcode":40016,"errmsg":"invalid but"} - 正确:不要带body参数,直接把整个json内容作为请求体提交,微信服务器会把整个body内容当作菜单json
POSTJson工具方法(修正ContentType)
public static string PostJson(string json, string url)
{
string textResponse = string.Empty;
Stream outstream = null;
Stream instream = null;
StreamReader sr = null;
HttpWebResponse response = null;
HttpWebRequest request = null;
Encoding encoding = Encoding.UTF8;
byte[] data = encoding.GetBytes(json);
try
{
request = WebRequest.Create(url) as HttpWebRequest;
CookieContainer cookieContainer = new CookieContainer();
request.CookieContainer = cookieContainer;
request.AllowAutoRedirect = true;
request.Method = "POST";
// 重点:提交JSON必须改为application/json,而不是form表单类型
request.ContentType = "application/json";
request.ContentLength = data.Length;
outstream = request.GetRequestStream();
outstream.Write(data, 0, data.Length);
outstream.Close();
//发送请求并获取相应回应数据
response = request.GetResponse() as HttpWebResponse;
instream = response.GetResponseStream();
sr = new StreamReader(instream, encoding);
//返回结果网页(html)代码
textResponse = sr.ReadToEnd();
}
catch(Exception ex)
{
textResponse = ex.Message;
}
// 释放资源代码省略
return textResponse;
}
Code language: PHP (php)
发送成功后,微信刷新菜单规则
- 成功返回:
{"errcode":0,"errmsg":"ok"} - 如果距离上次菜单刷新小于5分钟,则需要等5分钟客户端菜单才会刷新
- 调试可用微信测试接口:
[https://mp.weixin.qq.com/debug/cgi-bin/apiinfo?t=index&type=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95&form=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E5%88%9B%E5%BB%BA%E6%8E%A5%E5%8F%A3/menu/create](https://mp.weixin.qq.com/debug/cgi-bin/apiinfo?t=index&type=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95&form=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E5%88%9B%E5%BB%BA%E6%8E%A5%E5%8F%A3/menu/create)
反序列化返回的json结果
新建实体类接收返回结果
public class MenuCreateResultInfo
{
public string errcode { set; get; }
public string errmsg { set; get; }
}
Code language: JavaScript (javascript)
//反序列化返回的结果
MenuCreateResultInfo menuResObj = Tool.DeserializeJson<MenuCreateResultInfo>(textResponse);
Code language: HTML, XML (xml)

Previous: 获取微信服务器IP地址
Next: C# 微信公众号自定义菜单JSON反序列化