博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java版微信公众号开发(四):自定义菜单的实现
阅读量:6231 次
发布时间:2019-06-21

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

想要实现自定义菜单的功能,需要有已认证订阅号和已认证服务号。对于测试开发来说,可以直接申请一个测试账号:

同样需要token的验证,前期接口已经定义好了,直接拿来就可以

clipboard.png

根据开发者文档,自定义菜单注意:

1、自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。2、一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。3、创建自定义菜单后,菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。

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

clipboard.png

接口调用请求说明

http请求方式:POST(请使用https协议)

click和view的请求示例

{     "button":[     {              "type":"click",          "name":"今日歌曲",          "key":"V1001_TODAY_MUSIC"      },      {           "name":"菜单",           "sub_button":[           {                   "type":"view",               "name":"搜索",               "url":"http://www.soso.com/"            },            {                 "type":"miniprogram",                 "name":"wxa",                 "url":"http://mp.weixin.qq.com",                 "appid":"wx286b93c14bbf93aa",                 "pagepath":"pages/lunar/index"             },            {               "type":"click",               "name":"赞一下我们",               "key":"V1001_GOOD"            }]       }] }

参数说明:

clipboard.png

返回结果

正确时的返回JSON数据包如下:

{"errcode":0,"errmsg":"ok"}

错误时的返回JSON数据包如下(示例为无效菜单名长度):

{"errcode":40018,"errmsg":"invalid button name size"}

以上均来自官方说明文档。


pom引入jar包:

net.sf.ezmorph
ezmorph
1.0.6
commons-beanutils
commons-beanutils
1.8.0
commons-collections
commons-collections
3.2.1
commons-lang
commons-lang
2.3
commons-logging
commons-logging
1.1.1
net.sf.json-lib
json-lib
2.4
jdk15
dom4j
dom4j
1.6.1
com.thoughtworks.xstream
xstream
1.4.9

定义菜单实体类:

/** * 按钮基类 * @author zhoumin * @create 2018-07-11 15:22 */@Setter@Getterpublic class BasicButton {    private String name;    private String url;}/** * 普通按钮 * * @author zhoumin * @create 2018-07-12 9:56 */@Setter@Getterpublic class CommonButton extends BasicButton {    private String type;    private String key;}/** * 父按钮 * @author zhoumin * @create 2018-07-11 15:24 */@Setter@Getterpublic class ComplexButton extends BasicButton {    private BasicButton[] sub_button;}/** * 菜单 * @author zhoumin * @create 2018-07-11 15:22 */@Setter@Getterpublic class Menu {    private BasicButton[] button;}/** * @author zhoumin * @create 2018-07-11 15:23 */@Setter@Getterpublic class ViewButton extends BasicButton {    private String type;    private String name;    private String url;}/** * 凭证 * @author zhoumin * @create 2018-07-11 15:22 */@Setter@Getterpublic class AccessToken {    /**     *  获取到的凭证     */    private String accessToken;        /**     *  凭证有效时间,单位:秒     */    private int expiresIn;}

定义工具类:

