hello 各位小夥伴,今天咱們來繼續學習如何經過 Spring Boot 開發微信公衆號。還沒閱讀過上篇文章的小夥伴建議先看看上文,有助於理解本文:java
上篇文章中咱們將微信服務器和咱們本身的服務器對接起來了,而且在本身的服務器上也能收到微信服務器發來的消息,本文咱們要看的就是如何給微信服務器回覆消息。數據庫
在討論如何給微信服務器回覆消息以前,咱們須要先來了解下微信服務器發來的消息主要有哪些類型以及咱們回覆給微信的消息都有哪些類型。後端
在上文中你們瞭解到,微信發送來的 xml 消息中有一個 MsgType 字段,這個字段就是用來標記消息的類型。這個類型能夠標記出這條消息是普通消息仍是事件消息仍是圖文消息等。服務器
普通消息主要是指:微信
不一樣的消息類型,對應不一樣的 MsgType,這裏我仍是以普通消息爲例,以下:app
消息類型 | MsgType |
---|---|
文本消息 | text |
圖片消息 | image |
語音消息 | voice |
視頻消息 | video |
小視頻消息 | shortvideo |
地址位置消息 | location |
連接消息 | link |
你們千萬不要覺得不一樣類型消息的格式是同樣的,實際上是不同的,也就是說,MsgType 爲 text 的消息和 MsgType 爲 image 的消息,微信服務器發給咱們的消息內容是不同的,這樣帶來一個問題就是我沒法使用一個 Bean 去接收不一樣類型的數據,所以這裏咱們通常使用 Map 接收便可。前後端分離
這是消息的接收,除了消息的接收以外,還有一個消息的回覆,咱們回覆的消息也有不少類型,能夠回覆普通消息,也能夠回覆圖片消息,回覆語音消息等,不一樣的回覆消息咱們能夠進行相應的封裝。由於不一樣的返回消息實例也是有一些共同的屬性的,例如消息是誰發來的,發給誰,消息類型,消息 id 等,因此咱們能夠將這些共同的屬性定義成一個父類,而後不一樣的消息再去繼承這個父類。ide
首先咱們來定義一個公共的消息類型:微服務
public class BaseMessage { private String ToUserName; private String FromUserName; private long CreateTime; private String MsgType; private long MsgId; //省略 getter/setter }
在這裏:工具
這是咱們的基本消息類型,就是說,咱們返回給用戶的消息,不管是什麼類型的消息,都有這幾個基本屬性。而後在此基礎上,咱們再去擴展出文本消息、圖片消息 等。
咱們來看下文本消息的定義:
public class TextMessage extends BaseMessage { private String Content; //省略 getter/setter }
文本消息在前面消息的基礎上多了一個 Content 屬性,所以文本消息繼承自 BaseMessage ,再額外添加一個 Content 屬性便可。
其餘的消息類型也是相似的定義,我就不一一列舉了,至於其餘消息的格式,你們能夠參考微信開放文檔(http://1t.click/aPXK)。
消息類型的 Bean 定義完成以後,接下來就是將實體類生成 XML。
首先咱們定義一個消息工具類,將常見的消息類型枚舉出來:
/** * 返回消息類型:文本 */ public static final String RESP_MESSAGE_TYPE_TEXT = "text"; /** * 返回消息類型:音樂 */ public static final String RESP_MESSAGE_TYPE_MUSIC = "music"; /** * 返回消息類型:圖文 */ public static final String RESP_MESSAGE_TYPE_NEWS = "news"; /** * 返回消息類型:圖片 */ public static final String RESP_MESSAGE_TYPE_Image = "image"; /** * 返回消息類型:語音 */ public static final String RESP_MESSAGE_TYPE_Voice = "voice"; /** * 返回消息類型:視頻 */ public static final String RESP_MESSAGE_TYPE_Video = "video"; /** * 請求消息類型:文本 */ public static final String REQ_MESSAGE_TYPE_TEXT = "text"; /** * 請求消息類型:圖片 */ public static final String REQ_MESSAGE_TYPE_IMAGE = "image"; /** * 請求消息類型:連接 */ public static final String REQ_MESSAGE_TYPE_LINK = "link"; /** * 請求消息類型:地理位置 */ public static final String REQ_MESSAGE_TYPE_LOCATION = "location"; /** * 請求消息類型:音頻 */ public static final String REQ_MESSAGE_TYPE_VOICE = "voice"; /** * 請求消息類型:視頻 */ public static final String REQ_MESSAGE_TYPE_VIDEO = "video"; /** * 請求消息類型:推送 */ 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"; /** * 事件類型:VIEW(自定義菜單 URl 視圖) */ public static final String EVENT_TYPE_VIEW = "VIEW"; /** * 事件類型:LOCATION(上報地理位置事件) */ public static final String EVENT_TYPE_LOCATION = "LOCATION"; /** * 事件類型:LOCATION(上報地理位置事件) */ public static final String EVENT_TYPE_SCAN = "SCAN";
你們注意這裏消息類型的定義,以 RESP 開頭的表示返回的消息類型,以 REQ 表示微信服務器發來的消息類型。而後在這個工具類中再定義兩個方法,用來將返回的對象轉換成 XML:
public static String textMessageToXml(TextMessage textMessage) { xstream.alias("xml", textMessage.getClass()); return xstream.toXML(textMessage); } private static XStream xstream = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { boolean cdata = true; @SuppressWarnings("rawtypes") public void startNode(String name, Class clazz) { super.startNode(name, clazz); } protected void writeText(QuickWriter writer, String text) { if (cdata) { writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } });
textMessageToXML 方法用來將 TextMessage 對象轉成 XML 返回給微信服務器,相似的方法咱們還須要定義 imageMessageToXml、voiceMessageToXml 等,不過定義的方式都基本相似,我就不一一列出來了。
因爲用戶發來的消息可能存在多種狀況,咱們須要分類進行處理,這個就涉及到返回消息的分發問題。所以我在這裏再定義一個返回消息分發的工具類,以下:
public class MessageDispatcher { public static String processMessage(Map<String, String> map) { String openid = map.get("FromUserName"); //用戶 openid String mpid = map.get("ToUserName"); //公衆號原始 ID if (map.get("MsgType").equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) { //普通文本消息 TextMessage txtmsg = new TextMessage(); txtmsg.setToUserName(openid); txtmsg.setFromUserName(mpid); txtmsg.setCreateTime(new Date().getTime()); txtmsg.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT); txtmsg.setContent("這是返回消息"); return MessageUtil.textMessageToXml(txtmsg); } return null; } public String processEvent(Map<String, String> map) { //在這裏處理事件 } }
這裏咱們還能夠多加幾個 elseif 去判斷不一樣的消息類型,我這裏由於只有普通文本消息,因此一個 if 就夠用了。
在這裏返回值我寫死了,實際上這裏須要根據微信服務端傳來的 Content 去數據中查詢,將查詢結果返回,數據庫查詢這一套相信你們都能搞定,我這裏就不重複介紹了。
最後在消息接收 Controller 中調用該方法,以下:
@PostMapping(value = "/verify_wx_token",produces = "application/xml;charset=utf-8") public String handler(HttpServletRequest request, HttpServletResponse response) throws Exception { request.setCharacterEncoding("UTF-8"); Map<String, String> map = MessageUtil.parseXml(request); String msgType = map.get("MsgType"); if (MessageUtil.REQ_MESSAGE_TYPE_EVENT.equals(msgType)) { return messageDispatcher.processEvent(map); }else{ return messageDispatcher.processMessage(map); } }
在 Controller 中,咱們首先判斷消息是不是事件,若是是事件,進入到事件處理通道,若是不是事件,則進入到消息處理通道。
注意,這裏須要配置一下返回消息的編碼,不然可能會出現中文亂碼。
如此以後,咱們的服務器就能夠給公衆號返回消息了。
上篇文章發出後,有小夥伴問鬆哥這個會不會開源,我能夠負責任的告訴你們,確定會開源,這個系列截稿後,我把代碼處理下就上傳到 GitHub。
好了,本文咱們就先說到這裏。
關注公衆號【江南一點雨】,專一於 Spring Boot+微服務以及先後端分離等全棧技術,按期視頻教程分享,關注後回覆 Java ,領取鬆哥爲你精心準備的 Java 乾貨!