工欲善其事必先利其器!本篇內容主要解說怎樣將微信公衆平臺定義的消息及消息相關的操做封裝成工具類,方面後期的使用。這裏需要明白的是消息事實上是由用戶發給你的公衆賬號的,消息先被微信平臺接收到,而後微信平臺會將該消息轉給你在開發模式接口配置中指定的URL地址。php
微信公衆平臺消息接口java
要接收微信平臺發送的消息,咱們需要先熟悉微信公衆平臺API中消息接口部分,點此進入,點擊後將進入到消息接口指南部分,例如如下圖所看到的:編程
在上圖左側可以看到微信公衆平臺眼下開放的接口有三種:消息接口、通用接口和本身定義菜單接口。通用接口和本身定義菜單接口惟獨拿到內測資格才幹調用,而內測資格的申請也已經關閉了,咱們惟獨期待未來某一天微信會對大衆用戶開放吧,因此沒有內測資格的用戶就不要再浪費時間在這兩個接口上,僅僅需要用好消息接口就可以了。微信
消息推送和消息回覆微信開發
如下將主要介紹消息接口。對於消息的接收、響應咱們僅僅需要關注上圖中的「4 消息推送」和「5 消息回覆」就足夠了。微信公衆平臺
咱們先來了解接口中的「消息推送」指的是什麼,點擊「4 消息推送」,可以看到接口中的「消息推送」指的是「當普通用戶向公衆賬號發消息時,微信server將POST該消息到填寫的URL上」,即這裏定義的是用戶可以發送哪些類型的消息、消息有哪些字段、消息被微信server以什麼方式轉發給咱們的公衆賬號後臺。框架
消息推送中定義了咱們將會接收到的消息類型有5種:文本消息、圖片消息、地理位置消息、連接消息和事件推送,事實上語音消息咱們也可以接收到的,僅僅只是拿不到詳細的語音文件而以(需要內測資格才幹夠獲取語音文件)。dom
接口中的「消息回覆」定義了咱們能回覆給用戶的消息類型、消息字段和消息格式,微信公衆平臺的接口指南中是這樣描寫敘述的:編程語言
上面說到咱們能回覆給用戶的消息有5種,但眼下在開發模式下能回覆的消息惟獨3種:文本消息、音樂消息和圖文消息,而語音消息和視頻消息眼下僅僅能在編輯模式下使用。工具
消息的封裝
接下來要作的就是將消息推送(請求)、消息回覆(響應)中定義的消息進行封裝,創建與之相應的Java類(Java是一門面向對象的編程語言,封裝後使用起來更方便),如下的請求消息是指消息推送中定義的消息,響應消息指消息回覆中定義的消息。
請求消息的基類
把消息推送中定義的所有消息都有的字段提取出來,封裝成一個基類,這些公有的字段包含:ToUserName(開發人員微信號)、FromUserName(發送方賬號,OPEN_ID)、CreateTime(消息的建立時間)、MsgType(消息類型)、MsgId(消息ID),封裝後基類org.liufeng.course.message.req.BaseMessage的代碼例如如下:
- package org.liufeng.course.message.req;
-
-
-
-
-
-
-
- public class BaseMessage {
-
- private String ToUserName;
-
- private String FromUserName;
-
- private long CreateTime;
-
- private String MsgType;
-
- private long MsgId;
-
- public String getToUserName() {
- return ToUserName;
- }
-
- public void setToUserName(String toUserName) {
- ToUserName = toUserName;
- }
-
- public String getFromUserName() {
- return FromUserName;
- }
-
- public void setFromUserName(String fromUserName) {
- FromUserName = fromUserName;
- }
-
- public long getCreateTime() {
- return CreateTime;
- }
-
- public void setCreateTime(long createTime) {
- CreateTime = createTime;
- }
-
- public String getMsgType() {
- return MsgType;
- }
-
- public void setMsgType(String msgType) {
- MsgType = msgType;
- }
-
- public long getMsgId() {
- return MsgId;
- }
-
- public void setMsgId(long msgId) {
- MsgId = msgId;
- }
- }
請求消息之文本消息
- package org.liufeng.course.message.req;
-
-
-
-
-
-
-
- public class TextMessage extends BaseMessage {
-
- private String Content;
-
- public String getContent() {
- return Content;
- }
-
- public void setContent(String content) {
- Content = content;
- }
- }
請求消息之圖片消息
- package org.liufeng.course.message.req;
-
-
-
-
-
-
-
- public class ImageMessage extends BaseMessage {
-
- private String PicUrl;
-
- public String getPicUrl() {
- return PicUrl;
- }
-
- public void setPicUrl(String picUrl) {
- PicUrl = picUrl;
- }
- }
請求消息之地理位置消息
- package org.liufeng.course.message.req;
-
-
-
-
-
-
-
- public class LocationMessage extends BaseMessage {
-
- private String Location_X;
-
- private String Location_Y;
-
- private String Scale;
-
- private String Label;
-
- public String getLocation_X() {
- return Location_X;
- }
-
- public void setLocation_X(String location_X) {
- Location_X = location_X;
- }
-
- public String getLocation_Y() {
- return Location_Y;
- }
-
- public void setLocation_Y(String location_Y) {
- Location_Y = location_Y;
- }
-
- public String getScale() {
- return Scale;
- }
-
- public void setScale(String scale) {
- Scale = scale;
- }
-
- public String getLabel() {
- return Label;
- }
-
- public void setLabel(String label) {
- Label = label;
- }
- }
請求消息之連接消息
- package org.liufeng.course.message.req;
-
-
-
-
-
-
-
- public class LinkMessage extends BaseMessage {
-
- private String Title;
-
- private String Description;
-
- private String Url;
-
- public String getTitle() {
- return Title;
- }
-
- public void setTitle(String title) {
- Title = title;
- }
-
- public String getDescription() {
- return Description;
- }
-
- public void setDescription(String description) {
- Description = description;
- }
-
- public String getUrl() {
- return Url;
- }
-
- public void setUrl(String url) {
- Url = url;
- }
- }
請求消息之語音消息
- package org.liufeng.course.message.req;
-
-
-
-
-
-
-
- public class VoiceMessage extends BaseMessage {
-
- private String MediaId;
-
- private String Format;
-
- public String getMediaId() {
- return MediaId;
- }
-
- public void setMediaId(String mediaId) {
- MediaId = mediaId;
- }
-
- public String getFormat() {
- return Format;
- }
-
- public void setFormat(String format) {
- Format = format;
- }
- }
響應消息的基類
相同,把消息回覆中定義的所有消息都有的字段提取出來,封裝成一個基類,這些公有的字段包含:ToUserName(接收方賬號,用戶的OPEN_ID)、FromUserName(開發人員的微信號)、CreateTime(消息的建立時間)、MsgType(消息類型)、FuncFlag(消息的星標標識),封裝後基類org.liufeng.course.message.resp.BaseMessage的代碼例如如下:
- package org.liufeng.course.message.resp;
-
-
-
-
-
-
-
- public class BaseMessage {
-
- private String ToUserName;
-
- private String FromUserName;
-
- private long CreateTime;
-
- private String MsgType;
-
- private int FuncFlag;
-
- public String getToUserName() {
- return ToUserName;
- }
-
- public void setToUserName(String toUserName) {
- ToUserName = toUserName;
- }
-
- public String getFromUserName() {
- return FromUserName;
- }
-
- public void setFromUserName(String fromUserName) {
- FromUserName = fromUserName;
- }
-
- public long getCreateTime() {
- return CreateTime;
- }
-
- public void setCreateTime(long createTime) {
- CreateTime = createTime;
- }
-
- public String getMsgType() {
- return MsgType;
- }
-
- public void setMsgType(String msgType) {
- MsgType = msgType;
- }
-
- public int getFuncFlag() {
- return FuncFlag;
- }
-
- public void setFuncFlag(int funcFlag) {
- FuncFlag = funcFlag;
- }
- }
響應消息之文本消息
- package org.liufeng.course.message.resp;
-
-
-
-
-
-
-
- public class TextMessage extends BaseMessage {
-
- private String Content;
-
- public String getContent() {
- return Content;
- }
-
- public void setContent(String content) {
- Content = content;
- }
- }
響應消息之音樂消息
- package org.liufeng.course.message.resp;
-
-
-
-
-
-
-
- public class MusicMessage extends BaseMessage {
-
- private Music Music;
-
- public Music getMusic() {
- return Music;
- }
-
- public void setMusic(Music music) {
- Music = music;
- }
- }
音樂消息中Music類的定義
- package org.liufeng.course.message.resp;
-
-
-
-
-
-
-
- public class Music {
-
- private String Title;
-
- private String Description;
-
- private String MusicUrl;
-
- private String HQMusicUrl;
-
- public String getTitle() {
- return Title;
- }
-
- public void setTitle(String title) {
- Title = title;
- }
-
- public String getDescription() {
- return Description;
- }
-
- public void setDescription(String description) {
- Description = description;
- }
-
- public String getMusicUrl() {
- return MusicUrl;
- }
-
- public void setMusicUrl(String musicUrl) {
- MusicUrl = musicUrl;
- }
-
- public String getHQMusicUrl() {
- return HQMusicUrl;
- }
-
- public void setHQMusicUrl(String musicUrl) {
- HQMusicUrl = musicUrl;
- }
-
- }
響應消息之圖文消息
- package org.liufeng.course.message.resp;
-
- import java.util.List;
-
-
-
-
-
-
-
- public class NewsMessage extends BaseMessage {
-
- private int ArticleCount;
-
- private List<Article> Articles;
-
- public int getArticleCount() {
- return ArticleCount;
- }
-
- public void setArticleCount(int articleCount) {
- ArticleCount = articleCount;
- }
-
- public List<Article> getArticles() {
- return Articles;
- }
-
- public void setArticles(List<Article> articles) {
- Articles = articles;
- }
- }
圖文消息中Article類的定義
- package org.liufeng.course.message.resp;
-
-
-
-
-
-
-
- public class Article {
-
- private String Title;
-
- private String Description;
-
- private String PicUrl;
-
- private String Url;
-
- public String getTitle() {
- return Title;
- }
-
- public void setTitle(String title) {
- Title = title;
- }
-
- public String getDescription() {
- return null == Description ? "" : Description;
- }
-
- public void setDescription(String description) {
- Description = description;
- }
-
- public String getPicUrl() {
- return null == PicUrl ? "" : PicUrl;
- }
-
- public void setPicUrl(String picUrl) {
- PicUrl = picUrl;
- }
-
- public String getUrl() {
- return null == Url ? "" : Url;
- }
-
- public void setUrl(String url) {
- Url = url;
- }
-
- }
所有消息封裝完成後,Eclipse工程中關於消息部分的結構應該與下圖保持一致,假設不一致的(類名、屬性名稱不一致的)請檢查後調整一致,由於後面的章節還要介紹怎樣將微信開發中通用的類方法、與業務無關的工具類封裝打成jar包,之後再作微信項目僅僅需要引入該jar包就能夠,這樣的工做作一次就可以了。
怎樣解析請求消息?
接下來解決請求消息的解析問題。微信server會將用戶的請求經過doPost方法發送給咱們,讓咱們再來回憶下上一章節已經寫好的doPost方法的定義:
-
-
-
- public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-
- }
doPost方法有兩個參數,request中封裝了請求相關的所有內容,可以從request中取出微信server發來的消息;而經過response咱們可以對接收到的消息進行響應,即發送消息。
那麼怎樣解析請求消息的問題也就轉化爲怎樣從request中獲得微信server發送給咱們的xml格式的消息了。這裏咱們藉助於開源框架dom4j去解析xml(這裏使用的是dom4j-1.6.1.jar),而後將解析獲得的結果存入HashMap,解析請求消息的方法例如如下:
-
-
-
-
-
-
-
- @SuppressWarnings("unchecked")
- public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
-
- Map<String, String> map = new HashMap<String, String>();
-
-
- InputStream inputStream = request.getInputStream();
-
- SAXReader reader = new SAXReader();
- Document document = reader.read(inputStream);
-
- Element root = document.getRootElement();
-
- List<Element> elementList = root.elements();
-
-
- for (Element e : elementList)
- map.put(e.getName(), e.getText());
-
-
- inputStream.close();
- inputStream = null;
-
- return map;
- }
怎樣將響應消息轉換成xml返回?
咱們先前已經將響應消息封裝成了Java類,方便咱們在代碼中使用。那麼,請求接收成功、處理完成後,該怎樣將消息返回呢?這裏就涉及到怎樣將響應消息轉換成xml返回的問題,這裏咱們將採用開源框架xstream來實現Java類到xml的轉換(這裏使用的是xstream-1.3.1.jar),代碼例如如下:
-
-
-
-
-
-
- public static String textMessageToXml(TextMessage textMessage) {
- xstream.alias("xml", textMessage.getClass());
- return xstream.toXML(textMessage);
- }
-
-
-
-
-
-
-
- public static String musicMessageToXml(MusicMessage musicMessage) {
- xstream.alias("xml", musicMessage.getClass());
- return xstream.toXML(musicMessage);
- }
-
-
-
-
-
-
-
- public static String newsMessageToXml(NewsMessage newsMessage) {
- xstream.alias("xml", newsMessage.getClass());
- xstream.alias("item", new Article().getClass());
- return xstream.toXML(newsMessage);
- }
-
-
-
-
-
-
- private static XStream xstream = new XStream(new XppDriver() {
- public HierarchicalStreamWriter createWriter(Writer out) {
- return new PrettyPrintWriter(out) {
-
- boolean cdata = true;
-
- @SuppressWarnings("unchecked")
- 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);
- }
- }
- };
- }
- });
說明:由於xstream框架自己並不支持CDATA塊的生成,40~62行代碼是對xtream作了擴展,使其支持在生成xml各元素值時添加CDATA塊。
消息處理工具的封裝
知道怎麼解析請求消息,也知道怎樣將響應消息轉化成xml了,接下來就是將消息相關的處理方法所有封裝到工具類MessageUtil中,該類的完整代碼例如如下:
- package org.liufeng.course.util;
-
- import java.io.InputStream;
- import java.io.Writer;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
-
- import javax.servlet.http.HttpServletRequest;
-
- import org.dom4j.Document;
- import org.dom4j.Element;
- import org.dom4j.io.SAXReader;
- import org.liufeng.course.message.resp.Article;
- import org.liufeng.course.message.resp.MusicMessage;
- import org.liufeng.course.message.resp.NewsMessage;
- import org.liufeng.course.message.resp.TextMessage;
-
- import com.thoughtworks.xstream.XStream;
- import com.thoughtworks.xstream.core.util.QuickWriter;
- import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
- import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
- import com.thoughtworks.xstream.io.xml.XppDriver;
-
-
-
-
-
-
-
- public class MessageUtil {
-
-
-
-
- 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 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_EVENT = "event";
-
-
-
-
- public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";
-
-
-
-
- public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
-
-
-
-
- public static final String EVENT_TYPE_CLICK = "CLICK";
-
-
-
-
-
-
-
-
- @SuppressWarnings("unchecked")
- public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
-
- Map<String, String> map = new HashMap<String, String>();
-
-
- InputStream inputStream = request.getInputStream();
-
- SAXReader reader = new SAXReader();
- Document document = reader.read(inputStream);
-
- Element root = document.getRootElement();
-
- List<Element> elementList = root.elements();
-
-
- for (Element e : elementList)
- map.put(e.getName(), e.getText());
-
-
- inputStream.close();
- inputStream = null;
-
- return map;
- }
-
-
-
-
-
-
-
- public static String textMessageToXml(TextMessage textMessage) {
- xstream.alias("xml", textMessage.getClass());
- return xstream.toXML(textMessage);
- }
-
-
-
-
-
-
-
- public static String musicMessageToXml(MusicMessage musicMessage) {
- xstream.alias("xml", musicMessage.getClass());
- return xstream.toXML(musicMessage);
- }
-
-
-
-
-
-
-
- public static String newsMessageToXml(NewsMessage newsMessage) {
- xstream.alias("xml", newsMessage.getClass());
- xstream.alias("item", new Article().getClass());
- return xstream.toXML(newsMessage);
- }
-
-
-
-
-
-
- private static XStream xstream = new XStream(new XppDriver() {
- public HierarchicalStreamWriter createWriter(Writer out) {
- return new PrettyPrintWriter(out) {
-
- boolean cdata = true;
-
- @SuppressWarnings("unchecked")
- 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);
- }
- }
- };
- }
- });
- }
OK,到這裏關於消息及消息處理工具的封裝就說到這裏,事實上就是對請求消息/響應消息創建了與之相應的Java類、對xml消息進行解析、將響應消息的Java對象轉換成xml。下一篇講會介紹怎樣利用上面封裝好的工具識別用戶發送的消息類型,並作出正確的響應。
假設認爲文章對你有所幫助,請留言支持或關注微信公衆賬號xiaoqrobot支持柳峯哦!