微信公众帐号开发教程第4篇-消息及消息处理工具的封装
工欲善其事必先利其器!本篇内容主要讲解如何将微信公众平台定义的消息及消息相关的操作封装成工具类,方面后期的使用。这里需要明确的是消息其实是由用户发给你的公众帐号的,消息先被微信平台接收到,然后微信平台会将该消息转给你在开发模式接口配置中指定的URL地址。
微信公众平台消息接口
要接收微信平台发送的消息,我们需要先熟悉微信公众平台API中消息接口部分,点此进入,点击后将进入到消息接口指南部分,如下图所示:
在上图左侧可以看到微信公众平台目前开放的接口有三种:消息接口、通用接口和自定义菜单接口。通用接口和自定义菜单接口只有拿到内测资格才能调用,而内测资格的申请也已经关闭了,我们只有期待将来某一天微信会对大众用户开放吧,所以没有内测资格的用户就不要再浪费时间在这两个接口上,只需要用好消息接口就可以了。
消息推送和消息回复
下面将主要介绍消息接口。对于消息的接收、响应我们只需要关注上图中的“4 消息推送”和“5 消息回复”就足够了。
我们先来了解接口中的“消息推送”指的是什么,点击“4 消息推送”,可以看到接口中的“消息推送”指的是“当普通用户向公众帐号发消息时,微信服务器将POST该消息到填写的URL上”,即这里定义的是用户能够发送哪些类型的消息、消息有哪些字段、消息被微信服务器以什么方式转发给我们的公众帐号后台。
消息推送中定义了我们将会接收到的消息类型有5种:文本消息、图片消息、地理位置消息、链接消息和事件推送,其实语音消息我们也能够接收到的,只不过拿不到具体的语音文件而以(需要内测资格才能够获取语音文件)。
接口中的“消息回复”定义了我们能回复给用户的消息类型、消息字段和消息格式,微信公众平台的接口指南中是这样描述的:
上面说到我们能回复给用户的消息有5种,但目前在开发模式下能回复的消息只有3种:文本消息、音乐消息和图文消息,而语音消息和视频消息目前只能在编辑模式下使用。
消息的封装
接下来要做的就是将消息推送(请求)、消息回复(响应)中定义的消息进行封装,建立与之对应的Java类(Java是一门面向对象的编程语言,封装后使用起来更方便),下面的请求消息是指消息推送中定义的消息,响应消息指消息回复中定义的消息。
请求消息的基类
把消息推送中定义的所有消息都有的字段提取出来,封装成一个基类,这些公有的字段包括:ToUserName(开发者微信号)、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 BaseMessage { // 开发者微信号 private String ToUserName; // 发送方帐号(一个OpenID) private String FromUserName; // 消息创建时间 (整型) private long CreateTime; // 消息类型(text/image/location/link) private String MsgType; // 消息id,64位整型 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; /** * 文本消息 * * @author liufeng * @date 2013-05-19 */ 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; /** * 图片消息 * * @author liufeng * @date 2013-05-19 */ 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; /** * 地理位置消息 * * @author liufeng * @date 2013-05-19 */ 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; /** * 链接消息 * * @author liufeng * @date 2013-05-19 */ 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; /** * 音频消息 * * @author liufeng * @date 2013-05-19 */ public class VoiceMessage extends BaseMessage { // 媒体ID 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; /** * 消息基类(公众帐号 -> 普通用户) * * @author liufeng * @date 2013-05-19 */ public class BaseMessage { // 接收方帐号(收到的OpenID) private String ToUserName; // 开发者微信号 private String FromUserName; // 消息创建时间 (整型) private long CreateTime; // 消息类型(text/music/news) private String MsgType; // 位0x0001被标志时,星标刚收到的消息 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; /** * 文本消息 * * @author liufeng * @date 2013-05-19 */ 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; /** * 音乐消息 * * @author liufeng * @date 2013-05-19 */ public class MusicMessage extends BaseMessage { // 音乐 private Music Music; public Music getMusic() { return Music; } public void setMusic(Music music) { Music = music; } }
响应消息之图文消息
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; } }
图文消息中Article类的定义
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。下一篇讲会介绍如何利用上面封装好的工具识别用户发送的消息类型,并做出正确的响应。

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











데비안 시스템의 readdir 함수는 디렉토리 컨텐츠를 읽는 데 사용되는 시스템 호출이며 종종 C 프로그래밍에 사용됩니다. 이 기사에서는 ReadDir를 다른 도구와 통합하여 기능을 향상시키는 방법을 설명합니다. 방법 1 : C 언어 프로그램을 파이프 라인과 결합하고 먼저 C 프로그램을 작성하여 readDir 함수를 호출하고 결과를 출력하십시오.#포함#포함#포함#포함#includinTmain (intargc, char*argv []) {dir*dir; structdirent*entry; if (argc! = 2) {

