微信公眾號開發:訊息與事件處理
阿新 • • 發佈:2019-02-14
在成功接入微信公眾平臺之後(如何接入請參考《微信公眾號開發:賬號申請與接入》),就可以對微信伺服器POST過來的訊息或者事件XML資料包進行監聽與處理了。在《微信公眾號開發:賬號申請與接入》的 WeChatController 控制器中, handleMsgAndEvent() 方法用來監聽並處理訊息與事件,示例專案的完整目錄層次如下圖所示。
本示例使用了Maven來構建工程,除了要引入基本的SpringMVC-WEB依賴,還需引入以下三個工具包。
WeChatController 控制器和 WeChatEncrypt 加密工具類的完整程式碼在《微信公眾號開發:賬號申請與接入》中已經貼出,此章中不再重複貼出。<dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> </dependency> <dependency> <groupId>xstream</groupId> <artifactId>xstream</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
WechatSession 是處理所有訊息的入口。AbstractMsg 是普通訊息與事件訊息公用的抽象父類,包含了一些通用的基礎屬性、提供了一些通用的基礎方法。import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerFactory; import org.w3c.dom.Document; import org.xml.sax.SAXException; /** * 處理訊息和事件的入口類 */ public class WechatSession { private InputStream in; private PrintWriter out; public static TransformerFactory tffactory = TransformerFactory.newInstance(); private static DocumentBuilder documentBuilder = null; public static DocumentBuilder getDocumentBuilder() { // 先檢查例項是否已建立,如果未建立才進入同步塊 if (null == documentBuilder) { synchronized (WechatSession.class) { // 再次檢查例項是否已建立,如果真的未建立才建立例項 if (null == documentBuilder) { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { documentBuilder = factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { e.printStackTrace(); } } } } return documentBuilder; } /** * 構造方法 * * @param in * @param out */ public WechatSession(InputStream in, PrintWriter out) { this.in = in; this.out = out; } /** * 對接收到的訊息和事件進行處理 */ public void process() { try { Document document = getDocumentBuilder().parse(in); String msgType = document.getElementsByTagName("MsgType").item(0).getTextContent(); if ("text".equals(msgType)) { TextMsg msg = new TextMsg(); msg.read(document); msg.onTextMsg(out); } else if ("event".equals(msgType)) { EventMsg msg = new EventMsg(); msg.read(document); msg.onEventMsg(out); } } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public void close() { try { if (in != null) { in.close(); } if (out != null) { out.flush(); out.close(); } } catch (IOException e) { e.printStackTrace(); } } }
TextMsg 普通訊息Java類。import java.io.IOException; import java.io.StringWriter; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.CDATASection; import org.w3c.dom.Document; import org.w3c.dom.Element; import lombok.Data; /** * 訊息與事件的抽象父類 */ @Data public abstract class AbstractMsg { protected String toUserName; protected String fromUserName; protected String createTime; protected String msgType; /** * 讀取Document內容到Java物件 * * @param document */ public void read(Document document) { readHead(document); readBody(document); } /** * 獲取Java物件的XML字串 */ public String write() { Document document = WechatSession.getDocumentBuilder().newDocument(); Element root = document.createElement("xml"); writeHead(root, document); writeBody(root, document); document.appendChild(root); return readDocument(document); } /** * 讀取訊息和事件公有的一些基礎屬性 * * @param document */ private void readHead(Document document) { toUserName = document.getElementsByTagName("ToUserName").item(0).getTextContent(); fromUserName = document.getElementsByTagName("FromUserName").item(0).getTextContent(); createTime = document.getElementsByTagName("CreateTime").item(0).getTextContent(); msgType = document.getElementsByTagName("MsgType").item(0).getTextContent(); } /** * 訊息和事件具體子類需實現該方法,用來讀取一些自身獨有的屬性資料 * * @param document */ protected abstract void readBody(Document document); /** * 將Java物件中的基礎屬性寫入Document * * @param root * @param document */ private void writeHead(Element root, Document document) { Element toUserNameElement = document.createElement("ToUserName"); CDATASection toUserNameCData = document.createCDATASection(this.toUserName); toUserNameElement.appendChild(toUserNameCData); Element fromUserNameElement = document.createElement("FromUserName"); CDATASection fromUserNameCData = document.createCDATASection(this.fromUserName); fromUserNameElement.appendChild(fromUserNameCData); Element createTimeElement = document.createElement("CreateTime"); createTimeElement.setTextContent(this.createTime); Element msgTypeElement = document.createElement("MsgType"); CDATASection msgTypeCData = document.createCDATASection(this.msgType); msgTypeElement.appendChild(msgTypeCData); root.appendChild(toUserNameElement); root.appendChild(fromUserNameElement); root.appendChild(createTimeElement); root.appendChild(msgTypeElement); } /** * 訊息和事件各子類需實現該方法,用來寫入一些自身獨有的屬性資料 * * @param root * @param document */ protected abstract void writeBody(Element root, Document document); /** * 獲取Document物件中指定元素的內容 * * @param document * @param elementName * @return */ protected String getElementContent(Document document, String elementName) { if (document.getElementsByTagName(elementName).getLength() > 0) { return document.getElementsByTagName(elementName).item(0).getTextContent(); } return null; } /** * 讀取Document物件為XML字串 * * @param document */ private String readDocument(Document document) { String docXml = ""; StringWriter writer = new StringWriter(); try { Transformer transformer = WechatSession.tffactory.newTransformer(); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");// 設定編碼字符集 transformer.setOutputProperty(OutputKeys.INDENT, "yes");// 設定縮排 transformer.transform(new DOMSource(document), new StreamResult(writer)); docXml = writer.getBuffer().toString(); System.out.println(docXml);// 將獲取到的XML字串列印至控制檯 writer.close(); } catch (Exception e) { e.printStackTrace(); } finally { if (null != writer) { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } return docXml; } }
EventMsg 事件訊息Java類。import java.io.PrintWriter; import org.w3c.dom.CDATASection; import org.w3c.dom.Document; import org.w3c.dom.Element; import lombok.Data; import lombok.EqualsAndHashCode; /** * 普通文字訊息 */ @Data @EqualsAndHashCode(callSuper = false) public class TextMsg extends AbstractMsg { private String content; private String msgId; /** * 從Document中讀取普通文字訊息獨有的屬性資料 */ @Override protected void readBody(Document document) { content = document.getElementsByTagName("Content").item(0).getTextContent(); msgId = document.getElementsByTagName("MsgId").item(0).getTextContent(); } /** * 回覆時專用,將普通文字訊息獨有的回覆內容寫入Document中 */ @Override protected void writeBody(Element root, Document document) { Element contentElement = document.createElement("Content"); CDATASection contentCData = document.createCDATASection(this.content); contentElement.appendChild(contentCData); root.appendChild(contentElement); } public void onTextMsg(PrintWriter out) { System.out.println("此處新增處理普通文字訊息的業務邏輯,此處簡單回覆:你好 某某某"); TextMsg msg = new TextMsg(); msg.setFromUserName(this.toUserName); msg.setToUserName(this.fromUserName); msg.setCreateTime(this.createTime); msg.setMsgType("text"); msg.setContent("你好 " + this.fromUserName); out.print(msg.write()); } }
import java.io.PrintWriter; import lombok.Data; import lombok.EqualsAndHashCode; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * 事件統一處理類 */ @Data @EqualsAndHashCode(callSuper = false) public class EventMsg extends AbstractMsg { // 事件列表:CLICK(點選自定義選單)/subscribe(關注)/unsubscribe(取消關注)/SCAN(掃描二維碼)/LOCATION(上報地理位置) private String event; private String eventKey; private String ticket; private String latitude; private String longitude; private String precision; /** * 從Document中讀取各事件獨有的屬性資料 */ @Override protected void readBody(Document document) { event = getElementContent(document, "Event").toLowerCase(); switch (event) { case "click": { eventKey = getElementContent(document, "EventKey"); break; } case "subscribe": case "unsubscribe": case "scan": { eventKey = getElementContent(document, "EventKey"); ticket = getElementContent(document, "Ticket"); break; } case "location": { latitude = getElementContent(document, "Latitude"); longitude = getElementContent(document, "Longitude"); precision = getElementContent(document, "Precision"); break; } default: { break; } } } /** * 回覆時專用,由於事件無須回覆,在此空實現 */ @Override protected void writeBody(Element root, Document document) { } public void onEventMsg(PrintWriter out) { System.out .println("此處新增處理事件的業務邏輯,對於關注事件回覆:尊敬的 某某某 女士/先生,歡迎您關注我的個人公眾號!"); switch (event) { case "subscribe": { TextMsg msg = new TextMsg(); msg.setFromUserName(this.toUserName); msg.setToUserName(this.fromUserName); msg.setCreateTime(this.createTime); msg.setMsgType("text"); msg.setContent("尊敬的 " + this.fromUserName + " 女士/先生,歡迎您關注我的個人公眾號!"); out.print(msg.write()); break; } } } }