1. 程式人生 > >springboot框架開發微信公眾號(四)之自定義選單的使用

springboot框架開發微信公眾號(四)之自定義選單的使用

關於自定義選單,微信為我們提供了幾個介面分別是建立、查詢和刪除。我們平時使用最多的就是建立選單了,往往我們執行了建立選單的程式碼後,選單並不會當即在我們關注過的公眾號上顯示出來,我們可以通過重新關注的方法,來檢視我們建立或更改後的選單。下面就來跟筆者來學一學這些介面的使用方法吧

一、由於這幾個介面都是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為空");
		}	
	}
}