自定义菜单创建接口:

http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

自定义菜单查询接口:

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

自定义菜单删除接口:

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

自定义菜单接口可实现多种类型按钮,如下:

1、click:点击推事件
用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event    的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;
2、view:跳转URL
用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的网页URL,可与网页授权获取用户基本信息接口结合,获得用户基本信息。
3、scancode_push:扫码推事件
用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后显示扫描结果(如果是URL,将进入URL),且会将扫码的结果传给开发者,开发者可以下发消息。
4、scancode_waitmsg:扫码推事件且弹出“消息接收中”提示框
用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后,将扫码的结果传给开发者,同时收起扫一扫工具,然后弹出“消息接收中”提示框,随后可能会收到开发者下发的消息。
5、pic_sysphoto:弹出系统拍照发图
用户点击按钮后,微信客户端将调起系统相机,完成拍照操作后,会将拍摄的相片发送给开发者,并推送事件给开发者,同时收起系统相机,随后可能会收到开发者下发的消息。
6、pic_photo_or_album:弹出拍照或者相册发图
用户点击按钮后,微信客户端将弹出选择器供用户选择“拍照”或者“从手机相册选择”。用户选择后即走其他两种流程。
7、pic_weixin:弹出微信相册发图器
用户点击按钮后,微信客户端将调起微信相册,完成选择操作后,将选择的相片发送给开发者的服务器,并推送事件给开发者,同时收起相册,随后可能会收到开发者下发的消息。
8、location_select:弹出地理位置选择器
用户点击按钮后,微信客户端将调起地理位置选择工具,完成选择操作后,将选择的地理位置发送给开发者的服务器,同时收起位置选择工具,随后可能会收到开发者下发的消息。
9、media_id:下发消息(除文本消息)
用户点击media_id类型按钮后,微信服务器会将开发者填写的永久素材id对应的素材下发给用户,永久素材类型可以是图片、音频、视频、图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。
10、view_limited:跳转图文消息URL
用户点击view_limited类型按钮后,微信客户端将打开开发者在按钮中填写的永久素材id对应的图文消息URL,永久素材类型只支持图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。

请注意,3到8的所有事件,仅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用户,旧版本微信用户点击后将没有回 应,开发者也不能正常接收到事件推送。9和10,是专门给第三方平台旗下未微信认证(具体而言,是资质认证未通过)的订阅号准备的事件类型,它们是没有事 件推送的,能力相对受限,其他类型的公众号不必使用。

click和view的请求示例

{"button":[{    "type":"click","name":"今日","key":"1"},{"name":"菜单","sub_button":[{    "type":"view","name":"搜索","url":"http://www.soso.com/"},{"type":"view","name":"视频","url":"http://v.qq.com/"},{"type":"click","name":"测试","key":"2"}]}]}

其他新增按钮类型的请求示例

{"button": [{"name": "扫码", "sub_button": [{"type": "scancode_waitmsg", "name": "扫码带提示", "key": "rselfmenu_0_0", "sub_button": [ ]}, {"type": "scancode_push", "name": "扫码推事件", "key": "rselfmenu_0_1", "sub_button": [ ]}]}, {"name": "发图", "sub_button": [{"type": "pic_sysphoto", "name": "系统拍照发图", "key": "rselfmenu_1_0", "sub_button": [ ]}, {"type": "pic_photo_or_album", "name": "拍照或者相册发图", "key": "rselfmenu_1_1", "sub_button": [ ]}, {"type": "pic_weixin", "name": "微信相册发图", "key": "rselfmenu_1_2", "sub_button": [ ]}]}, {"name": "发送位置", "type": "location_select", "key": "rselfmenu_2_0"},{"type": "media_id", "name": "图片", "media_id": "MEDIA_ID1"}, {"type": "view_limited", "name": "图文消息", "media_id": "MEDIA_ID2"}]
}

参数说明

参数 是否必须 说明
button 一级菜单数组,个数应为1~3个
sub_button 二级菜单数组,个数应为1~5个
type 菜单的响应动作类型
name 菜单标题,不超过16个字节,子菜单不超过40个字节
key click等点击类型必须 菜单KEY值,用于消息接口推送,不超过128字节
url view类型必须 网页链接,用户点击菜单可打开链接,不超过256字节
media_id media_id类型和view_limited类型必须 调用新增永久素材接口返回的合法media_id

