關於自定義菜單,微信爲我們提供了幾個接口分別是創建、查詢和刪除。我們平時使用最多的就是創建菜單了,往往我們執行了創建菜單的代碼後,菜單並不會當即在我們關注過的公衆號上顯示出來,我們可以通過重新關注的方法,來查看我們創建或更改後的菜單。下面就來跟筆者來學一學這些接口的使用方法吧
一、由於這幾個接口都是https協議,所以我們首先要能讓我們的程序可以請求https。
對於https請求,我們需要一個證書信任管理器, 這個管理器類需要自己定義,但需要實現X509TrustManager接口代碼如下:
public class MyX509TrustManager implements X509TrustManager{
/**
* 該方法用於檢查客戶端的證書,若不信則拋出異常
* 由於我們不需要對客戶端進行認證,可以不做任何處理
*/
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateEncodingException{
}
/**
* 該方法用於檢驗服務器端的證書,若不信任則拋出異常
* 通過自己實現該方法,可以使之信任我們指定的任何證書
* 在實現該方法時,也可以不做任何處理,即一個空的方法實現
* 由於不會拋出異常,它就會信任任何證書
*/
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateEncodingException{
}
/**
* 返回收信任的X509證書數組
*/
@Override
public X509Certificate[] getAcceptedIssuers(){
return null;
}
發起https請求工具類
/**
*
* @Description: 發起https請求並獲取結果
* @Parameters: requestUrl 請求地址。 需要寫全地址,即前邊的http必須寫上,不能只寫www.baidu.com這樣的。
* requestMethod 請求方式(GET、POST)
* outputStr 我們在發起請求的時候傳遞參數到所要請求的服務器,
* 要傳遞的參數也要看接口文檔確定格式,一般是封裝成json或xml.
* @Return: JSONObject(通過JSONObject.get(key)的方式獲取json對象的屬性值)
* @Create Date: 2018年9月19日上午8:20:33
* @Version: V1.00
* @author: 來日可期
*/
@Component
public class HttpRequestUtil {
Logger logger = LoggerFactory.getLogger(HttpRequestUtil.class);
public JSONObject httpsRequest(String requestUrl,String requestMethod,String outputStr){
//初始化一個json對象
JSONObject jsonObject = null;
try {
//創建SSLContext對象,並使用我們指定的信任管理器初始化
TrustManager[] tmManagers = {new MyX509TrustManager()};
SSLContext sslContext = SSLContext.getInstance("SSL","SunJSSE");
sslContext.init(null, tmManagers, new java.security.SecureRandom());
//從上述SSLContext對象中得到SSLSocketFactory對象
SSLSocketFactory sslSocket = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection httpsURLConnection = (HttpsURLConnection)url.openConnection();
httpsURLConnection.setSSLSocketFactory(sslSocket);
httpsURLConnection.setDoOutput(true); /*httpUrlConnection.setDoOutput(true);以後就可以使用conn.getOutputStream().write() httpUrlConnection.setDoInput(true);以後就可以使用conn.getInputStream().read(); get請求用不到conn.getOutputStream(),因爲參數直接追加在地址後面,因此默認是false。 post請求(比如:文件上傳)需要往服務區傳輸大量的數據,這些數據是放在http的body裏面的,因此需要在建立連接以後,往服務端寫數據。 因爲總是使用conn.getInputStream()獲取服務端的響應,因此默認值是true。 */
httpsURLConnection.setDoInput(true);
httpsURLConnection.setUseCaches(false);
//設置請求方式 GET/POST
httpsURLConnection.setRequestMethod(requestMethod);
if ("GET".equalsIgnoreCase(requestMethod)) { //不考慮大小寫。如果兩個字符串的長度相等,並且兩個字符串中的相應字符都相等(忽略大小寫),則認爲這兩個字符串是相等的。
httpsURLConnection.connect();
}
//當有數據需要提交時,往服務器端寫內容 也就是發起http請求需要帶的參數
if (null != outputStr) {
OutputStream outputStream = httpsURLConnection.getOutputStream();
//注意編碼格式,防止中文亂碼
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
//獲得輸入流 讀取服務器端返回的內容
InputStream inputStream = httpsURLConnection.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer stringBuffer = new StringBuffer();
while((str = bufferedReader.readLine()) != null){
stringBuffer.append(str);
}
//釋放資源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
httpsURLConnection.disconnect();
//將字符串轉換爲json對象
jsonObject = JSONObject.fromObject(stringBuffer.toString());
System.out.println("JSONObject---------------------->"+jsonObject);
} catch (ConnectException ce) {
logger.error("Weixin server connection timed out.");
} catch(Exception e){
logger.error("https request error:{}",e);
}
return jsonObject;
}
}
二、封裝菜單類,共有五種,將它們放到一個包下
/**
* @Description: 按鈕的基類(每個按鈕對象都有一個共同的name屬性,
* 因此需要定義一個按鈕對象的基類,所有按鈕對象都需要繼承該類)
* @Parameters:
* @Return:
* @Create Date: 2018年3月10日上午9:30:27
* @Version: V1.00
* @author: 來日可期
*/
public class Button {
private String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
/**
* @Description: click類型的按鈕(有type、name和key3個屬性)
* @Parameters:
* @Return:
* @Create Date: 2018年3月10日上午9:35:30
* @Version: V1.00
* @author: 來日可期
*/
public class ClickButton 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;
}
}
/**
* @Description: view類型的按鈕(有type、name、url三個屬性)
* @Parameters:
* @Return:
* @Create Date: 2018年3月10日上午9:39:06
* @Version: V1.00
* @author: 來日可期
*/
public class ViewButton extends Button{
public String type;
public 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;
}
}
/**
* @Description: 複合類型的按鈕(也就是含有子菜單的一級菜單)
* @Parameters:
* @Return:
* @Create Date: 2018年3月10日上午9:43:13
* @Version: V1.00
* @author: 來日可期
*/
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;
}
}
/**
* @Description: 菜單
* @Parameters:
* @Return:
* @Create Date: 2018年3月10日上午9:47:20
* @Version: V1.00
* @author: 來日可期
*/
public class Menu {
private Button[] button;
public Button[] getButton() {
return button;
}
public void setButton(Button[] button) {
this.button = button;
}
}
三、利用封裝的菜單類和按鈕類,編輯自己的自定義菜單
click類型的按鈕,key值對應微信請求消息中MsgType(請求消息的類型)中Event(事件)的EventKey(事件的key值)
package com.b505.weixin.util;
import com.b505.weixin.menu.Button;
import com.b505.weixin.menu.ClickButton;
import com.b505.weixin.menu.Menu;
import com.b505.weixin.menu.ViewButton;
/**
* @Description: 菜單管理器類
* @Parameters:
* @Return:
* @Create Date: 2018年9月28日下午4:33:24
* @Version: V1.00
* @author: 來日可期
*/
public class WechatMenuManagerUtil {
/**
* @Description: 定義菜單結構
* @Parameters:
* @Return:
* @Create Date: 2018年9月28日下午5:36:08
* @Version: V1.00
* @author: 來日可期
*/
public Menu getMenu(){
ClickButton firstClickButton = new ClickButton();
firstClickButton.setName("功能圖文");
firstClickButton.setKey("function");
firstClickButton.setType("click");
ViewButton firstViewButton = new ViewButton();
firstViewButton.setName("聯繫我們");
firstViewButton.setType("view");
firstViewButton.setUrl("");
Menu menu = new Menu();
Button[] boButtons = {firstClickButton,firstViewButton};
menu.setButton(boButtons);
return menu;
}
}
四、編寫自定義菜單工具類,調用微信接口,實現菜單的創建,查詢與刪除
如果是創建菜單,則需要將第三步中編輯好的菜單一併請求給創建自定義菜單接口
package com.b505.weixin.util;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.b505.util.HttpRequestUtil;
import com.b505.weixin.menu.Menu;
/**
* @Description: 自定義菜單工具類,包括菜單的創建、查詢、刪除
* @Parameters:
* @Return:
* @Create Date: 2018年9月19日下午7:46:27
* @Version: V1.00
* @author: 來日可期
*/
@Component
public class WechatMenuUtil {
Logger logger = LoggerFactory.getLogger(WechatMenuUtil.class);
@Autowired
private HttpRequestUtil httpRequestUtil;
/**
* @Parameters: menu 菜單實例
* accessToken 憑證
* @Return: true false
* @Return:
* @Create Date: 2018年9月22日上午7:45:53
* @Version: V1.00
* @author: 來日可期
*/
public boolean creatMenu(Menu menu,String accessToken){
boolean result = false;
String url = WechatConstants.MENU_CREATE_URL.replace("ACCESS_TOKEN", accessToken);
//將菜單對象轉換成JSON字符串
String jsonMenu = JSONObject.fromObject(menu).toString();
//發起POST請求創建菜單
JSONObject jsonObject = httpRequestUtil.httpsRequest(url, "POST", jsonMenu);
if (null != jsonObject) {
int errorCode = jsonObject.getInt("errcode");
String errorMsg = jsonObject.getString("errmsg");
if (0== errorCode) {
result = true;
} else {
result = false;
logger.error("創建菜單失敗 errcode:{} errmsg:{} ",errorCode,errorMsg);
}
}
return result;
}
/**
*
* @Description: 查詢菜單
* @Parameters:
* @Return:
* @Create Date: 2018年3月13日下午2:24:02
* @Version: V1.00
* @author: 來日可期
*/
public String getMenu(String accessToken){
String result = null;
String requestUrl = WechatConstants.MENU_GET_URL.replace("ACCESS_TOKEN", accessToken);
//發起GET請求查詢菜單
JSONObject jsonObject = httpRequestUtil.httpsRequest(requestUrl, "GET", null);
if (null != jsonObject) {
result = jsonObject.toString();
}
return result;
}
/**
*
* @Description: 刪除菜單
* @Parameters:
* @Return:
* @Create Date: 2018年3月13日下午2:31:15
* @Version: V1.00
* @author: 來日可期
*/
public boolean deleteMenu(String accessToken){
boolean result = false;
String requestUrl = WechatConstants.MENU_DELETE_URL.replace("ACCESS_TOKEN", accessToken);
//發起GET請求刪除菜單
JSONObject jsonObject = httpRequestUtil.httpsRequest(requestUrl, "GET", null);
if (null != jsonObject) {
int errorCode = jsonObject.getInt("errcode");
String errorMsg = jsonObject.getString("errmsg");
if (0== errorCode) {
result = true;
} else {
result = false;
logger.error("創建菜單失敗 errcode:{} errmsg:{} ",errorCode,errorMsg);
}
}
return result;
}
}
五、編寫獲取access_token的工具類
調用微信高級接口需要憑證access_token,所以在這之前我們需要獲取access_token.
package com.b505.weixin.util;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.b505.util.HttpRequestUtil;
import com.b505.weixin.pojo.AccessToken;
@Component
public class WechatCommonUtil {
Logger logger = LoggerFactory.getLogger(WechatCommonUtil.class);
//獲取access_token接口
private static String token_url = WechatConstants.ACCESS_TOKEN_URL;
@Autowired
HttpRequestUtil httpRequestUtil;
/**
* @Description: 獲取微信調用高級接口的憑證(access_token)
* @Parameters:
* @Return:
* @Create Date: 2018年9月28日下午12:22:55
* @Version: V1.00
* @author: 來日可期
*/
public AccessToken getAccessToken(String appid,String appsecret){
//將公衆號的appid和appsecret替換進url
String url = token_url.replace("APPID", appid).replace("APPSECRET", appsecret);
AccessToken accessToken = new AccessToken();
//發起get請求獲取憑證
JSONObject jsonObject = httpRequestUtil.httpsRequest(url,"GET",null);
logger.info("獲取到的json格式的Token爲:"+jsonObject);
if (jsonObject!=null) {
try {
accessToken.setAccess_token(jsonObject.getString("access_token"));
accessToken.setExpires_in(jsonObject.getInt("expires_in"));
} catch (Exception e) {
accessToken = null;
//獲取token失敗
logger.error("獲取token失敗 errcode:{} errmsg:{}",
jsonObject.getInt("errcode"),
jsonObject.getString("errmsg"));
}
}
return accessToken;
}
}
六、編寫主方法,進行自定義菜單創建
package com.b505.weixin.main;
import com.b505.weixin.menu.Menu;
import com.b505.weixin.util.WechatCommonUtil;
import com.b505.weixin.util.WechatConstants;
import com.b505.weixin.util.WechatMenuManagerUtil;
import com.b505.weixin.util.WechatMenuUtil;
/**
* @Description: 創建自定義菜單主方法
* @Parameters:
* @Return:
* @Create Date: 2018年9月28日下午2:25:33
* @Version: V1.00
* @author: 來日可期
*/
public class WechatCreatDefaultMenu {
public static void main(String[] args){
WechatCommonUtil wechatCommonUtil = new WechatCommonUtil();
WechatMenuUtil wechatMenuUtil = new WechatMenuUtil();
WechatMenuManagerUtil wechatMenuManagerUtil = new WechatMenuManagerUtil();
String appid = "公衆號的appid";
String appsecret = "公衆號的appsecret";
//獲取access_token
String accessToken = wechatCommonUtil.getAccessToken(appid, appsecret).getAccess_token();
//獲取菜單結構
Menu menu = wechatMenuManagerUtil.getMenu();
if (accessToken!=null) {
//生成菜單
boolean result = wechatMenuUtil.creatMenu(menu, accessToken);
if (result) {
System.out.println("菜單創建成功");
}else {
System.out.println("菜單創建失敗");
}
}else {
System.out.println("token爲空");
}
}
}