微信服务号开发之创建自定义菜单

本文介绍如何创建微信服务号的自定义菜单,并详细解释了ACCESS_TOKEN的获取及存储方式,包括定时更新机制。

一、思路

  1. 获取全局ACCESS_TOKEN
    创建微信自定义菜单的接口为:
    https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
    故需要先取得获取全局ACCESS_TOKEN
  2. 创建自定义菜单
    关于自定义菜单的相关介绍请参考微信服务号开发官方文档:
    https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141013
  3. 向微信接口发送创建自定义菜单的请求

二、具体代码

  1. 开发过程中用到的微信接口类 Constant
    public class Constant {
    	// 微信服务号开发 账号
    	public static final String appid = PropertyUtil.getProperty("appId");
    	
    	// 微信服务号开发 密码
    	public static final String secret = PropertyUtil.getProperty("appSecret");
    	
    	// 微信创建菜单接口
    	public static final String createMenu = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
    
    	// 微信基础支持access_token接口,也称为全局access_token接口
    	public static final String access_token = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    	
    }
     其中 PropertyUtil.getProperty 方法如下
    import java.util.Properties;
    
    import org.apache.log4j.Logger;
    import org.springframework.core.io.ClassPathResource;
    /**
     * 获取配置文件信息
     * @author liguiji
     *
     */
    public class PropertyUtil {
    	
    	private static final Logger LOG = Logger.getLogger(PropertyUtil.class);
    	private static final Properties props = new Properties();
    
    	static {
    		init();
    	}
    
    	public static Properties getProperties() {
    		return props;
    	}
    
    	private static void init() {
    
    		String mpConfig = "/mp.properties";
    
    		ClassPathResource cr = null;
    		try {
    			cr = new ClassPathResource(mpConfig);
    			props.load(cr.getInputStream());
    			LOG.info("装入default主配置文件:" + mpConfig);
    		} catch (Exception e) {
    			LOG.info("装入default主配置文件" + mpConfig + "失败!", e);
    		}
    		
    		LOG.info("系统配置属性装载完毕");
            LOG.info("******************属性列表***************************");
            for(String propertyName : props.stringPropertyNames()){
                LOG.info("  "+propertyName+" = "+props.getProperty(propertyName));
            }
            LOG.info("***********************************************************");
    	}
    
    	public static String getProperty(String name) {
    		String value = props.getProperty(name);
    
    		return value;
    	}
    
    	public static void setProperty(String name, String value) {
    		props.setProperty(name, value);
    	}
    }

     

  2. 获取全局access_token
    public static AccessToken getAccessToken() {
    	AccessToken accessToken = null;
    
    	// 将接口中的APPID和APPSECRET替换为自己的appid和secret
    	String requestUrl = Constant.access_token.replace("APPID",
    			Constant.appid).replace("APPSECRET", Constant.secret);
    		
    	// 想微信发送获取AccessToken的请求
    	JSONObject jsonObject = HTTPSUtil.httpRequest(requestUrl, "GET", null);
    		
    	// 如果请求成功
    	if (null != jsonObject) {
    		try {
    			accessToken = new AccessToken();
    			accessToken.setToken(jsonObject.getString("access_token"));
    			accessToken.setExpiresIn(jsonObject.getInt("expires_in"));
    		} catch (JSONException e) {
    			accessToken = null;
    			// 获取token失败
    			System.out.println("获取token失败 errcode:{} errmsg:{}"
    					+ jsonObject.getInt("errcode")
    					+ jsonObject.getString("errmsg"));
    		}
    	}
    	return accessToken;
    }

    其中 AccessToken 类如下

    public class AccessToken {
    	private String token;  //token
    	private int expiresIn; //凭证有效时间 ,单位秒
    	
    	public String getToken() {
    		return token;
    	}
    	public void setToken(String token) {
    		this.token = token;
    	}
    	public int getExpiresIn() {
    		return expiresIn;
    	}
    	public void setExpiresIn(int expiresIn) {
    		this.expiresIn = expiresIn;
    	}
    	
    }

    httpRequest方法如下

    public class HTTPSUtil {
    	private static Logger log = LoggerFactory.getLogger(HTTPSUtil.class);
    	/** 
         * 发起https请求并获取结果 
         *  
         * @param requestUrl 请求地址 
         * @param requestMethod 请求方式(GET、POST) 
         * @param outputStr 提交的数据 
         * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值) 
         */  
    	public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {  
            JSONObject jsonObject = null;  
            StringBuffer buffer = new StringBuffer();  
            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 httpUrlConn = (HttpsURLConnection) url.openConnection();  
                httpUrlConn.setSSLSocketFactory(ssf);  
      
                httpUrlConn.setDoOutput(true);  
                httpUrlConn.setDoInput(true);  
                httpUrlConn.setUseCaches(false);  
                // 设置请求方式(GET/POST)  
                httpUrlConn.setRequestMethod(requestMethod);  
      
                if ("GET".equalsIgnoreCase(requestMethod)) 
                    httpUrlConn.connect();  
      
                // 当有数据需要提交时  
                if (null != outputStr) {  
                    OutputStream outputStream = httpUrlConn.getOutputStream();  
                    // 注意编码格式,防止中文乱码  
                    outputStream.write(outputStr.getBytes("UTF-8"));  
                    outputStream.close();  
                }  
      
                // 将返回的输入流转换成字符串  
                InputStream inputStream = httpUrlConn.getInputStream();  
                InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");  
                BufferedReader bufferedReader = new BufferedReader(inputStreamReader);  
      
                String str = null;  
                while ((str = bufferedReader.readLine()) != null) {  
                    buffer.append(str);  
                }  
                bufferedReader.close();  
                inputStreamReader.close();  
                // 释放资源  
                inputStream.close();  
                inputStream = null;  
                httpUrlConn.disconnect();  
                jsonObject = JSONObject.fromObject(buffer.toString());  
            } catch (ConnectException ce) {  
                log.error("Weixin server connection timed out.");  
            } catch (Exception e) {  
                log.error("https request error:{}", e);  
            }  
            return jsonObject;  
        }
    }

     

  3.  创建菜单类
    菜单基类  ButtonBase
    public class ButtonBase {
    	
    	// 菜单名称
    	private String name;
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    }

    二级菜单 CommonButton

    public class CommonButton extends ButtonBase {
    	
    	// 菜单名称
    	private String name;
    	
    	// 菜单类型
    	private String type;
    	
    	// 菜单key值
    	private String key;
    	
    	// 菜单url
    	private String url;
    	
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	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;
    	}
    	public String getUrl() {
    		return url;
    	}
    	public void setUrl(String url) {
    		this.url = url;
    	}
    }

    一级菜单 ComplexButton

    public class ComplexButton extends ButtonBase{
    	
    	// 菜单名称
    	private String name;
    	
    	// 二级菜单数组
    	private ButtonBase[] sub_button;
    	
    	// 菜单key值
    	private String key;
    	
    	// 菜单类型
    	private String type;
    	
    	// 菜单url
    	private String url;
    	
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public ButtonBase[] getSub_button() {
    		return sub_button;
    	}
    	public void setSub_button(ButtonBase[] sub_button) {
    		this.sub_button = sub_button;
    	}
    	public String getKey() {
    		return key;
    	}
    	public void setKey(String key) {
    		this.key = key;
    	}
    	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;
    	}
    }

    菜单类 Menu

    public class Menu {
    	
    	// 一级菜单数组
    	private ComplexButton[] button;
    
    	public ComplexButton[] getButton() {
    		return button;
    	}
    
    	public void setButton(ComplexButton[] button) {
    		this.button = button;
    	}
    }

     