/** * 实现接口 * @author zhoumin * @create 2018-07-12 10:01 */public class MyX509TrustManager implements X509TrustManager {    // 检查客户端证书    @Override    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {    }    // 检查服务器端证书    @Override    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {    }    // 返回受信任的X509证书数组    @Override    public X509Certificate[] getAcceptedIssuers() {        return new X509Certificate[0];    }}/** * @author zhoumin * @create 2018-07-12 10:04 */public class CommonWechatUtil {    private static Logger log = LoggerFactory.getLogger(CommonWechatUtil.class);    // 凭证获取(GET)    public final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";    /**     * 发送https请求     *     * @param requestUrl 请求地址     * @param requestMethod 请求方式(GET、POST)     * @param outputStr 提交的数据     * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)     */    public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {        JSONObject jsonObject = null;        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 conn = (HttpsURLConnection) url.openConnection();            conn.setSSLSocketFactory(ssf);            conn.setDoOutput(true);            conn.setDoInput(true);            conn.setUseCaches(false);            // 设置请求方式(GET/POST)            conn.setRequestMethod(requestMethod);            // 当outputStr不为null时向输出流写数据            if (null != outputStr) {                OutputStream outputStream = conn.getOutputStream();                // 注意编码格式                outputStream.write(outputStr.getBytes("UTF-8"));                outputStream.close();            }            // 从输入流读取返回内容            InputStream inputStream = conn.getInputStream();            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);            String str = null;            StringBuffer buffer = new StringBuffer();            while ((str = bufferedReader.readLine()) != null) {                buffer.append(str);            }            // 释放资源            bufferedReader.close();            inputStreamReader.close();            inputStream.close();            inputStream = null;            conn.disconnect();            jsonObject = JSONObject.fromObject(buffer.toString());        } catch (ConnectException ce) {            log.error("连接超时:{}", ce);        } catch (Exception e) {            log.error("https请求异常:{}", e);        }        return jsonObject;    }    /**     * 获取接口访问凭证     *     * @param appid 凭证     * @param appsecret 密钥     * @return     */    public static AccessToken getToken(String appid, String appsecret) {        AccessToken token = null;        String requestUrl = token_url.replace("APPID", appid).replace("APPSECRET", appsecret);        // 发起GET请求获取凭证        JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);        if (null != jsonObject) {            try {                token = new AccessToken();                token.setAccessToken(jsonObject.getString("access_token"));                token.setExpiresIn(jsonObject.getInt("expires_in"));            } catch (JSONException e) {                token = null;                // 获取token失败                log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));            }        }        return token;    }    /**     * URL编码(utf-8)     *     * @param source     * @return     */    public static String urlEncodeUTF8(String source) {        String result = source;        try {            result = java.net.URLEncoder.encode(source, "utf-8");        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        }        return result;    }    /**     * 根据内容类型判断文件扩展名     *     * @param contentType 内容类型     * @return     */    public static String getFileExt(String contentType) {        String fileExt = "";        if ("image/jpeg".equals(contentType))            fileExt = ".jpg";        else if ("audio/mpeg".equals(contentType))            fileExt = ".mp3";        else if ("audio/amr".equals(contentType))            fileExt = ".amr";        else if ("video/mp4".equals(contentType))            fileExt = ".mp4";        else if ("video/mpeg4".equals(contentType))            fileExt = ".mp4";        return fileExt;    }    // 菜单创建(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;        // 拼装创建菜单的url        String url = menu_create_url.replace("ACCESS_TOKEN", accessToken);        // 将菜单对象转换成json字符串        String jsonMenu = JSONObject.fromObject(menu).toString();        // 调用接口创建菜单        JSONObject jsonObject = httpsRequest(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;    }    public static String menu_get_url = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";    /**     * 查询菜单     *     * @param accessToken 有效的access_token     * @return 0表示成功,其他值表示失败     */    public static JSONObject getMenu(String accessToken) {        int result = 0;        // 拼装创建菜单的url        String url = menu_get_url.replace("ACCESS_TOKEN", accessToken);        // 将菜单对象转换成json字符串//        String jsonMenu = JSONObject.fromObject(menu).toString();        // 调用接口创建菜单        JSONObject jsonObject = httpsRequest(url, "POST", null);        return jsonObject;    }    public static String menu_delete_url = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN";    /**     * 查询菜单     *     * @param accessToken 有效的access_token     * @return 0表示成功,其他值表示失败     */    public static int deleteMenu(String accessToken) {        int result = 0;        // 拼装创建菜单的url        String url = menu_delete_url.replace("ACCESS_TOKEN", accessToken);        // 调用接口创建菜单        JSONObject jsonObject = httpsRequest(url, "POST", null);        if (null != jsonObject) {            if (0 != jsonObject.getInt("errcode")) {                result = jsonObject.getInt("errcode");                log.error("删除菜单失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));            }        }        return result;    }

定义常量:

/** * 添加id和密码信息 * @author zhoumin * @create 2018-07-11 17:07 */public class ConstantWeChat {    public static final String APPID = "自己的AppId";    public static final String APPSECRET = "自己的APPSecret";}

实现方法:

/** * @author zhoumin * @create 2018-07-11 15:39 */public interface MenuService {}/** * @author zhoumin * @create 2018-07-11 15:40 */@Service("menuService")public class MenuServiceImpl implements MenuService {    private static final Logger LOGGER = LoggerFactory.getLogger(MenuServiceImpl.class);    //    @Override    public static Boolean createMenu() {        // 第三方用户唯一凭证        String appId = ConstantWeChat.APPID;        // 第三方用户唯一凭证密钥        String appSecret = ConstantWeChat.APPSECRET;        // 调用接口获取access_token        AccessToken at = CommonWechatUtil.getToken(appId, appSecret);        if (null != at) {            // 调用接口创建菜单            int result = CommonWechatUtil.createMenu(getMenu(), at.getAccessToken());            // 判断菜单创建结果            if (0 == result){                LOGGER.info("菜单创建成功!");                return true;            }            else{                LOGGER.info("菜单创建失败,错误码:" + result);                return false;            }        }        return false;    }    //    @Override    public static JSONObject getMenuBtn() {        // 第三方用户唯一凭证        String appId = ConstantWeChat.APPID;        // 第三方用户唯一凭证密钥        String appSecret = ConstantWeChat.APPSECRET;        // 调用接口获取access_token        AccessToken at = CommonWechatUtil.getToken(appId, appSecret);        if (null != at) {            // 调用接口获取菜单            JSONObject result = CommonWechatUtil.getMenu(at.getAccessToken());            // 判断菜单创建结果            if (null != result && result.size()>0){                LOGGER.info("菜单查询成功!");                return result;            }            else{                LOGGER.info("菜单查询失败,错误码:" + result);                return null;            }        }        return null;    }    //    @Override    public static Boolean deleteMenu() {        // 第三方用户唯一凭证        String appId = ConstantWeChat.APPID;        // 第三方用户唯一凭证密钥        String appSecret = ConstantWeChat.APPSECRET;        // 调用接口获取access_token        AccessToken at = CommonWechatUtil.getToken(appId, appSecret);        if (null != at) {            // 调用接口删除菜单            int result = CommonWechatUtil.deleteMenu(at.getAccessToken());            // 判断菜单删除结果            if (0 == result){                LOGGER.info("菜单删除成功!");                return true;            }            else{                LOGGER.info("菜单删除失败,错误码:" + result);                return false;            }        }        return false;    }    /**     * 组装菜单数据     *     * @return     * @throws UnsupportedEncodingException     */    private static Menu getMenu() {        ViewButton btn11 = new ViewButton();        btn11.setName("我是");        btn11.setType("view");        btn11.setUrl("https://segmentfault.com/u/panzi_5abcaf30a5e6b");        ViewButton btn21 = new ViewButton();        btn21.setName("盘子");        btn21.setType("view");        btn21.setUrl("https://segmentfault.com/u/panzi_5abcaf30a5e6b");        ViewButton btn31 = new ViewButton();        btn31.setName("谢谢");        btn31.setType("view");        btn31.setUrl("https://segmentfault.com/u/panzi_5abcaf30a5e6b");        ViewButton btn41 = new ViewButton();        btn41.setName("关注");        btn41.setType("view");        btn41.setUrl("https://segmentfault.com/u/panzi_5abcaf30a5e6b");        CommonButton btn12 = new CommonButton();        btn12.setName("赞");        btn12.setType("click");        btn12.setKey("return_content");        ComplexButton mainBtn1 = new ComplexButton();        mainBtn1.setName("自我介绍");        mainBtn1.setSub_button(new BasicButton[] { btn11, btn21,btn31});        ComplexButton mainBtn2 = new ComplexButton();        mainBtn2.setName("谢谢!");        mainBtn2.setSub_button(new BasicButton[] { btn41, btn12 });        /**         *在某个一级菜单下没有二级菜单的情况,menu应该这样定义:
* menu.setButton(new Button[] { mainBtn1, mainBtn2, btn33 }); */ Menu menu = new Menu(); menu.setButton(new BasicButton[] { mainBtn1, mainBtn2}); return menu; } public static void main(String[] args) { createMenu(); }}

