微信开发源码之自动回复功能
白羽 2018-06-06 来源 :网络 阅读 1130 评论 0

摘要:本文主要带你了解消息接受和发送简单介绍,xml格式解析,其他代码简介,希望本文对大家学微信有所帮助。


本文记述主线·消息接受和发送简单介绍·xml格式解析·其他代码简介
 
消息的自定回复,就是用户在订阅号中进行了某些操作(订阅、点击菜单、输入内容等),然后通过微信服务器发送到个人服务器上,个人服务器处理操作后,再通过微信服务器转发给用户。我们需要考虑的过程是,个人服务器和微信服务器的交互过程。官网介绍如下,感觉比较易于理解:当用户发送消息给公众号时(或某些特定的用户操作引发的事件推送时),会产生一个POST请求,开发者可以在响应包(Get)中返回特定XML结构,来对该消息进行响应(现支持回复文本、图片、图文、语音、视频、音乐)。严格来说,发送被动响应消息其实并不是一种接口,而是对微信服务器发过来消息的一次回复。
从微信服务器接收到的消息例子(文本消息):
[
html] view plain copy
1. <xml>  
2. <ToUserName><![CDATA[toUser]]></ToUserName>  
3. <FromUserName><![CDATA[fromUser]]></FromUserName>   
4. <CreateTime>1348831860</CreateTime>  
5. <MsgType><![CDATA[text]]></MsgType>  
6. <Content><![CDATA[this is a test]]></Content>  
7. <MsgId>1234567890123456</MsgId>  
8. </xml>  
 
其中前四个项目是固定了,每个消息都有这四个,ToUserName、FromUserName、CreateTime和MsgType,后面试根据不同的消息类型(MsgType)而不同的;MsgType包括文本、图片、语音、视频、小视频、地理位置、连接等消息,和关注/取消关注、扫描二维码、自定义菜单等时间推送,详细可关注官网,本博客仅以文本作为例子;MsgType是消息的时候,有MsgId,是事件推送的时候,没有MsgId;如果想避免重复,存在MsgId的时候,使用MsgId作为判断,不存在MsgId的话,可以使用 FromUserName+CreateTime作为判断;<![CDATA ]>符号的含义是,在xml解析的时候,在这个符号内的所有字符均解析为字符串,尤其是一些特殊符号(</>d等),一些数值类型的一般不加这个符号。发送给微信服务器的消息例子(文本消息):
[html] view plain copy
1. <xml>  
2. <ToUserName><![CDATA[toUser]]></ToUserName>  
3. <FromUserName><![CDATA[fromUser]]></FromUserName>  
4. <CreateTime>12345678</CreateTime>  
5. <MsgType><![CDATA[text]]></MsgType>  
6. <Content><![CDATA[你好]]></Content>  
7. </xml>  
格式和要求基本上和接受的消息一样,不一样的地方在于MsgType没有事件推送类型,只有消息类型,包括文本、图片、语音、视频、音乐和图文等。· xml格式解析xml格式的解析是xml和java之间的转换。这里我用到了dom4j包和xstream包。这两个包的maven导入如下:
[
html] view plain copy
1. <dependency>  
2.   <groupId>dom4j</groupId>  
3.   <artifactId>dom4j</artifactId>  
4.   <version>1.6</version>  
5. </dependency>  
6. <dependency>  
7.   <groupId>com.thoughtworks.xstream</groupId>  
8.   <artifactId>xstream</artifactId>  
9.   <version>1.4.8</version>  
10. </dependency>  
先说由xml转化为java类:MessageUtil
[java] view plain copy
1. public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {  
2.     Map<String, String> map = new HashMap<String, String>();  
3.   
4.     InputStream inputStream = request.getInputStream();  
5.   
6.     SAXReader reader = new SAXReader();  
7.     Document document = reader.read(inputStream);  
8.   
9.     Element root = document.getRootElement();  
10.   
11.     @SuppressWarnings("unchecked")  
12.     List<Element> elements = root.elements();  
13.   
14.     for (Element el : elements) {  
15.         map.put(el.getName(), el.getText());  
16.     }  
17.   
18.     inputStream.close();  
19.     inputStream = null;  
20.   
21.     return map;  
22. }  
分析:这里是从request的InputStream流中读取数据,通过dom4j包转化为Map类型。其中需要注意的是收到的xml格式中,标签的首字母是大写的,例<ToUserName></ToUserName>,Map中的key是'ToUserName'。
由java转化为xml,以text类型的xml格式为例类:MessageUtil
[java] view plain copy
1. public static String textMessageToXml(TextRespMessage textMessage) {  
2.     xstream.autodetectAnnotations(true);  
3.     return xstream.toXML(textMessage);  
4. }  
5.   
6. private static XStream xstream = new XStream(new XppDriver() {  
7.     @Override  
8.     public HierarchicalStreamWriter createWriter(Writer out) {  
9.         return new PrettyPrintWriter(out) {  
10.   
11.             boolean cdata = false;  
12.             Class<?> targetClass = null;  
13.   
14.             @Override  
15.             public void startNode(String name, @SuppressWarnings("rawtypes") Class clazz) {  
16.                 super.startNode(name, clazz);  
17.                 if (!name.equals("xml")) {  
18.                     cdata = needCDATA(targetClass, name);  
19.                 } else {  
20.                     targetClass = clazz;  
21.                 }  
22.             }  
23.   
24.             @Override  
25.             protected void writeText(QuickWriter writer, String text) {  
26.                 if (cdata) {  
27.                     writer.write("<![CDATA[");  
28.                     writer.write(text);  
29.                     writer.write("]]>");  
30.                 } else {  
31.                     writer.write(text);  
32.                 }  
33.             }  
34.         };  
35.     }  
36. });  
37.   
38. private static boolean needCDATA(Class<?> targetClass, String fieldAlias) {  
39.     boolean cdata = false;  
40.     cdata = existsCDATA(targetClass, fieldAlias);  
41.     if (cdata) {  
42.         return cdata;  
43.     }  
44.     Class<?> superClass = targetClass.getSuperclass();  
45.     while (!superClass.equals(Object.class)) {  
46.         cdata = existsCDATA(superClass, fieldAlias);  
47.         if (cdata) {  
48.             return cdata;  
49.         }  
50.         superClass = superClass.getClass().getSuperclass();  
51.     }  
52.     return false;  
53. }  
54.   
55. private static boolean existsCDATA(Class<?> clazz, String fieldAlias) {  
56.     Field[] fields = clazz.getDeclaredFields();  
57.     for (Field field : fields) {  
58.         if (field.getAnnotation(XStreamCDATA.class) != null) {  
59.             XStreamAlias xStreamAlias = field.getAnnotation(XStreamAlias.class);  
60.             if (null != xStreamAlias) {  
61.                 if (fieldAlias.equals(xStreamAlias.value()))  
62.                     return true;  
63.             } else {  
64.                 if (fieldAlias.equals(field.getName()))  
65.                     return true;  
66.             }  
67.         }  
68.     }  
69.     return false;  
70. }  
 
