想要實現自定義菜單的功能,須要有已認證訂閱號和已認證服務號。對於測試開發來講,能夠直接申請一個測試帳號:http://mp.weixin.qq.com/debug...java
一樣須要token的驗證,前期接口已經定義好了,直接拿來就能夠git
根據開發者文檔,自定義菜單注意:github
一、自定義菜單最多包括3個一級菜單,每一個一級菜單最多包含5個二級菜單。 二、一級菜單最多4個漢字,二級菜單最多7個漢字,多出來的部分將會以「...」代替。 三、建立自定義菜單後,菜單的刷新策略是,在用戶進入公衆號會話頁或公衆號profile頁時,若是發現上一次拉取菜單的請求在5分鐘之前,就會拉取一下菜單,若是菜單有更新,就會刷新客戶端的菜單。測試時能夠嘗試取消關注公衆帳號後再次關注,則能夠看到建立後的效果。
自定義菜單接口可實現多種類型按鈕,以下:json
接口調用請求說明segmentfault
http請求方式:POST(請使用https協議) https://api.weixin.qq.com/cgi...api
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" }] }] }
參數說明:服務器
返回結果
正確時的返回JSON數據包以下:app
{"errcode":0,"errmsg":"ok"}
錯誤時的返回JSON數據包以下(示例爲無效菜單名長度):dom
{"errcode":40018,"errmsg":"invalid button name size"}
以上均來自官方說明文檔。
pom引入jar包:
<!-- https://mvnrepository.com/artifact/net.sf.ezmorph/ezmorph --> <dependency> <groupId>net.sf.ezmorph</groupId> <artifactId>ezmorph</artifactId> <version>1.0.6</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils --> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.8.0</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-collections/commons-collections --> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-lang/commons-lang --> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.3</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <version>2.4</version> <classifier>jdk15</classifier> </dependency> <!-- https://mvnrepository.com/artifact/org.dom4j/dom4j --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.9</version> </dependency>
定義菜單實體類:
/** * 按鈕基類 * @author zhoumin * @create 2018-07-11 15:22 */ @Setter @Getter public class BasicButton { private String name; private String url; } /** * 普通按鈕 * * @author zhoumin * @create 2018-07-12 9:56 */ @Setter @Getter public class CommonButton extends BasicButton { private String type; private String key; } /** * 父按鈕 * @author zhoumin * @create 2018-07-11 15:24 */ @Setter @Getter public class ComplexButton extends BasicButton { private BasicButton[] sub_button; } /** * 菜單 * @author zhoumin * @create 2018-07-11 15:22 */ @Setter @Getter public class Menu { private BasicButton[] button; } /** * @author zhoumin * @create 2018-07-11 15:23 */ @Setter @Getter public class ViewButton extends BasicButton { private String type; private String name; private String url; } /** * 憑證 * @author zhoumin * @create 2018-07-11 15:22 */ @Setter @Getter public 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應該這樣定義:<br> * 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方法就行了
找到測試二維碼,掃描關注,能夠看到菜單已經有啦!!
若是修改了話,能夠取消關注再添加關注,就能看到更改信息後的菜單信息。
對於菜單的點擊事件,能夠回到咱們的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); } }