三、因为基础支持ACCESS_TOKEN每天只能请求2000次,每个ACCESS_TOKEN的失效是2小时,当ACCESS_TOKEN的请求次数超过2000次后,会使得服务号无法正常运行,所以最好每隔1到2小时请求一次,期间所有需要ACCESS_TOKEN均使用该ACCESS_TOKEN,而不是请求新的ACCESS_TOKEN。此处我是将ACCESS_TOKEN存在数据库中,每隔1小时重新请求一次,网上有中控服务器的方法,可自行研究。代码如下,定时任务使用的是spring的task定时任务调度。

import org.springframework.scheduling.annotation.Scheduled;

import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import *.Constant;  // 此处路径涉及到公司隐私,故用*代替
import *.AccessToken;
import *.ServiceAccountService;

/**
 * 每隔1个小时更新一次access_token,并保存在数据库中
 * 
 * @author Administrator
 * 
 */
public class GetAccessToken {

    // spring service 类,用来跟数据库交互
	private static ServiceAccountService serviceAccountService;
	
	public static void setServiceAccountService(
			ServiceAccountService serviceAccountService) {
		GetAccessToken.serviceAccountService = serviceAccountService;
	}

	/**
	 * 从微信接口获取accessToken
	 * @return
	 */
	public static AccessToken getAccessToken() {
		AccessToken accessToken = null;

		// 将接口中的APPID和APPSECRET替换为自己的appid和secret
		String requestUrl = Constant.access_token.replace("APPID",
				Constant.appid).replace("APPSECRET", Constant.secret);
		
		// 想微信发送获取AccessToken的请求
		JSONObject jsonObject = HTTPSUtil.httpRequest(requestUrl, "GET", null);
		
		// 如果请求成功
		if (null != jsonObject) {
			try {
				accessToken = new AccessToken();
				accessToken.setToken(jsonObject.getString("access_token"));
				accessToken.setExpiresIn(jsonObject.getInt("expires_in"));
			} catch (JSONException e) {
				accessToken = null;
				// 获取token失败
				System.out.println("获取token失败 errcode:{} errmsg:{}"
						+ jsonObject.getInt("errcode")
						+ jsonObject.getString("errmsg"));
			}
		}
		return accessToken;
	}
	
	/**
	 * 从本地数据库获取accessToken
	 * @return
	 */
	public static AccessToken getAccessTokenFromLocal(){
		return serviceAccountService.getAccessTokenFromLocal();
	}
	
	/**
	 * 每隔1小时50分钟更新一次
	 */
	@Scheduled(cron = "0 0 */1 * * ?")
	public static void updateAccessToken(){
		AccessToken accessToken = getAccessToken();
		if(serviceAccountService.updateAccessToken(accessToken)>0){
			System.out.println("更新accessToken成功");
		}else{
			System.out.println("更新accessToken失败");
		}
	}
}

 

转载于:https://my.oschina.net/worriedfox/blog/1615676

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值