이 기사에서는 데비안 시스템에서 Hadoop 데이터 처리 효율성을 향상시키는 방법에 대해 설명합니다. 최적화 전략에는 하드웨어 업그레이드, 운영 체제 매개 변수 조정, Hadoop 구성 수정 및 효율적인 알고리즘 및 도구 사용이 포함됩니다. 1. 하드웨어 리소스 강화는 모든 노드에 일관된 하드웨어 구성, 특히 CPU, 메모리 및 네트워크 장비 성능에주의를 기울일 수 있도록합니다. 전반적인 처리 속도를 향상시키기 위해서는 고성능 하드웨어 구성 요소를 선택하는 것이 필수적입니다. 2. 운영 체제 조정 파일 설명 자 및 네트워크 연결 : /etc/security/limits.conf 파일을 수정하여 파일 설명자의 상한을 늘리고 동시에 시스템에 의해 열 수 있습니다. JVM 매개 변수 조정 : Hadoop-env.sh 파일에서 조정

Debian Systems에서 ReadDir 시스템 호출은 디렉토리 내용을 읽는 데 사용됩니다. 성능이 좋지 않은 경우 다음과 같은 최적화 전략을 시도해보십시오. 디렉토리 파일 수를 단순화하십시오. 대규모 디렉토리를 가능한 한 여러 소규모 디렉토리로 나누어 읽기마다 처리 된 항목 수를 줄입니다. 디렉토리 컨텐츠 캐싱 활성화 : 캐시 메커니즘을 구축하고 정기적으로 캐시를 업데이트하거나 디렉토리 컨텐츠가 변경 될 때 캐시를 업데이트하며 readDir로 자주 호출을 줄입니다. 메모리 캐시 (예 : Memcached 또는 Redis) 또는 로컬 캐시 (예 : 파일 또는 데이터베이스)를 고려할 수 있습니다. 효율적인 데이터 구조 채택 : 디렉토리 트래버스를 직접 구현하는 경우 디렉토리 정보를 저장하고 액세스하기 위해보다 효율적인 데이터 구조 (예 : 선형 검색 대신 해시 테이블)를 선택하십시오.

Debian Mail 서버에 SSL 인증서를 설치하는 단계는 다음과 같습니다. 1. OpenSSL 툴킷을 먼저 설치하십시오. 먼저 OpenSSL 툴킷이 이미 시스템에 설치되어 있는지 확인하십시오. 설치되지 않은 경우 다음 명령을 사용하여 설치할 수 있습니다. 개인 키 및 인증서 요청 생성 다음에 다음, OpenSSL을 사용하여 2048 비트 RSA 개인 키 및 인증서 요청 (CSR)을 생성합니다.

웹 사이트 성능 최적화는 액세스 로그의 심층 분석과 분리 할 수 없습니다. NGINX 로그는 웹 사이트를 방문하는 사용자의 자세한 정보를 기록합니다. 이 데이터를 영리하게 사용하면 웹 사이트의 속도가 효과적으로 향상 될 수 있습니다. 이 기사는 NGINX 로그를 기반으로 여러 웹 사이트 성능 최적화 방법을 소개합니다. 1. 사용자 행동 분석 및 최적화. NGINX 로그를 분석하여 사용자 동작에 대한 깊은 이해를 얻고이를 기반으로 목표 최적화를 수행 할 수 있습니다. 고주파 액세스 IP 식별 : 액세스 빈도가 가장 높은 IP 주소를 찾고, 대역폭 증가 또는 특정 콘텐츠의 응답 속도 향상과 같은 이러한 IP 주소의 서버 리소스 구성을 최적화합니다. 상태 코드 분석 : 다른 HTTP 상태 코드 (예 : 404 오류)의 빈도를 분석하고 웹 사이트 탐색 또는 컨텐츠 관리의 문제를 찾고 진행하십시오.

데비안 시스템에서 Zookeeper 버전을 업그레이드하면 아래 단계를 따라갈 수 있습니다. 1. 기존 구성 및 데이터를 백업하면 업그레이드하기 전에 기존 Zookeeper 구성 파일 및 데이터 디렉토리를 백업하는 것이 좋습니다. sudocp-r/var/lib/Zookeeper/var/lib/Zookeeper_backupsudocp/etc/ketokeeper/conf/zoo.cfg/etc/Zookeokeeper/conf/Zookeeper/z

Debian Systems에서 OpenSSL은 암호화, 암호 해독 및 인증서 관리를위한 중요한 라이브러리입니다. MITM (Man-in-the-Middle Attack)을 방지하려면 다음 측정을 수행 할 수 있습니다. HTTPS 사용 : 모든 네트워크 요청이 HTTP 대신 HTTPS 프로토콜을 사용하도록하십시오. HTTPS는 TLS (Transport Layer Security Protocol)를 사용하여 통신 데이터를 암호화하여 전송 중에 데이터가 도난 당하거나 변조되지 않도록합니다. 서버 인증서 확인 : 클라이언트의 서버 인증서를 수동으로 확인하여 신뢰할 수 있는지 확인하십시오. 서버는 대의원 메소드를 통해 수동으로 확인할 수 있습니다.

Debian에서 Hadoop 로그 관리하면 다음 단계 및 모범 사례를 따라갈 수 있습니다. 로그 집계 로그 집계 : Yarn-site.xml 파일에서 Ture에서 True로 설정 할 수 있도록 설정 : 로그 집계를 활성화하십시오. 로그 보유 정책 구성 : 172800 초 (2 일)와 같이 로그의 유지 시간을 정의하기 위해 yarn.log-aggregation.retain-seconds를 설정하십시오. 로그 저장 경로를 지정하십시오 : Yarn.n을 통해
