松哥原创的 Spring Boot 视频教程已经杀青,感兴趣的小伙伴戳这里–>Spring Boot Vue 微人事视频教程
1.缘起
2.实现思路
4.开发
4.1 服务器有效性校验
4.2 消息接收接口
5.消息分类
6.返回消息类型定义
7.返回消息生成
8.返回消息分发
1.缘起
于是我就在想,该写点代码了。
2.实现思路
其实松哥这个回复口令获取视频链接的实现原理很简单,说白了,就是一个数据查询操作而已,回复的口令是查询关键字,回复的内容则是查询结果。这个原理很简单。
这张图,我给大家稍微解释下:
大致的流程就是这个样子。
接下来我们就来看一下实现细节。
Token 可由开发者可以任意填写,用作生成签名(该 Token 会和接口 URL 中包含的 Token 进行比对,从而验证安全性)。
EncodingAESKey 由开发者手动填写或随机生成,将用作消息体加解密密钥。
4.开发
4.1 服务器有效性校验
我们首先来创建一个普通的 Spring Boot 项目,创建时引入 spring-boot-starter-web 依赖,项目创建成功后,我们创建一个 Controller ,添加如下接口:
@GetMapping(“/verify_wx_token”)publicvoidlogin(HttpServletRequestrequest,HttpServletResponseresponse)throwsUnsupportedEncodingException{request.setCharacterEncoding(“UTF-8”);Stringsignature=request.getParameter(“signature”);Stringtimestamp=request.getParameter(“timestamp”);Stringnonce=request.getParameter(“nonce”);Stringechostr=request.getParameter(“echostr”);PrintWriterout=null;try{out=response.getWriter();if(CheckUtil.checkSignature(signature,timestamp,nonce)){out.write(echostr);}}catch(IOExceptione){e.printStackTrace();}finally{out.close();}}
关于这段代码,我做如下解释:
校验代码如下:
OK,完成之后,我们的校验接口就算是开发完成了。接下来就可以开发消息接收接口了。
4.2 消息接收接口
<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName><CreateTime>1348831860</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[thisisatest]]></Content><MsgId>1234567890123456</MsgId></xml>
这些参数含义如下:
这里我们先来个简单的,我们将收到的消息解析并打印出来:
@PostMapping(“/verify_wx_token”)publicvoidhandler(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{request.setCharacterEncoding(“UTF-8”);response.setCharacterEncoding(“UTF-8”);PrintWriterout=response.getWriter();Map<String,String>parseXml=MessageUtil.parseXml(request);StringmsgType=parseXml.get(“MsgType”);Stringcontent=parseXml.get(“Content”);Stringfromusername=parseXml.get(“FromUserName”);Stringtousername=parseXml.get(“ToUserName”);System.out.println(msgType);System.out.println(content);System.out.println(fromusername);System.out.println(tousername);}publicstaticMap<String,String>parseXml(HttpServletRequestrequest)throwsException{Map<String,String>map=newHashMap<String,String>();InputStreaminputStream=request.getInputStream();SAXReaderreader=newSAXReader();Documentdocument=reader.read(inputStream);Elementroot=document.getRootElement();List<Element>elementList=root.elements();for(Elemente:elementList)map.put(e.getName(),e.getText());inputStream.close();inputStream=null;returnmap;}
大家看到其实都是一些常规代码,没有什么难度。
5.消息分类
普通消息主要是指:
不同的消息类型,对应不同的 MsgType,这里我还是以普通消息为例,如下:
这是消息的接收,除了消息的接收之外,还有一个消息的回复,我们回复的消息也有很多类型,可以回复普通消息,也可以回复图片消息,回复语音消息等,不同的回复消息我们可以进行相应的封装。因为不同的返回消息实例也是有一些共同的属性的,例如消息是谁发来的,发给谁,消息类型,消息 id 等,所以我们可以将这些共同的属性定义成一个父类,然后不同的消息再去继承这个父类。
6.返回消息类型定义
首先我们来定义一个公共的消息类型:
publicclassBaseMessage{privateStringToUserName;privateStringFromUserName;privatelongCreateTime;privateStringMsgType;privatelongMsgId;//省略getter/setter}
在这里:
这是我们的基本消息类型,就是说,我们返回给用户的消息,无论是什么类型的消息,都有这几个基本属性。然后在此基础上,我们再去扩展出文本消息、图片消息 等。
我们来看下文本消息的定义:
publicclassTextMessageextendsBaseMessage{privateStringContent;//省略getter/setter}
文本消息在前面消息的基础上多了一个 Content 属性,因此文本消息继承自 BaseMessage ,再额外添加一个 Content 属性即可。
7.返回消息生成
消息类型的 Bean 定义完成之后,接下来就是将实体类生成 XML。
首先我们定义一个消息工具类,将常见的消息类型枚举出来:
publicstaticStringtextMessageToXml(TextMessagetextMessage){xstream.alias(“xml”,textMessage.getClass());returnxstream.toXML(textMessage);}privatestaticXStreamxstream=newXStream(newXppDriver(){publicHierarchicalStreamWritercreateWriter(Writerout){returnnewPrettyPrintWriter(out){booleancdata=true;@SuppressWarnings(“rawtypes”)publicvoidstartNode(Stringname,Classclazz){super.startNode(name,clazz);}protectedvoidwriteText(QuickWriterwriter,Stringtext){if(cdata){writer.write(“<![CDATA[“);writer.write(text);writer.write(“]]>”);}else{writer.write(text);}}};}});
8.返回消息分发
由于用户发来的消息可能存在多种情况,我们需要分类进行处理,这个就涉及到返回消息的分发问题。因此我在这里再定义一个返回消息分发的工具类,如下:
这里我们还可以多加几个 elseif 去判断不同的消息类型,我这里因为只有普通文本消息,所以一个 if 就够用了。
最后在消息接收 Controller 中调用该方法,如下:
@PostMapping(value=”/verify_wx_token”,produces=”application/xml;charset=utf-8″)publicStringhandler(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{request.setCharacterEncoding(“UTF-8”);Map<String,String>map=MessageUtil.parseXml(request);StringmsgType=map.get(“MsgType”);if(MessageUtil.REQ_MESSAGE_TYPE_EVENT.equals(msgType)){returnmessageDispatcher.processEvent(map);}else{returnmessageDispatcher.processMessage(map);}}
在 Controller 中,我们首先判断消息是否是事件,如果是事件,进入到事件处理通道,如果不是事件,则进入到消息处理通道。
「注意,这里需要配置一下返回消息的编码,否则可能会出现中文乱码。」
好了,本文我们就先说到这里,感兴趣的小伙伴不妨试试。