微信公众平台自定义菜单创建,菜单点击/关注/取消关注事件(第18篇)

2017年08月21日 19:38 | 2301次浏览 作者原创 版权保护

自定义菜单创建

package com.test.weixin;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import net.sf.json.JSONObject;

/***
 * @author V型知识库 www.vxzsk.com
 * linfanhehe@163.com
 *
 */
public class TestMenu {
	
	public static String sendPost(String requrl,String param){
		 URL url;
		  String sTotalString="";  
		try {
			url = new URL(requrl);
			 URLConnection connection = url.openConnection(); 
			 
			 connection.setRequestProperty("accept", "*/*");
			 connection.setRequestProperty("connection", "Keep-Alive");
			 connection.setRequestProperty("Content-Type", "text/xml");
			// connection.setRequestProperty("Content-Length", body.getBytes().length+"");
			 connection.setRequestProperty("User-Agent",
                    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)");
			 
			 
		        connection.setDoOutput(true);  
		        OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "utf-8");  
		        out.write(param); // 向页面传递数据。post的关键所在!  
		        out.flush();  
		        out.close();  
		        // 一旦发送成功,用以下方法就可以得到服务器的回应:  
		        String sCurrentLine;  
		      
		        sCurrentLine = "";  
		        sTotalString = "";  
		        InputStream l_urlStream;  
		        l_urlStream = connection.getInputStream();  
		        // 传说中的三层包装阿!  
		        BufferedReader l_reader = new BufferedReader(new InputStreamReader(  
		                l_urlStream));  
		        while ((sCurrentLine = l_reader.readLine()) != null) {  
		            sTotalString += sCurrentLine + "\r\n";  
		  
		        }  
		        
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}  
	       
	        System.out.println(sTotalString);  
	        return sTotalString;
	 }
	/***
	 * 模拟get请求
	 * @param url
	 * @param charset
	 * @param timeout
	 * @return
	 */
	 public static String sendGet(String url, String charset, int timeout)
	  {
	    String result = "";
	    try
	    {
	      URL u = new URL(url);
	      try
	      {
	        URLConnection conn = u.openConnection();
	        conn.connect();
	        conn.setConnectTimeout(timeout);
	        BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), charset));
	        String line="";
	        while ((line = in.readLine()) != null)
	        {
	        
	          result = result + line;
	        }
	        in.close();
	      } catch (IOException e) {
	        return result;
	      }
	    }
	    catch (MalformedURLException e)
	    {
	      return result;
	    }
	  
	    return result;
	  }

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		String appid="你公众号基本设置里的应用id";//应用ID
		String appSecret="你公众号基本设置里的应用密钥";//(应用密钥)
        String url ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appid+"&secret="+appSecret+"";
	    String jsonData=TestMenu.sendGet(url, "utf-8", 10000);
	    JSONObject jsonObj = JSONObject.fromObject(jsonData);
	    //获取access_token
	    String accessToken = jsonObj.getString("access_token");
	    System.out.println("accessToken:"+accessToken);
	    //自定义菜单json字符串
	    String menuJson = "{"
     +"\"button\":["
     +"{"	
     +" \"type\":\"click\","
          +"\"name\":\"今日歌曲\","
          +"\"key\":\"123\""
          +"},"
          +"{"
          +" \"name\":\"菜单2\","
           +"\"sub_button\":["
           +"{"	
               +"\"type\":\"view\","
               +"\"name\":\"点我直接跳转\","
               +"\"url\":\"https://www.vxzsk.com/xxx.do\""
               +"},"
               +"{"
               +"\"type\":\"view\","
               +"\"name\":\"视频\","
               +"\"url\":\"http://v.qq.com/\""
               +"},"
               +"{"
               +"\"type\":\"click\","
               +"\"name\":\"赞一下我们\","
               +"\"key\":\"456\""
               +"}]"
               +"}]"
               +" }";
             String url2 = " https://api.weixin.qq.com/cgi-bin/menu/create?access_token="+accessToken;
             String backData = TestMenu.sendPost(url2, menuJson);
             System.out.println("自定义菜单创建返回:"+backData);
	    
	    

	}

}

在main方法中填写自己的应用id 和应用秘钥,直接运行main方法即可生成自定义菜单,自定义菜单一次生成永久有效,所以不用每次启动创建菜单。

咱们来看看115行-143行自定义菜单json字符串,在这里 我们定义了两个父菜单,第一个为点击事件click类型,并且key的值为123,第二个菜单2,其中有个view类型,意思是点击"点我直接跳转",就会直接跳转到https://www.vxzsk.com/weixin/xxx.do界面,还有个click类型为"赞一下我们",key的值为456,为什么一直强调key的值呢,在接下来的代码中会用到。这里的举例为了让读者更加明白,不在用前几章节封装好的代码。

处理自定义菜单click事件

