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为空");
		}	
	}
}

猜你喜欢

转载自blog.csdn.net/qq_40715775/article/details/82776882