返回结果

正确时的返回JSON数据包如下:{"errcode":0,"errmsg":"ok"}

错误时的返回JSON数据包如下(示例为无效菜单名长度):{"errcode":40018,"errmsg":"invalid button name size"}

根据以上的信息,我们进行封装一下菜单。

我们现在能用的是一个点击click(点击事件)和view(访问网页)这俩种类型。

代码实现:

首先是 菜单项的基类,所有一级菜单、二级菜单都共有一个相同的属性,那就是name。菜单项基类的封装代码如下:

package cn.com.comit.appointment.modules.wechat.message.menu;/**
* 类名: Button </br>
* 描述: 菜单项的基类  </br>*/
public class Button {private String name;//所有一级菜单、二级菜单都共有一个相同的属性,那就是namepublic String getName() {return name;}public void setName(String name) {this.name = name;}
}

接着是子菜单项的封装。这里对子菜单是这样定义的:没有子菜单的菜单项,有可能是二级菜单项,也有可能是不含二级菜单的一级菜单。这类子菜单项一定会包含三个属性:type、name和key,封装的代码如下:

package cn.com.comit.appointment.modules.wechat.message.menu;/**
* 类名: CommonButton </br>
* 描述: 子菜单项 :没有子菜单的菜单项,有可能是二级菜单项,也有可能是不含二级菜单的一级菜单。 </br>*/
public class CommonButton extends Button {private String type;private String key;public String getType() {return type;}public void setType(String type) {this.type = type;}public String getKey() {return key;}public void setKey(String key) {this.key = key;}
}
package cn.com.comit.appointment.modules.wechat.message.menu;/*** view按钮**/
public class ViewButton extends Button{private String type;private String url;public String getType() {return type;}public void setType(String type) {this.type = type;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}}

再往下是父菜单项的封装。对父菜单项的定义:包含有二级菜单项的一级菜单。这类菜单项包含有二个属性:name和sub_button,而sub_button以是一个子菜单项数组。父菜单项的封装代码如下:

package cn.com.comit.appointment.modules.wechat.message.menu;/**
* 类名: ComplexButton </br>
* 描述: 父菜单项 :包含有二级菜单项的一级菜单。这类菜单项包含有二个属性:name和sub_button,而sub_button以是一个子菜单项数组 </br>*/
public class ComplexButton extends Button {private Button[] sub_button;public Button[] getSub_button() {return sub_button;}public void setSub_button(Button[] sub_button) {this.sub_button = sub_button;}
}

最后是整个菜单对象的封装,菜单对象包含多个菜单项(最多只能有3个),这些菜单项即可以是子菜单项(不含二级菜单的一级菜单),也可以是父菜单项(包含二级菜单的菜单项),如果能明白上面所讲的,再来看封装后的代码就很容易理解了:

package cn.com.comit.appointment.modules.wechat.message.menu;/**
* 类名: Menu </br>
* 描述: 整个菜单对象的封装 </br>*/
public class Menu {private Button[] button;public Button[] getButton() {return button;}public void setButton(Button[] button) {this.button = button;}
}

AccessToken 的POJO的封装:详情可以看微信公众号开发之获取公众号的access_token

package cn.com.comit.appointment.modules.wechat.entity;/**
* 类名: AccessToken </br>
* 描述: 微信通用接口凭证  </br>*/
public class AccessToken {// 获取到的凭证private String token;// 凭证有效时间,单位:秒private int expiresIn;public String getToken() {return token;}public void setToken(String token) {this.token = token;}public int getExpiresIn() {return expiresIn;}public void setExpiresIn(int expiresIn) {this.expiresIn = expiresIn;}
}

封装通用的请求方法

读到这里,就默认大家已经掌握了上面讲到的所有关于自定义菜单的理论知识,下面就进入代码实战讲解的部分。

先前我们了解到,创建菜单需要调用二个接口,并且都是https请求,而非http。如果要封装一个通用的请求方法,该方法至少需要具备以下能力:

1)支持HTTPS请求;

2)支持GET、POST两种方式;

3)支持参数提交,也支持无参数的情况;

对于https请求,我们需要一个证书信任管理器,这个管理器类需要自己定义,但需要实现X509TrustManager接口,代码如下:

package cn.com.comit.appointment.modules.wechat.utils;import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;/**
* 类名: MyX509TrustManager </br>
* 描述: 信任管理器 </br>*/
public class MyX509TrustManager implements X509TrustManager {// 检查客户端证书public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}// 检查服务器端证书public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}// 返回受信任的X509证书数组public X509Certificate[] getAcceptedIssuers() {return null;}
}

这个证书管理器的作用就是让它信任我们指定的证书,上面的代码意味着信任所有证书,不管是否权威机构颁发。

证书有了,通用的https请求方法就不难实现了,实现代码如下:

package cn.com.comit.appointment.modules.wechat.utils;import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;import net.sf.json.JSONException;
import net.sf.json.JSONObject;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import cn.com.comit.appointment.modules.wechat.entity.AccessToken;
import cn.com.comit.appointment.modules.wechat.message.menu.Menu;/**
* 类名: WeixinUtil </br>
* 描述: 公众平台通用接口工具类 </br>*/
public class WeixinUtil {private static Logger log = LoggerFactory.getLogger(WeixinUtil.class);// 获取access_token的接口地址(GET) 限200(次/天)public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";// 菜单创建(POST) 限100(次/天)public static String menu_create_url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";/*** 创建菜单* * @param menu 菜单实例* @param accessToken 有效的access_token* @return 0表示成功,其他值表示失败*/public static int createMenu(Menu menu, String accessToken) {int result = 0;// 拼装创建菜单的urlString url = menu_create_url.replace("ACCESS_TOKEN", accessToken);// 将菜单对象转换成json字符串String jsonMenu = JSONObject.fromObject(menu).toString();System.out.println("cs-->"+jsonMenu);// 调用接口创建菜单JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);if (null != jsonObject) {if (0 != jsonObject.getInt("errcode")) {result = jsonObject.getInt("errcode");log.error("创建菜单失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));}}return result;}/*** 获取access_token* * @param appid 凭证* @param appsecret 密钥* @return*/public static AccessToken getAccessToken(String appid, String appsecret) {AccessToken accessToken = null;String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret);JSONObject jsonObject = httpRequest(requestUrl, "GET", null);// 如果请求成功if (null != jsonObject) {try {accessToken = new AccessToken();accessToken.setToken(jsonObject.getString("access_token"));accessToken.setExpiresIn(jsonObject.getInt("expires_in"));} catch (JSONException e) {accessToken = null;// 获取token失败log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));}}return accessToken;}/*** 描述:  发起https请求并获取结果* @param requestUrl 请求地址* @param requestMethod 请求方式(GET、POST)* @param outputStr 提交的数据* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)*/public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {JSONObject jsonObject = null;StringBuffer buffer = new StringBuffer();try {// 创建SSLContext对象,并使用我们指定的信任管理器初始化TrustManager[] tm = { new MyX509TrustManager() };SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");sslContext.init(null, tm, new java.security.SecureRandom());// 从上述SSLContext对象中得到SSLSocketFactory对象SSLSocketFactory ssf = sslContext.getSocketFactory();URL url = new URL(requestUrl);HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();httpUrlConn.setSSLSocketFactory(ssf);httpUrlConn.setDoOutput(true);httpUrlConn.setDoInput(true);httpUrlConn.setUseCaches(false);// 设置请求方式(GET/POST)httpUrlConn.setRequestMethod(requestMethod);if ("GET".equalsIgnoreCase(requestMethod))httpUrlConn.connect();// 当有数据需要提交时if (null != outputStr) {OutputStream outputStream = httpUrlConn.getOutputStream();// 注意编码格式,防止中文乱码outputStream.write(outputStr.getBytes("UTF-8"));outputStream.close();}// 将返回的输入流转换成字符串InputStream inputStream = httpUrlConn.getInputStream();InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String str = null;while ((str = bufferedReader.readLine()) != null) {buffer.append(str);}bufferedReader.close();inputStreamReader.close();// 释放资源inputStream.close();inputStream = null;httpUrlConn.disconnect();jsonObject = JSONObject.fromObject(buffer.toString());} catch (ConnectException ce) {log.error("Weixin server connection timed out.");} catch (Exception e) {log.error("https request error:{}", e);}return jsonObject;}
}

添加菜单管理器:注意替换称自己公众号的appId和appSecret。

