关于自定义菜单,微信为我们提供了几个接口分别是创建、查询和删除。我们平时使用最多的就是创建菜单了,往往我们执行了创建菜单的代码后,菜单并不会当即在我们关注过的公众号上显示出来,我们可以通过重新关注的方法,来查看我们创建或更改后的菜单。下面就来跟笔者来学一学这些接口的使用方法吧
一、由于这几个接口都是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为空");
}
}
}