労働者が仕事をうまくやりたいなら、まず道具を研ぐ必要があります。この記事では主に、WeChat パブリック プラットフォームによって定義されたメッセージとメッセージ関連の操作を、後で使用できるようにツール クラスにカプセル化する方法について説明します。ここで明確にする必要があるのは、メッセージは実際にはユーザーによって公開アカウントに送信され、その後、WeChat プラットフォームが開発時に指定した URL アドレスにメッセージを転送するということです。モードインターフェイスの設定。
WeChatパブリックプラットフォームメッセージインターフェース
WeChat プラットフォームから送信されたメッセージを受信するには、まず WeChat パブリック プラットフォーム API のメッセージ インターフェイス部分を理解する必要があります。ここをクリックして入力すると、図に示すようにメッセージ インターフェイス ガイド部分に入ります。以下:
上の写真の左側では、WeChat パブリック プラットフォームには現在、メッセージ インターフェイス、一般インターフェイス、カスタム メニュー インターフェイスの 3 つのオープン インターフェイスがあることがわかります。一般インターフェイスとカスタム メニュー インターフェイスは、内部テストの資格がある場合にのみ呼び出すことができます。また、内部テストの資格の申請は終了しています。WeChat が将来的に一般ユーザーに公開されることを祈るばかりです。内部テストの資格を持たない人は、これら 2 つのインターフェイスで時間を無駄にする必要はありません。メッセージ インターフェイスを使用してください。
メッセージプッシュとメッセージ返信
以下では主にメッセージインターフェースについて紹介します。メッセージの受信と応答は、上図の「4 メッセージのプッシュ」と「5 メッセージの返信」に注意するだけで済みます。
まずインターフェースの「メッセージプッシュ」が何を指すのか理解しましょう。「4 メッセージプッシュ」をクリックすると、インターフェースの「メッセージプッシュ」が「一般ユーザーがパブリックアカウントにメッセージを送信するとき、WeChatサーバーが」を指すことがわかります。 「このメッセージを入力された URL に POST します」、つまり、ここで定義されているのは、ユーザーが送信できるメッセージの種類、メッセージに含まれるフィールド、メッセージが WeChat サーバーによってパブリック アカウント バックエンドに転送される方法です。
メッセージ プッシュは、テキスト メッセージ、画像メッセージ、地理的位置メッセージ、リンク メッセージ、イベント プッシュの 5 種類のメッセージを定義します。実際には、音声メッセージも受信できますが、詳細は取得できません。が取得されます (音声ファイルを取得するには、内部テストの資格が必要です)。
インターフェースの「メッセージ返信」は、ユーザーに返信できるメッセージの種類、メッセージフィールド、メッセージ形式を定義します。WeChat パブリックプラットフォームのインターフェースガイドは次のように説明されています。
ユーザーに返信できるメッセージの種類は 5 種類あると前述しましたが、現在、開発モードで返信できるメッセージは 3 種類のみです。テキスト メッセージ、音楽メッセージ、グラフィック メッセージ、音声メッセージ、ビデオ メッセージは現在、使用されているモードでのみ編集できます。
メッセージのカプセル化
次に行うことは、メッセージ プッシュ (リクエスト) とメッセージ リプライ (応答) で定義されたメッセージをカプセル化し、対応する Java クラスを作成することです (Java はオブジェクト指向プログラミング言語であり、カプセル化した後に使用する方が便利です) )、以下の要求メッセージはメッセージ プッシュで定義されたメッセージを指し、応答メッセージはメッセージ応答で定義されたメッセージを指します。
リクエストメッセージの基本クラス
メッセージ プッシュで定義されたすべてのメッセージに共通のフィールドを抽出し、それらを基本クラスにカプセル化します。これらのパブリック フィールドには、ToUserName (開発者の WeChat アカウント)、FromUserName (送信者アカウント、OPEN_ID)、CreateTime (メッセージの作成時間)、 MsgType (メッセージ タイプ)、MsgId (メッセージ ID)、カプセル化された基本クラス org.liufeng.course.message.req.BaseMessage のコードは次のとおりです:
リーリー
メッセージテキストメッセージをリクエストリーリー
リクエストメッセージ画像メッセージ
リーリー
リクエストメッセージ地理位置情報メッセージリーリー
メッセージリンクメッセージをリクエストpackage org.liufeng.course.message.req; /*** リンクメッセージ * * @author liufeng * @date 2013-05-19*/ public class LinkMessage extends BaseMessage { // メッセージのタイトル private String Title // メッセージのリンク private String Url; String getTitle() { タイトルを返す; } public void setTitle(String title) { タイトル = タイトル; } public String getDescription() { 説明を返す; } public String getUrl() { URL を返す; } public void setUrl(String url) { URL = URL; }
ボイスメッセージのリクエストメッセージリーリー
応答メッセージの基本クラス同様に、メッセージ返信で定義されているすべてのメッセージに共通のフィールドを抽出し、それらを基本クラスにカプセル化します。これらのパブリック フィールドには、ToUserName (受信者のアカウント、ユーザーの OPEN_ID)、FromUserName (開発者の WeChat ID)、CreateTime (メッセージの作成時刻) が含まれます。 message)、MsgType (メッセージタイプ)、FuncFlag (メッセージの星マーク)、カプセル化された基本クラス org.liufeng.course.message.resp.BaseMessage のコードは次のとおりです:
リーリー
応答メッセージのテキストメッセージ
リーリー
音楽メッセージへの応答リーリー
グラフィックメッセージ付きの応答メッセージ
package org.liufeng.course.message.resp; import java.util.List; /** * 文本消息 * * @author liufeng * @date 2013-05-19 */ public class NewsMessage extends BaseMessage { // 图文消息个数,限制为10条以内 private int ArticleCount; // 多条图文消息信息,默认第一个item为大图 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; } }
package org.liufeng.course.message.resp; /** * 图文model * * @author liufeng * @date 2013-05-19 */ public class Article { // 图文消息名称 private String Title; // 图文消息描述 private String Description; // 图片链接,支持JPG、PNG格式,较好的效果为大图640*320,小图80*80,限制图片链接的域名需要与开发者填写的基本资料中的Url一致 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包即可,这种工作做一次就可以了。
如何解析请求消息?
接下来解决请求消息的解析问题。微信服务器会将用户的请求通过doPost方法发送给我们,让我们再来回顾下上一章节已经写好的doPost方法的定义:
/** * 处理微信服务器发来的消息 */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO 消息的接收、处理、响应 }
如何将响应消息转换成xml返回?
我们先前已经将响应消息封装成了Java类,方便我们在代码中使用。那么,请求接收成功、处理完成后,该如何将消息返回呢?这里就涉及到如何将响应消息转换成xml返回的问题,这里我们将采用开源框架xstream来实现Java类到xml的转换(这里使用的是xstream-1.3.1.jar),代码如下:
/** * 文本消息对象转换成xml * * @param textMessage 文本消息对象 * @return xml */ public static String textMessageToXml(TextMessage textMessage) { xstream.alias("xml", textMessage.getClass()); return xstream.toXML(textMessage); } /** * 音乐消息对象转换成xml * * @param musicMessage 音乐消息对象 * @return xml */ public static String musicMessageToXml(MusicMessage musicMessage) { xstream.alias("xml", musicMessage.getClass()); return xstream.toXML(musicMessage); } /** * 图文消息对象转换成xml * * @param newsMessage 图文消息对象 * @return xml */ public static String newsMessageToXml(NewsMessage newsMessage) { xstream.alias("xml", newsMessage.getClass()); xstream.alias("item", new Article().getClass()); return xstream.toXML(newsMessage); } /** * 扩展xstream,使其支持CDATA块 * * @date 2013-05-19 */ private static XStream xstream = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { // 对所有xml节点的转换都增加CDATA标记 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; /** * 消息工具类 * * @author liufeng * @date 2013-05-19 */ 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"; /** * 事件类型: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"; /** * 解析微信发来的请求(XML) * * @param request * @return * @throws Exception */ @SuppressWarnings("unchecked") public static Map<String, String> parseXml(HttpServletRequest request) throws Exception { // 将解析结果存储在HashMap中 Map<String, String> map = new HashMap<String, String>(); // 从request中取得输入流 InputStream inputStream = request.getInputStream(); // 读取输入流 SAXReader reader = new SAXReader(); Document document = reader.read(inputStream); // 得到xml根元素 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 * * @param textMessage 文本消息对象 * @return xml */ public static String textMessageToXml(TextMessage textMessage) { xstream.alias("xml", textMessage.getClass()); return xstream.toXML(textMessage); } /** * 音乐消息对象转换成xml * * @param musicMessage 音乐消息对象 * @return xml */ public static String musicMessageToXml(MusicMessage musicMessage) { xstream.alias("xml", musicMessage.getClass()); return xstream.toXML(musicMessage); } /** * 图文消息对象转换成xml * * @param newsMessage 图文消息对象 * @return xml */ public static String newsMessageToXml(NewsMessage newsMessage) { xstream.alias("xml", newsMessage.getClass()); xstream.alias("item", new Article().getClass()); return xstream.toXML(newsMessage); } /** * 扩展xstream,使其支持CDATA块 * * @date 2013-05-19 */ private static XStream xstream = new XStream(new XppDriver() { public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { // 对所有xml节点的转换都增加CDATA标记 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。下一篇讲会介绍如何利用上面封装好的工具识别用户发送的消息类型,并做出正确的响应。