package cn.com.comit.appointment.modules.wechat.test;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import cn.com.comit.appointment.modules.wechat.entity.AccessToken;
import cn.com.comit.appointment.modules.wechat.message.menu.Button;
import cn.com.comit.appointment.modules.wechat.message.menu.CommonButton;
import cn.com.comit.appointment.modules.wechat.message.menu.ComplexButton;
import cn.com.comit.appointment.modules.wechat.message.menu.Menu;
import cn.com.comit.appointment.modules.wechat.message.menu.ViewButton;
import cn.com.comit.appointment.modules.wechat.utils.WeixinUtil;/**
* 类名: MenuManager </br>
* 描述:菜单管理器类 </br>*/
public class MenuManager {private static Logger log = LoggerFactory.getLogger(MenuManager.class);public static void main(String[] args) {// 第三方用户唯一凭证String appId = "你公众号的appId";// 第三方用户唯一凭证密钥String appSecret = "你公众号的appSecret";// 调用接口获取access_tokenAccessToken at = WeixinUtil.getAccessToken(appId, appSecret);if (null != at) {// 调用接口创建菜单int result = WeixinUtil.createMenu(getMenu(), at.getToken());// 判断菜单创建结果if (0 == result)log.info("菜单创建成功!");elselog.info("菜单创建失败,错误码:" + result);}}/*** 组装菜单数据* * @return*/private static Menu getMenu() {ViewButton btn21 = new ViewButton();btn21.setName("百度");btn21.setType("view");btn21.setUrl("http://www.baidu.com");ViewButton btn22 = new ViewButton();btn22.setName("官方链接");btn22.setType("view");btn22.setUrl("https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirect");ViewButton btn23 = new ViewButton();btn23.setName("查询");btn23.setType("view");btn23.setUrl("https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx44eb1076428bc798&redirect_uri=http://992nkr.natappfree.cc/appointment/wechat/toAppoin/oauth&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect");ViewButton btn24 = new ViewButton();btn24.setName("查询2");btn24.setType("view");btn24.setUrl("https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx44eb1076428bc798&redirect_uri=http%3a%2f%2fgb4xrs.natappfree.cc%2fappointment%2fwechat%2ftoAppoin%2foauth&response_type=code&scope=snsapi_base&state=123#wechat_redirect");ViewButton btn25 = new ViewButton();btn25.setName("登录");btn25.setType("view");btn25.setUrl("http://gb4xrs.natappfree.cc/appointment/sys/login.view");/*  ViewButton btn26 = new ViewButton();btn26.setName("测试");btn26.setType("view");btn26.setUrl("http://ytuwpp.natappfree.cc/appointment/wechat/toAppoin/toAppointInit");*/CommonButton btn33 = new CommonButton();btn33.setName("幽默笑话");btn33.setType("click");btn33.setKey("33");/*** 微信:  mainBtn1,mainBtn2,mainBtn3底部的三个一级菜单。*/ComplexButton mainBtn1 = new ComplexButton();mainBtn1.setName("违章查询");//一级下有4个子菜单mainBtn1.setSub_button(new ViewButton[] { btn21 });ComplexButton mainBtn2 = new ComplexButton();mainBtn2.setName("菜单");mainBtn2.setSub_button(new ViewButton[] {btn22,btn23,btn24,btn25 });ComplexButton mainBtn3 = new ComplexButton();mainBtn3.setName("更多体验");mainBtn3.setSub_button(new CommonButton[] {  btn33 });/*** 封装整个菜单*/Menu menu = new Menu();menu.setButton(new Button[] { mainBtn1, mainBtn2, mainBtn3 });return menu;}
}

最后直接执行MenuManager 的main 方法即可。

点击菜单中登录的按钮就跳转到我本地ssm项目的登录页面了,ssm是在本地run起来的。

菜单跳转的url是你的域名+项目+访问路径。详情可以看微信公众号开发需要准备的一些资料以及如何搭建一个本地测试服务环境

效果:

在公众号点击登录菜单在手机上跳转:

在浏览器打开登录的url:

我的座右铭:不会,我可以学;落后,我可以追赶;跌倒,我可以站起来;我一定行。

微信公众号开发之实现自定义微信公众号菜单和跳转第三方网页相关推荐

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

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

  2. 微信公众号开发系列-玩转微信开发-目录汇总

    引言 最遗憾的不是把理想丢在路上,而是理想从未上路. 每一个将想法变成现实的人,都值得称赞和学习.致正在奔跑的您! 在现在这个无处不在的互联网背景下,各种应用已不再仅仅局限于网页或桌面应用了,IOS. ...

