Java微信公众平台开发(二)--微信服务器post消息体的接收
在上一篇的文章中我们详细讲述了如何将我们的应用服务器和微信腾讯服务器之间的对接操作,最后接入成功,不知道你有没有发现在上一篇的【controller】中我定义了一个get方法和一个post方法,但是在使用过程中我们就用了get方法,这里我们就来说说我们预留的post的方法的使用!
当我们在完成了服务器验证之后,此后用户每次向公众号发送消息、或者产生自定义菜单点击事件时,开发者填写的服务器配置URL将得到微信服务器推送过来的消息和事件,然后开发者可以依据自身业务逻辑进行响应,例如回复消息等!通过这句话我们能知道后面所有的微信服务器和我们应用服务器之间的沟通都是通过post消息体来完成的,那么我们这里将讲述如何接受微信post的消息体!
(一)消息类型和消息格式
上面有说道我们所有的和微信服务器之间进行沟通基本都是通过post消息体完成的,首先我们了解下消息体的类型,大致类型有两种:
-
普通消息类型:文本消息、图片消息、语音消息、视频消息、小视频消息、地理位置消息、链接消息
-
事件消息类型:关注/取消关注事件、扫描带参数二维码事件、上报地理位置事件、自定义菜单事件、点击菜单拉取消息时的事件推送、点击菜单跳转链接时的事件推送
消息类型:微信服务端推送的所有消息体的类型格式都是xml格式;
(二)消息重试机制
微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试,但是这里后期可以使用【客服消息接口】去完成消息再次推送。
(三)消息接收处理
在前面我们有说道微信的消息体是采用xml格式,那么我在这里写了一个MessageUtil去做消息格式的处理,大致代码如下:
1 package com.gede.wechat.util; 2 3 import java.io.InputStream; 4 import java.io.Writer; 5 import java.util.HashMap; 6 import java.util.List; 7 import java.util.Map; 8 import javax.servlet.http.HttpServletRequest; 9 import org.dom4j.Document; 10 import org.dom4j.Element; 11 import org.dom4j.io.SAXReader; 12 import com.thoughtworks.xstream.XStream; 13 import com.thoughtworks.xstream.core.util.QuickWriter; 14 import com.thoughtworks.xstream.io.HierarchicalStreamWriter; 15 import com.thoughtworks.xstream.io.xml.DomDriver; 16 import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; 17 import com.thoughtworks.xstream.io.xml.XppDriver; 18 /** 19 * @author gede 20 * @version date:2019年5月22日 下午3:44:27 21 * @description : 22 */ 23 public class MessageUtil { 24 25 /** 26 * 返回消息类型:文本 27 */ 28 public static final String RESP_MESSAGE_TYPE_TEXT = "text"; 29 30 /** 31 * 返回消息类型:音乐 32 */ 33 public static final String RESP_MESSAGE_TYPE_MUSIC = "music"; 34 35 /** 36 * 返回消息类型:图文 37 */ 38 public static final String RESP_MESSAGE_TYPE_NEWS = "news"; 39 40 /** 41 * 返回消息类型:图片 42 */ 43 public static final String RESP_MESSAGE_TYPE_Image = "image"; 44 45 /** 46 * 返回消息类型:语音 47 */ 48 public static final String RESP_MESSAGE_TYPE_Voice = "voice"; 49 50 /** 51 * 返回消息类型:视频 52 */ 53 public static final String RESP_MESSAGE_TYPE_Video = "video"; 54 55 /** 56 * 请求消息类型:文本 57 */ 58 public static final String REQ_MESSAGE_TYPE_TEXT = "text"; 59 60 /** 61 * 请求消息类型:图片 62 */ 63 public static final String REQ_MESSAGE_TYPE_IMAGE = "image"; 64 65 /** 66 * 请求消息类型:链接 67 */ 68 public static final String REQ_MESSAGE_TYPE_LINK = "link"; 69 70 /** 71 * 请求消息类型:地理位置 72 */ 73 public static final String REQ_MESSAGE_TYPE_LOCATION = "location"; 74 75 /** 76 * 请求消息类型:音频 77 */ 78 public static final String REQ_MESSAGE_TYPE_VOICE = "voice"; 79 80 /** 81 * 请求消息类型:视频 82 */ 83 public static final String REQ_MESSAGE_TYPE_VIDEO = "video"; 84 85 /** 86 * 请求消息类型:推送 87 */ 88 public static final String REQ_MESSAGE_TYPE_EVENT = "event"; 89 90 /** 91 * 事件类型:subscribe(订阅) 92 */ 93 public static final String EVENT_TYPE_SUBSCRIBE = "subscribe"; 94 95 /** 96 * 事件类型:unsubscribe(取消订阅) 97 */ 98 public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe"; 99 100 /** 101 * 事件类型:CLICK(自定义菜单点击事件) 102 */ 103 public static final String EVENT_TYPE_CLICK = "CLICK"; 104 105 /** 106 * 事件类型:VIEW(自定义菜单 URl 视图) 107 */ 108 public static final String EVENT_TYPE_VIEW = "VIEW"; 109 110 /** 111 * 事件类型:LOCATION(上报地理位置事件) 112 */ 113 public static final String EVENT_TYPE_LOCATION = "LOCATION"; 114 115 /** 116 * 事件类型:LOCATION(上报地理位置事件) 117 */ 118 119 @SuppressWarnings("unchecked") 120 public static Map<String, String> parseXml(HttpServletRequest request) throws Exception { 121 // 将解析结果存储在HashMap中 122 Map<String, String> map = new HashMap<String, String>(); 123 124 // 从request中取得输入流 125 InputStream inputStream = request.getInputStream(); 126 // 读取输入流 127 SAXReader reader = new SAXReader(); 128 Document document = reader.read(inputStream); 129 // 得到xml根元素 130 Element root = document.getRootElement(); 131 // 得到根元素的所有子节点 132 List<Element> elementList = root.elements(); 133 134 // 遍历所有子节点 135 for (Element e : elementList) 136 map.put(e.getName(), e.getText()); 137 138 // 释放资源 139 inputStream.close(); 140 inputStream = null; 141 142 return map; 143 } 144 145 @SuppressWarnings("unused") 146 private static XStream xstream = new XStream(new XppDriver() { 147 public HierarchicalStreamWriter createWriter(Writer out) { 148 return new PrettyPrintWriter(out) { 149 // 对所有xml节点的转换都增加CDATA标记 150 boolean cdata = true; 151 @SuppressWarnings("rawtypes") 152 public void startNode(String name, Class clazz) { 153 super.startNode(name, clazz); 154 } 155 156 protected void writeText(QuickWriter writer, String text) { 157 if (cdata) { 158 writer.write("<![CDATA["); 159 writer.write(text); 160 writer.write("]]>"); 161 } else { 162 writer.write(text); 163 } 164 } 165 }; 166 } 167 }); 168 }
当你试图在自己电脑上添加这个类时,发现会报缺少jar包错误,而如果按我前言中所提到的前辈的思路取走也许不会错,后续也会省区很多麻烦,但前提你必须要学会maven的使用。这也正是我为什么要翻写前辈的博客的原因。在成长的路上,也许有人不如我,但更多的人时比我强的,我只是想尽力去帮助那些不如我还在努力的人,同时也鞭策自己积极上进。在之后的开发中,每一次用到的jar包我都会在文末附上下载链接,大家放心下载即可。
然后将我们的WechatSecurity Controller中的post方法修改为如下,用于做消息的接收和处理:
1 @RequestMapping(value = "security", method = RequestMethod.POST) 2 // post方法用于接收微信服务端消息 3 public void DoPost(HttpServletRequest request,HttpServletResponse response) { 4 System.out.println("这是post方法!"); 5 try{ 6 Map<String, String> map=MessageUtil.parseXml(request); 7 System.out.println("============================="+map.get("Content")); 8 }catch(Exception e){ 9 logger.error(e,e); 10 } 11 }
重新运行项目,进行测试。下面是我的测试图。
那么当我们在这里将我们代码发布之后再公众号上发送消息,在们的后台就能看到我们的消息体进入并解析成功了。
在这里我只是做了消息体的接收和转换成Map,并没有对消息做出来,那么下一篇我们将讲述对消息的分类处理!
涉及到的相关jar包下载地址:dom4j-2.0.2 xmlpull-1.1.3.1 xpp3_min-1.1.4c xstream-1.4.7-sources xstream-1.4.8