事件类型介绍:

在微信中有事件请求是消息请求中的一种。请求类型为:event

而event事件类型又分多种事件类型,具体分

关注:subscribe

取消关注:unsubscribe

自定义菜单点击:CLICK

根据上面的类型分类可建对应的常量

/**
	 * 请求消息类型:推送
	 */
	public static final String REQ_MESSAGE_TYPE_EVENT = "event";

	/**
	 * 事件类型:subscribe(订阅)
	 */
	public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";

	/**
	 * 事件类型:unsubscribe(取消订阅)
	 */
	public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";

	/**
	 * 事件类型:CLICK(自定义菜单点击事件)
	 */
	public static final String EVENT_TYPE_CLICK = "CLICK";

服务器配置URL的servelet代码编写

package com.test;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.test.util.MessageUtil;

/**
 * 核心请求处理类
 * @author V型知识库 www.vxzsk.com
 *
 * doGet方法里 有个weixinTest,这个是公众管理平台里面自己设置的token 大家根据自己的token替换
 */
public class WeChatServlet extends HttpServlet {
		
	private static final long serialVersionUID = 1508798736675904038L;
	
	/**
	 * 确认请求来自微信服务器
	 */
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("V型知识库原创www.vxzsk.com");
		// 微信加密签名
		String signature = request.getParameter("signature");
		System.out.println("微信加密签名signature:-----------------------"+signature);
		// 时间戳
		String timestamp = request.getParameter("timestamp");
		System.out.println("时间戳timestamp:-----------------------"+timestamp);
		// 随机数
		String nonce = request.getParameter("nonce");
		System.out.println("随机数nonce:-----------------------"+nonce);
		// 随机字符串
		String echostr = request.getParameter("echostr");
		System.out.println("随机字符串echostr:-----------------------"+echostr);
		//System.out.println("token-----------------------:"+token);
		
		PrintWriter out = response.getWriter();
		// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
		if (SignUtil.checkSignature("weixinTest", signature, timestamp, nonce)) {
			out.print(echostr);
			//System.out.println("这是:"+echostr);
		}
		out.close();
		out = null;
	}

	/**
	 * 处理微信服务器发来的消息
	 * 实例源码在文章顶部有下载连接
	 */
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
		System.out.println("微信服务器发来消息------------");
        // 将请求、响应的编码均设置为UTF-8(防止中文乱码)
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        try{
        //xml请求解析
        Map<String, String> requestMap = MessageUtil.parseXml(request);//接收微信发过来的xml格式
        //发送方帐号(open_id)
        String fromUserName = requestMap.get("FromUserName");
        //公众帐号
        String toUserName = requestMap.get("ToUserName");
        //消息类型
        String msgType = requestMap.get("MsgType");
        //消息创建时间
        String createTime = requestMap.get("CreateTime");
        //微信服务器post过来的内容
        String weixinContent = requestMap.get("Content");
        String respMessage =null;
      //事件推送
	    if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
			//事件类型
	     String eventType = requestMap.get("Event");
			//订阅
			if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {
			  //自定义回复内容
				respMessage = "<xml>"
		                +"<ToUserName><![CDATA["+fromUserName+"]]></ToUserName>"
		                +"<FromUserName><![CDATA["+toUserName+"]]></FromUserName>"
		                +"<CreateTime>12345678</CreateTime>"
		                +"<MsgType><![CDATA[text]]></MsgType>"
		                +"<Content><![CDATA[谢谢订阅。]]></Content>"
		                +"</xml>";
			}
			//取消订阅
			else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {
				//取消订阅后用户再收不到公众号发送的消息,因此不需要回复消息
			}
			//自定义菜单点击事件
			else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {
				System.out.println("菜单点击事件"+eventType);
				//事件KEY值,与创建自定义菜单时指定的KEY值对应
				String eventKey = requestMap.get("EventKey");
			    System.out.println("键值----:"+eventKey);
			    if(eventKey.equals("123")){
			    	//自定义回复内容
					respMessage = "<xml>"
			                +"<ToUserName><![CDATA["+fromUserName+"]]></ToUserName>"
			                +"<FromUserName><![CDATA["+toUserName+"]]></FromUserName>"
			                +"<CreateTime>12345678</CreateTime>"
			                +"<MsgType><![CDATA[text]]></MsgType>"
			                +"<Content><![CDATA[点击的是今日歌曲,key为123]]></Content>"
			                +"</xml>";
			    	
			    }else if(eventKey.equals("456")){
			    	//自定义回复内容
					respMessage = "<xml>"
			                +"<ToUserName><![CDATA["+fromUserName+"]]></ToUserName>"
			                +"<FromUserName><![CDATA["+toUserName+"]]></FromUserName>"
			                +"<CreateTime>12345678</CreateTime>"
			                +"<MsgType><![CDATA[text]]></MsgType>"
			                +"<Content><![CDATA[点击的是赞一下我们,key为456]]></Content>"
			                +"</xml>";
			    	
			    }
			 }
	    }
       
       // 响应回复消息
                PrintWriter out = response.getWriter();
                out.print(respMessage);
                out.close();
        }catch(Exception e){
            e.printStackTrace();
        }
        
	}
}

