摘要:想要實現自定義菜單的功能,需要有已認證訂閱號和已認證服務號。測試時可以嘗試取消關注公眾賬號后再次關注,則可以看到創建后的效果。
想要實現自定義菜單的功能,需要有已認證訂閱號和已認證服務號。對于測試開發來說,可以直接申請一個測試賬號:http://mp.weixin.qq.com/debug...
同樣需要token的驗證,前期接口已經定義好了,直接拿來就可以
根據開發者文檔,自定義菜單注意:
1、自定義菜單最多包括3個一級菜單,每個一級菜單最多包含5個二級菜單。 2、一級菜單最多4個漢字,二級菜單最多7個漢字,多出來的部分將會以“...”代替。 3、創建自定義菜單后,菜單的刷新策略是,在用戶進入公眾號會話頁或公眾號profile頁時,如果發現上一次拉取菜單的請求在5分鐘以前,就會拉取一下菜單,如果菜單有更新,就會刷新客戶端的菜單。測試時可以嘗試取消關注公眾賬號后再次關注,則可以看到創建后的效果。
自定義菜單接口可實現多種類型按鈕,如下:
接口調用請求說明
http請求方式:POST(請使用https協議) https://api.weixin.qq.com/cgi...
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數據包如下:
{"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 @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應該這樣定義:
* 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方法就好了
找到測試二維碼,掃描關注,可以看到菜單已經有啦?。?/p>
如果修改了話,可以取消關注再添加關注,就能看到更改信息后的菜單信息。
對于菜單的點擊事件,可以回到我們的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); } }
源碼地址:https://github.com/zhouminpz/...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/35957.html
摘要:想要實現自定義菜單的功能,需要有已認證訂閱號和已認證服務號。測試時可以嘗試取消關注公眾賬號后再次關注,則可以看到創建后的效果。 想要實現自定義菜單的功能,需要有已認證訂閱號和已認證服務號。對于測試開發來說,可以直接申請一個測試賬號:http://mp.weixin.qq.com/debug... 同樣需要token的驗證,前期接口已經定義好了,直接拿來就可以 showImg(https...
摘要:準備寫一個系列文章,記錄微信公眾號的開發過程,也希望能為同為開發的提供一些思路,不才,見諒。微信公眾號分為編輯模式和開發者模式,一旦啟用了開發者模式,前期的一些例如自動回復菜單等會失效,望周知。 準備寫一個系列文章,記錄微信公眾號的開發過程,也希望能為同為開發的提供一些思路,不才,見諒。 微信公眾號分為編輯模式和開發者模式,一旦啟用了開發者模式,前期的一些例如自動回復、菜單等會失效,望...
摘要:準備寫一個系列文章,記錄微信公眾號的開發過程,也希望能為同為開發的提供一些思路,不才,見諒。微信公眾號分為編輯模式和開發者模式,一旦啟用了開發者模式,前期的一些例如自動回復菜單等會失效,望周知。 準備寫一個系列文章,記錄微信公眾號的開發過程,也希望能為同為開發的提供一些思路,不才,見諒。 微信公眾號分為編輯模式和開發者模式,一旦啟用了開發者模式,前期的一些例如自動回復、菜單等會失效,望...
閱讀 2881·2021-09-28 09:36
閱讀 3608·2021-09-27 13:59
閱讀 2484·2021-08-31 09:44
閱讀 2277·2019-08-30 15:54
閱讀 2352·2019-08-30 15:44
閱讀 1180·2019-08-30 13:45
閱讀 1222·2019-08-29 18:38
閱讀 1206·2019-08-29 18:37