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 干货!