代码说明:注意第81行开始,对事件推送开始判断,首先第一个if判断消息类型,本案例消息类型应该为推送,msgType的值应该为event。

接下来第83行 String eventType = requestMap.get("Event");代码,判断事件的类型,我们在文章的开头部分说过了,消息类型一共目前有三个分别为 关注:subscribe,取消关注:unsubscribe,自定义菜单点击:CLICK,那么eventType的值应该在unsubscribe,subscribe,CLICK三种之间。

第94行开始,当eventType的值为CLICK时,意思就是自定义菜单的click触发的,那么我们在自定义菜单设置了两个key,其中一个key的值为123,另一个key的值为456也就是第104行String eventKey = requestMap.get("EventKey");代码中eventKey的值可能为123也有可能为456,然后判断进行相应的处理。

微信官方给的参数说明

用户点击自定义菜单后,如果菜单按钮设置为click类型,则微信会把此次点击事件推送给开发者,注意view类型(跳转到URL)的菜单点击不会上报。

要注意的是 EventKey 这个参数,与菜单创建的时候中的key值是对应的。就像我们创建菜单的时候分别创建了key值为123和456的菜单,然后在接下来的代码中判断EventKey的值是否为123和456。(仔细看104行代码)

其它工具类代码

SignUtil

package com.test;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/***
 * 
 * @author V型知识库 www.vxzsk.com
 * 
 * 
 */
public class SignUtil {
		
	/**
	* 验证签名
	* 
	* @param signature
	* @param timestamp
	* @param nonce
	* @return
	*/
	public static boolean checkSignature(String token, String signature, String timestamp, String nonce) { 		
		String[] arr = new String[] { token, timestamp, nonce };
		// 将token、timestamp、nonce三个参数进行字典序排序
		Arrays.sort(arr);
		StringBuilder content = new StringBuilder();
		for (int i = 0; i < arr.length; i++) {
			content.append(arr[i]);
		}
		MessageDigest md = null;
		String tmpStr = null;
		try {
		md = MessageDigest.getInstance("SHA-1");
		// 将三个参数字符串拼接成一个字符串进行sha1加密
		byte[] digest = md.digest(content.toString().getBytes());
		tmpStr = byteToStr(digest);
		} catch (NoSuchAlgorithmException e) {
		e.printStackTrace();
		}
		content = null;
		// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
		return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
	}
	/**
	* 将字节数组转换为十六进制字符串	
	* @param byteArray
	* @return
	*/
	private static String byteToStr(byte[] byteArray) {
		String strDigest = "";
		for (int i = 0; i < byteArray.length; i++) {
		strDigest += byteToHexStr(byteArray[i]);
		}
		return strDigest;
	}
	/**
	* 将字节转换为十六进制字符串
	* @param mByte
	* @return
	*/
	private static String byteToHexStr(byte mByte) {
		char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
		char[] tempArr = new char[2];
		tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
		tempArr[1] = Digit[mByte & 0X0F];
		String s = new String(tempArr);
		return s;
	}
}

MessageUtil

package com.test.message;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/***
 * 
 * @author V型知识库 www.vxzsk.com
 *
 * 
 */
public class MessageUtil {
	/**
	 * 请求消息类型:推送
	 */
	public static final String REQ_MESSAGE_TYPE_EVENT = "event";

	/**
	 * 事件类型:subscribe(订阅)
	 */
	public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";

	/**
	 * 事件类型:unsubscribe(取消订阅)
	 */
	public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";

	/**
	 * 事件类型:CLICK(自定义菜单点击事件)
	 */
	public static final String EVENT_TYPE_CLICK = "CLICK";
    /**
     * 解析微信发来的请求(XML)
     * 
     * @param request
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
        // 将解析结果存储在HashMap中
        Map<String, String> map = new HashMap<String, String>();
        // 从request中取得输入流
        InputStream inputStream = request.getInputStream();
         
        // 读取输入流
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        // 得到xml根元素
        Element root = document.getRootElement();
        // 得到根元素的所有子节点
        List<Element> elementList = root.elements();
        // 遍历所有子节点
        for (Element e : elementList) {
            map.put(e.getName(), e.getText());
        }
        // 释放资源
        inputStream.close();
        inputStream = null;
        return map;
    }
}


此文章本站原创,地址 https://www.vxzsk.com/128.html   转载请注明出处!谢谢!

感觉本站内容不错,读后有收获?小额赞助,鼓励网站分享出更好的教程