用到的实体类:
[java] view plain copy
1. public class BaseRespMessage {  
2.   
3.     // 发送方帐号(一个OpenID)  
4.     @XStreamCDATA  
5.     @XStreamAlias("ToUserName")  
6.     private String toUserName;  
7.   
8.     // 开发者微信号  
9.     @XStreamCDATA  
10.     @XStreamAlias("FromUserName")  
11.     private String fromUserName;  
12.   
13.     // 消息创建时间 (整型)  
14.     @XStreamAlias("CreateTime")  
15.     private long createTime;  
16.   
17.     // test  
18.     @XStreamCDATA  
19.     @XStreamAlias("MsgType")  
20.     private String msgType;  
21.   
22.     // getter/setter方法省略  
23. }  
24.   
25. @XStreamAlias("xml")  
26. public class TextRespMessage extends BaseRespMessage {  
27.   
28.     // 文本消息内容  
29.     @XStreamCDATA  
30.     @XStreamAlias("Content")  
31.     private String content;  
32.   
33.     // getter/setter方法省略  
34. }  
35.   
36. @Retention(RetentionPolicy.RUNTIME)    
37. @Target({ElementType.FIELD})  
38. public @interface XStreamCDATA {  
39.   
40. }


分析:
由java到xml主要用到了xstream包,通过toXml方法,直接把对象类转化为xml文档。
但是由于微信要求的xml格式如下,包括头字母大小和<![CDATA[]]>的问题。这里我采用注解的方式来解决这两个问题。
<ToUserName><![CDATA[toUser]]></ToUserName>


在实体类的定义中,toUserName的定义如下,通过@XStreamAlias来设定别名来保证生产的xml中首字母大写。
通过XStreamCDTA的自定义注解,来说明哪些项目需要追加<![CDATA[]]>,具体直接参考代码,还是比较容易理解的。这个方法是在网上看到的方法。
@XStreamCDATA
@XStreamAlias("ToUserName")
private String toUserName;


对于<![CDATA[]]>的问题,除了上述方法外,我通过规律还尝试了另外一种方法。
通过观察xml格式,里面数值类型没有追加<![CDATA[]]>,其他都追加了,所以可以在重写startNode方法的时候,直接判断Class的类型如果为String类型的话,就追加<![CDATA[]]>。这样就能节省很多代码。
注:因为本人没有尝试过所有的微信xml格式,所以不一定全部适用,目前我所遇到的格式都适用。

