白羽
2018-06-06
来源 :网络
阅读 1669
评论 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
您输入的评论内容中包含违禁敏感词
我知道了

请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号