  3. 【微信公众号开发】八、微信JS发起支付

    重要声明:本文章仅仅代表了作者个人对此观点的理解和表述.读者请查阅时持自己的意见进行讨论. 目录 本系列博文还包含了下面的博客: [微信公众号开发]一.运作及配置流程简介 [微信公众号开发]二.解析微 ...

  4. 【微信公众号开发】六、微信JS的使用

    文章详情:[微信公众号开发]六.微信JS的使用 文章链接:https://www.microanswer.cn/blog/16

  5. 微信公众平台开发(31)微信第三方登录接口

    原文: http://www.cnblogs.com/imaker/p/5491433.html 第一步:获取AppID AppSecret(不做解释,自己去微信公众平台申请) 第二步:生成扫描二维码 ...

  6. 微信公php开发视频,PHP实现微信公众平台开发 全套视频资源下载

    好久没有更新东西了,今天给大家分享一份比较不错的视频学习资源吧. 主要是关于PHP实现微信公众平台开发, 不知道大家对于微信平台的开发有多少了解,那么今天就从基础开始吧,资源目录如下(PS :适各自的 ...

  7. 微信公众平台开发教程Java版(六) 事件处理(菜单点击/关注/取消关注)

    前言: 事件处理是非常重要的,这一章讲讲常见的事件处理 1.关注/取消关注 2.菜单点击 事件类型介绍: 在微信中有事件请求是消息请求中的一种.请求类型为:event 而event事件类型又分多种事件 ...

  8. 视频教程-微信公众平台开发实战PHP版-微信开发

    微信公众平台开发实战PHP版 互联商通创始人/架构师, 在IT领域摸爬滚打20年的全栈老程序员(架构师.培训讲师).熟悉iOS.Android及LNMP的架构下快速开发互联网相关项目.精通微信和支付宝 ...

  9. 微信公众平台开发(二) 微信公众平台示例代码分析

    http://www.cnblogs.com/mchina/archive/2013/06/07/3120592.html 微信公众平台开发(二) 微信公众平台示例代码分析 一.摘要 微信公众平台提供 ...

最新文章

  1. pip 升级_python的pip命令
  2. 开放式Android联盟成立
  3. Android配置build.gradle解锁更高逼格玩法(多版本共存、分服务器打包等)
  4. 深入理解分布式技术 - 服务注册与发现背后的逻辑
  5. AI未来说 | 听大牛论自动驾驶领域干货,看无人驾驶小车真实运作
  6. python有什么用处案例_为什么大家都推荐你学python?看完这5个例子就明白了!...
  7. java.io.IOException: Unable to open sync connection!的解决方案
  8. 微软开源 rDSN 分布式系统开发框架
  9. use regular expression instead of ABAP function module to parse attachment
  10. python学习-集合(创建、add、remove、discard、与、或、去同、存异)
  11. 硅谷创业者中被遮蔽的“中国现象”
  12. Java集合之HashSet源码分析
  13. 如何在 Mac 上切换语言
  14. Java web中的web-xml中标签定义之jsp-config
  15. 微信小程序直播是怎么做的
  16. python离线语音识别_python语音识别模块
  17. 性能优化-图片压缩格式的选择(ETC和ASTC)
  18. python编程求100以内能被5整除之和_C语言,求100以内能被3整除或能被5整除的数的和...
  19. java,你的程序要读入一个整数,范围是[-100000,100000]。然后,用汉语拼音将这个整数的每一位输出出来。 如输入1234,则输出: yi er san si
  20. linux网络入侵命令,如何使用netstat命令验证DDOS入侵

热门文章

  1. 《J2SE 回炉再造10》-------溺水狗
  2. 2018年2月Angular开源项目Top10
  3. STM32单片机蓝牙APP自动伸缩遮阳棚雨伞雨滴角度温度光强控制
  4. matlab如何模拟转子断条,转子断条故障案例分析
  5. 用友盛大发布商业创新平台YonBIP,引领从ERP到BIP的变革
  6. quadtree java_LeetCode算法题-Quad Tree Intersection(Java实现)
  7. Web Performance评估工具介绍
  8. 洛谷:P2700 逐个击破(最大生成树、贪心)
  9. 你不得不知道的人脸识别「背后的故事」
  10. 自定义chrome浏览器导航条样式