[java] view plain copy
1. @Override  
2. public void startNode(String name, @SuppressWarnings("rawtypes") Class clazz) {  
3.     super.startNode(name, clazz);  
4.     if(clazz.equals(String.class)) {  
5.         cdata = true;  
6.     } else {  
7.         cdata = false;  
8.     }  
9. }


·其他代码简介


解析xml的代码完成后,这边是调用解析的代码,和简单业务逻辑的代码。

调用的代码:

WechatController
[java] view plain copy
1. @RequestMapping(value = "/verifyWechat", method = RequestMethod.POST)  
2. public void post(HttpServletRequest request, HttpServletResponse response) {  
3.     logger.info("post start.");  
4.   
5.     String xmlMessge = autoReturnService.processRequest(request);  
6.   
7.     PrintWriter writer = null;  
8.   
9.     try {  
10.         writer = response.getWriter();  
11.         writer.print(xmlMessge);  
12.     } catch (IOException e) {  
13.         e.printStackTrace();  
14.         logger.info("post exception.");  
15.     } finally {  
16.         writer.close();  
17.         writer = null;  
18.     }  
19.   
20.     logger.info("post end.");  
21. }


自动回复的业务逻辑的代码
AutoReturnServiceImpl

[java] view plain copy
1. @Service  
2. public class AutoReturnServiceImpl implements AutoReturnService {  
3.   
4.     private static final Logger logger = Logger.getLogger(AutoReturnServiceImpl.class);  
5.   
6.     @Override  
7.     public String processRequest(HttpServletRequest request) {  
8.   
9.         logger.info("processRequest start.");  
10.   
11.         String respXmlMessage = null;  
12.         try {  
13.             Map<String, String> map = MessageUtil.parseXml(request);  
14.   
15.             String toUserName = map.get("ToUserName");  
16.             String fromUserName = map.get("FromUserName");  
17.             String msgType = map.get("MsgType");  
18.   
19.             TextRespMessage textRespMessage = new TextRespMessage();  
20.             textRespMessage.setToUserName(fromUserName);  
21.             textRespMessage.setFromUserName(toUserName);  
22.             textRespMessage.setCreateTime(new Date().getTime());  
23.             textRespMessage.setMsgType(Constants.RESP_MESSAGE_TYPE);  
24.   
25.             // text type  
26.             if (Constants.REQ_MESSAGE_TYPE.equals(msgType)) {  
27.   
28.                 String content = map.get("Content");  
29.                 if (content.contains("1")) {  
30.                     textRespMessage.setContent("收到的是1");  
31.                     respXmlMessage = MessageUtil.textMessageToXml(textRespMessage);  
32.                 } else if (content.contains("2")) {  
33.                     textRespMessage.setContent("收到的是2");  
34.                     respXmlMessage = MessageUtil.textMessageToXml(textRespMessage);  
35.                 } else {  
36.                     textRespMessage.setContent("你说的是什么,我不懂!");  
37.                     respXmlMessage = MessageUtil.textMessageToXml(textRespMessage);  
38.                 }  
39.             } else {  
40.                 textRespMessage.setContent("收到的是我不能理解的类型!");  
41.                 respXmlMessage = MessageUtil.textMessageToXml(textRespMessage);  
42.             }  
43.   
44.         } catch (Exception e) {  
45.             e.printStackTrace();  
46.             respXmlMessage = "success"; // 官网推荐设置  
47.         }  
48.   
49.         logger.info("processRequest end.");  
50.         return respXmlMessage;  
51.     }  
52. }  
使用到的实体类:
[java] view plain copy
1. public class BaseReqMessage {  
2.   
3.     // 开发者微信号  
4.     private String toUserName;  
5.   
6.     // 发送方帐号(一个OpenID)  
7.     private String fromUserName;  
8.   
9.     // 消息创建时间 (整型)  
10.     private long createTime;  
11.   
12.     // test  
13.     private String msgType;  
14.   
15.     // 消息id,64位整型  
16.     private long msgId;  
17.   
18.     // getter/setter方法省略  
19. }  
20.   
21. public class TextReqMessage extends BaseReqMessage {  
22.   
23.     // 文本消息内容  
24.     private String content;  
25.   
26.     // getter/setter方法省略  
27. }


总结:
以上就是就是自动回复消息的简单示例,简单来说,就是处理一个request(post)请求,接收到微信服务器的xml格式的请求,解析xml-进行业务处理-进行封装生xml格式,通过response返回。中间用到的解析xml格式和封装xml格式。


本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之微信频道!

 


本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程