这里直接运行main方法就好了

找到测试二维码,扫描关注,可以看到菜单已经有啦!!

clipboard.png

如果修改了话,可以取消关注再添加关注,就能看到更改信息后的菜单信息。

对于菜单的点击事件,可以回到我们的newMessageRequest方法中添加代码:

// 自定义菜单点击事件else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {String eventKey = requestMap.get("EventKey");// 事件KEY值,与创建自定义菜单时指定的KEY值对应if (eventKey.equals("return_content")) {  TextMessage text = new TextMessage();  text.setContent("赞赞赞");  text.setToUserName(fromUserName);  text.setFromUserName(toUserName);  text.setCreateTime(new Date().getTime());  text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);  respMessage = MessageUtil.textMessageToXml(text);  }}

源码地址:

转载地址:http://oixna.baihongyu.com/

你可能感兴趣的文章
himawari-8卫星叶绿素a产品、_走过50年,看“风云”眼中的世界| 卫星看中国特别版...
查看>>
mybatis使用$报空指针_打破你的认知!Java空指针居然还能这样玩,90%人不知道…...
查看>>
windows mysql 重置root密码_在Windows下Mysql如何重置root用户密码
查看>>
mysql5.6 linux下载_mysql-5.6.45-linux-glibc2.12-x86_64.tar.gz下载安装
查看>>
r语言操作mysql_R语言 RMySQL连接操作mysql数据库
查看>>
mysql 整形相除_整型相除截断的技巧
查看>>
mysql备份字符集_浅谈MySQL备份字符集的问题
查看>>
dos下设置mysql密码_dos命令下修改mysql密码的方法
查看>>
交换机如何设置我能访问它但他不能访问我_“交换机”有什么作用?怎样使用?...
查看>>
数据结构基本操作_R中的数据结构简介及类别变量的基本操作
查看>>
微分方程解法总结_视频教学:线性微分方程解的结构、问题类型及求解思路与方法...
查看>>
blt功能_bitblt()用法
查看>>
MySQL中level的用法_leveldb使用 (转载)
查看>>
卷积神经网络由谁提出_科研人员提出一种基于卷积循环神经网络的单通道渐进语音增强方法...
查看>>
python 曲线拟合求参数_Python:查找任意曲线的拟合参数数量
查看>>
python批量生成图片_python日常实用技能:如何利用Python批量生成任意尺寸的图片...
查看>>
python爱好者社区公众号历史文章合集_GitHub - thinkingpy/weixin_crawler: 高效微信公众号历史文章和阅读数据爬虫powered by scrapy...
查看>>
ranger安装hbase插件_ranger的配置与使用
查看>>
mysql模板文件_MySQL 配置文件模板
查看>>
mysql连接数据库测试连接成功_java连接mysql数据库及测试是否连接成功的方法
查看>>