编程笔记|微信公众号开发系列教程(四)(监听关注/取消关注事件 消息接收与响应处理)

10万+IT人都在关注,史上最全面的微信公众号开发系列教程:基于Springboot开发公众号关注/取消关注事件
前言:
关于监听公众号用户关注和取消关注的消息事件,微信官方文档给出的参考内容寥寥无几,具体如何配置url,官方文档也没有具体的说明,确实很坑,让人很难懂,而且网上关于配置微信消息事件接口的讲解资料很少,大多数只讲到验证token的url的配置,很少有讲到消息接口的url配置注意事项,很多开发者朋友也经常会卡到这里,通过加笔者微信请教笔者,本篇内容是笔者经过深入钻研实现的。能读到本篇干货的读者都是幸运的,经过精心整理,笔者今天把实现过程和完整案例无私分享给广大开发者朋友。
【编程笔记|微信公众号开发系列教程(四)(监听关注/取消关注事件 消息接收与响应处理)】之前文章笔者已经实现了在测试公众号接入URL进行token验证,从本节起,开始利用微信官方提供的公众号开发文档来实现不同的交互案例。(注意:如果开发者还没有接入url进行token验证操作,请参考笔者之前的开发教程【springmvc开发微信公众号接口 微信公众号测试账号配置接口Token验证】。本案例演示效果如下:用户关注后公众号测试账号自动回复定制化的各种消息响应用户,并且可以根据用户发的消息进行智能回复。
编程笔记|微信公众号开发系列教程(四)(监听关注/取消关注事件 消息接收与响应处理)
文章图片

实现思路
1.打开公众账号测试网址http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login,扫码登陆测试公众号
2.我们打开微信公众号开发文档https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432,选择左侧"消息管理"模块中的"接收普通消息",阅读微信官方开发文档。
编程笔记|微信公众号开发系列教程(四)(监听关注/取消关注事件 消息接收与响应处理)
文章图片

文档中已经告诉我们,当普通微信用户向公众账号发送消息时,微信服务器会把该消息封装成XML数据包通过POST的方式发送到开发者填写的URL上。我们设置的URL仅仅只有一个,之前笔者分享的文章(【springmvc开发微信公众号接口 微信公众号测试账号配置接口Token验证】)中接入的url是用来做token验证的,就是微信服务器向开发者服务器发送GET请求过来,而现在是要用来做消息处理的,此时微信服务器向开发者服务器配置的url发送的是POST请求,因此我们只需要根据请求方式区分即可,配置的url和验证token配置的url保持一致即可,唯一不同的是,这次的请求接口路径上要用注解表明POST请求。详细见下面代码:

package com.chenyun.cloud.controller; import java.io.IOException; import java.io.PrintWriter; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.dom4j.DocumentException; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.alibaba.fastjson.JSONObject; import com.chenyun.cloud.utils.CheckoutUtil; import com.chenyun.cloud.utils.MessageUtil; import com.chenyun.cloud.utils.WeiXinUtil; import com.chenyun.cloud.utils.XmlUtil; /** * 创建时间:2019年3月18日 下午3:35:33 * 项目名称:weixindev * 类说明:微信开发之token验证以及各种消息处理 * @author guobinhui * @since JDK 1.8.0_51 */ @Controller public class WeixinMsgController {public static String URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN"; /** * 微信消息接收和token验证 * @param request * @param response * @throws IOException */ @RequestMapping(value="https://www.it610.com/msg/checkToken",method=RequestMethod.GET) public void weChat(HttpServletRequest request, HttpServletResponse response) throws IOException { boolean isGet = request.getMethod().toLowerCase().equals("get"); if (isGet) { // 微信加密签名 String signature = request.getParameter("signature"); // 时间戳 String timestamp = request.getParameter("timestamp"); // 随机数 String nonce = request.getParameter("nonce"); // 随机字符串 String echostr = request.getParameter("echostr"); // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败 if (signature != null && CheckoutUtil.checkSignature(signature, timestamp, nonce)) { try { boolean flag = CheckoutUtil.checkSignature(signature, timestamp, nonce); System.out.println(flag); PrintWriter print = response.getWriter(); print.write(echostr); System.out.println(echostr); print.flush(); print.close(); } catch (IOException e) { e.printStackTrace(); } } } }@RequestMapping(value="https://www.it610.com/msg/checkToken",method=RequestMethod.POST) @ResponseBody public StringresponseEvent(HttpServletRequest req, HttpServletResponse resp)throws IOException { req.setCharacterEncoding("UTF-8"); resp.setCharacterEncoding("UTF-8"); String message = "success"; try { //把微信返回的xml信息转义成map Map map = XmlUtil.xmlToMap(req); System.out.println("微信接收到的消息为:"+map.toString()); String fromUserName = map.get("FromUserName"); //消息来源用户标识 String toUserName = map.get("ToUserName"); //消息目的用户标识 String msgType = map.get("MsgType"); //消息类型(event或者text) System.out.println("消息来源于:"+fromUserName); System.out.println("openId:"+toUserName); System.out.println("消息类型为:"+msgType); String eventType = map.get("Event"); //事件类型 String nickName = getUserNickName(fromUserName); if(MessageUtil.MSGTYPE_EVENT.equals(msgType)){//如果为事件类型 if(MessageUtil.SUBSCIBE_EVENT.equals(eventType)){//处理订阅事件 String content = "欢迎关注,这是一个公众号测试账号,您可以回复任意消息测试,开发者郭先生,18629374628"; String msg = "@"+ nickName + "," +content; System.out.println("事件类型为:"+","+eventType); message = MessageUtil.subscribeForText(toUserName, fromUserName,msg); }else if(MessageUtil.UNSUBSCIBE_EVENT.equals(eventType)){//处理取消订阅事件 System.out.println("事件类型为:"+eventType); message = MessageUtil.unsubscribe(toUserName, fromUserName); } }else { //微信消息分为事件推送消息和普通消息,非事件即为普通消息类型 switch (msgType) { case "text":{//文本消息 String content = map.get("Content"); //用户发的消息内容 content = "您发的消息内容是:"+content+",如需帮助,请联系郭先生,18629374628"; message = MessageUtil.replyMsg(toUserName, fromUserName,content,"text"); break; } case "image":{//图片消息 String content = "您发的消息内容是图片,如需帮助,请联系郭先生,18629374628"; message = MessageUtil.replyMsg(toUserName, fromUserName,content,"text"); break; } default:{//其他类型的普通消息 break; } } } } catch (DocumentException e) { e.printStackTrace(); } System.out.println("关注微信公众号自动回复的消息内容为:"+message); return message; }public static String getUserNickName(String openId) { String nickName= null; try { Map map = WeiXinUtil.cacheTokenAndTicket(); String token = (String)map.get("access_token"); String url = URL.replace("OPENID", openId).replace("ACCESS_TOKEN", token); JSONObject obj = WeiXinUtil.HttpGet(url); nickName = (String)obj.get("nickname"); } catch (IOException e) { e.printStackTrace(); } return nickName; } }

次处access_token缓存7200秒是用笔者写的一个工具类实现的,这个工具类也一并分享给大家,主要思路就是通过IO流把生成的access_token写到项目所在的文件中,每隔7200秒重新生成access_token覆盖原有的access_token,当然也可以用ehcache等缓存插件实现,那么所有的需要用到access_token的请求,取到的access_token都是实时的最新的。直接看代码:
public staticMapcacheTokenAndTicket() throws IOException { Gson gson = new Gson(); Mapmap = new HashMap(); String token = null; String ticket = null; JSONObject tokenObj = null; //需要获取的access_token对象; JSONObject ticketObj = null; //需要获取的jsapi_ticket对象; String filePath = System.getProperty("user.dir")+"/src/main/resources/token.txt"; File file = new File(filePath); //Access_token保存的位置 if (!file.exists()) file.createNewFile(); // 如果文件大小等于0,说明第一次使用,存入Access_token if (file.length() == 0) { tokenObj = WeiXinUtil.getToken(); token = (String)tokenObj.get("access_token"); FileOutputStream fos = new FileOutputStream(filePath, false); // 不允许追加 tokenObj.put("expires_in",System.currentTimeMillis()/1000+""); String json = gson.toJson(tokenObj); fos.write(json.getBytes()); fos.close(); }else { //读取文件内容 @SuppressWarnings("resource") FileInputStream fis = new FileInputStream(file); byte[] b = new byte[2048]; int len = fis.read(b); String jsonAccess_token = new String(b, 0, len); // 读取到的文件内容 JSONObject access_token = gson.fromJson(jsonAccess_token,JSONObject.class); if (access_token.get("expires_in") != null) { String lastSaveTime = (String)access_token.get("expires_in"); long nowTime = System.currentTimeMillis()/1000; long remianTime = nowTime - Long.valueOf(lastSaveTime); if (remianTime < WeixinConstant.EXPIRESIN_TIME) { JSONObject access = gson.fromJson(jsonAccess_token,JSONObject.class); token = (String)access.get("access_token"); } else { tokenObj = WeiXinUtil.getToken(); FileOutputStream fos = new FileOutputStream(file, false); // 不允许追加 tokenObj.put("expires_in",System.currentTimeMillis()/1000+""); String json = gson.toJson(tokenObj); fos.write((json).getBytes()); fos.close(); } } } map.put("access_token",token); map.put("jsapi_ticket",ticket); return map; }

点击这里免费获取源码 :https://download.csdn.net/download/guobinhui/11270776
更多JavaEE资料请关注下面公众号,欢迎广大开发者朋友一起交流。笔者电话(微信):18629374628
编程笔记|微信公众号开发系列教程(四)(监听关注/取消关注事件 消息接收与响应处理)
文章图片
具体功能体验可以扫左边的公众号二维码体验

    推荐阅读