在上一篇的文章中我們詳細講述了如何將我們的應用服務器和微信騰訊服務器之間的對接操作,最后接入成功,不知道你有沒有發現在上一篇的【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