微信-接收消息(5)

来源:互联网 发布:最终幻想13 知乎 编辑:程序博客网 时间:2024/04/28 09:54



注意:微信公众平台文档已经明确的说明当普通用户向公众号发送消息时,微信服务器将通过post方式把xml数据包发送到开发者设置的URL上

以文本消息为例:xml结构如下

<xml> <ToUserName><![CDATA[toUser]]></ToUserName>      //开发者微信号 <FromUserName><![CDATA[fromUser]]></FromUserName>//发送方账号(微信提供的OpenId) <CreateTime>1348831860</CreateTime>              //消息创建的时间(long型) <MsgType><![CDATA[text]]></MsgType>              //消息类型text <Content><![CDATA[this is a test]]></Content>    //文本消息的内容 <MsgId>1234567890123456</MsgId>                  //消息id(64位整型) </xml>


这时候就可以打开eclipse写代码了。

新建一个project。命名随便你,开心就好。

因为此处仅仅以text消息为例子,所以我们先在项目中建立一个Message的基类(父类)。

package com.yunwei.message.request;/** * 消息基类,普通用户-》公众账号 * @author AaronTan * @date 2017年3月3日 上午10:00:54 * @file Weixin com.yunwei.message.request BaseMessage.java */public class BaseMessage {// 开发者微信号private String ToUserName;// 发送者微信号private String FromUserName;// 消息创建时间(整型)private long CreateTime;// 消息类型(text、image、location、link)private String MsgType;// 消息id,64位整型private String MsgId;public String getToUserNameS() {return ToUserName;}public void setToUserNameS(String toUserNameS) {ToUserName = toUserNameS;}public String getFromeUserName() {return FromUserName;}public void setFromeUserName(String fromeUserName) {FromUserName = fromeUserName;}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 String getMsgId() {return MsgId;}public void setMsgId(String msgId) {MsgId = msgId;}}

建立Message基类之后,建立一个Text的类(子类)。

package com.yunwei.message.request;/** * 文本消息基类 *  * @author Administrator *  */public class TextMessage extends BaseMessage {// 消息内容private String content;public String getContent() {return content;}public void setContent(String content) {this.content = content;}}

消息类封装完成之后,开始写核心代码。我们用servlet的个体方法。

package com.yunwei.servlet;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import com.yunwei.message.service.CoreService;import com.yunwei.util.SignUtil;/** * 微信核心请求处理类 */public class CoreServlet extends HttpServlet {public CoreServlet() {super();}public void destroy() {super.destroy(); // Just puts "destroy" string in log// Put your code here}/** * 确认请求来自微信服务器 */public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 微信加密签名String signature = request.getParameter("signature");// 时间戳String timestamp = request.getParameter("timestamp");// 随机数String nonce = request.getParameter("nonce");// 随机字符串String echostr = request.getParameter("echostr");PrintWriter out = response.getWriter();System.out.println("signature:" + signature + "timestamp:" + timestamp+ "nonc:" + nonce + "echostr" + echostr);// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败if (SignUtil.checkSignature(signature, timestamp, nonce)) {out.print(echostr);}out.close();out = null;}}
到这里为止,我们自己的服务器就可以接收到微信发来的消息了,下一步配置web.xml

<?xml version="1.0" encoding="UTF-8"?><web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"><servlet><description>This is the description of my J2EE component</description><display-name>This is the display name of my J2EE component</display-name><servlet-name>CoreServlet</servlet-name><servlet-class>com.yunwei.servlet.CoreServlet</servlet-class></servlet><servlet-mapping><servlet-name>CoreServlet</servlet-name><url-pattern>/servlet/CoreServlet</url-pattern></servlet-mapping><welcome-file-list><welcome-file>index.jsp</welcome-file></welcome-file-list></web-app>

配置完xml,通过url就可以访问到doget方法。

还有一个问题就是,微信给我们服务器的消息是XML结构的,我们要解析一下消息。

所以,现在我们应该新建一个类,MessageUtil来解析xml消息。用dom4j来解析。

package com.yunwei.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 com.sun.xml.internal.bind.v2.runtime.Name;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;import com.yunwei.message.response.TextMessage;import com.yunwei.message.response.Article;import com.yunwei.message.response.MusicMessage;import com.yunwei.message.response.NewsMessage;/** * 消息工具类 *  * @author AaronTan * @date 2017年3月10日 下午8:40:19 * @file Weixin com.yunwei.util MessageUtil.java */public class MessageUtil {/** * 返回消息类型:文本 */public static final String RESP_MESSAGE_TYPE_TEXT = "text";/** * 解析微信发来的请求(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>();// 从请求中获取输入流InputStream is = request.getInputStream();// 读取输入流SAXReader reader = new SAXReader();Document document = reader.read(is);// 得到xml根源素Element root = document.getRootElement();// 得到根源素的所有子节点List<Element> list = root.elements();// 遍历所有子节点for (Element element : list) {map.put(element.getName(), element.getText());}// 释放资源is.close();is = null;return map;}/** * 文本消息对象转换成xml *  * @param textMessage *            文本消息对象 * @return xml */public static String textMessageToXml(TextMessage textMessage) {xstream.alias("xml", textMessage.getClass());return xstream.toXML(textMessage);}/** * 扩展xstream,使其支持CDATA块 *  * @date 2017年3月10日 下午9:18:19 */private static XStream xstream = new XStream(new XppDriver() {public HierarchicalStreamWriter createWriter(Writer out) {return new PrettyPrintWriter(out) {// 对所有xml节点的转换都增加CDATA标记,除了CreatTimeboolean cdata = true;String noteName = null;@SuppressWarnings("unchecked")public void startNode(String name, Class clazz) {noteName = name;super.startNode(name, clazz);}protected void writeText(QuickWriter writer, String text) {System.out.print(noteName+":");if ("CreateTime".equals(noteName)) {writer.write(text);System.out.println(text);} else {writer.write("<![CDATA[");writer.write(text);writer.write("]]>");System.out.println(text);}}};}});}

还有最后一项就是服务器的校验类,我们封装成为了一个单独的类。

package com.yunwei.util;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.Arrays;/** *  * 请求校验工具类 *  * @author Administrator *  */public class SignUtil {// 与接口配置信息中的Token要一致private static String token = "123";/** * 校验签名 */public static boolean checkSignature(String signature, String timestamp,String nonce) {System.out.println("signature:"+signature+"timestamp:"+timestamp+"nonc:"+nonce);String[] arr = new String[] { token, timestamp, nonce };// 将token、timestamp、nonce三个参数进行字典序排序Arrays.sort(arr);StringBuilder content = new StringBuilder();for (int i = 0; i < arr.length; i++) {content.append(arr[i]);}MessageDigest md = null;String tmpStr = null;try {md = MessageDigest.getInstance("SHA-1");// 将三个参数字符串拼接成一个字符串进行sha1加密byte[] digest = md.digest(content.toString().getBytes());tmpStr = byteToStr(digest);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}content = null;// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信System.out.println(tmpStr.equals(signature.toUpperCase()));return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;}/** * 将字节数组转换为十六进制字符串 *  * @param byteArray * @return */private static String byteToStr(byte[] byteArray) {String strDigest = "";for (int i = 0; i < byteArray.length; i++) {strDigest += byteToHexStr(byteArray[i]);}return strDigest;}/** * 将字节转换为十六进制字符串 *  * @param mByte * @return */private static String byteToHexStr(byte mByte) {char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A','B', 'C', 'D', 'E', 'F' };char[] tempArr = new char[2];tempArr[0] = Digit[(mByte >>> 4) & 0X0F];tempArr[1] = Digit[mByte & 0X0F];String s = new String(tempArr);return s;}}

当我们的项目成功接收到微信发来的消息之后,我们应该做出一个处理,所以我们还要新建一个处理类,我们命名为CoreService,作为处理类

package com.yunwei.message.service;import java.util.Date;import java.util.Map;import javax.servlet.http.HttpServletRequest;import com.yunwei.message.response.TextMessage;import com.yunwei.util.MessageUtil;/** * 核心服务类 *  * @author AaronTan * @date 2017年3月10日 下午9:27:11 * @file Weixin com.yunwei.message.service CoreService.java */public class CoreService {/** * 处理微信发来的请求 *  * @param request * @return */public static String processRequest(HttpServletRequest request) {String respMessage = null;try {// 默认返回的文本消息内容String respContent = "请求处理异常,请稍候尝试!";// xml请求解析Map<String, String> requestMap = MessageUtil.parseXml(request);// 发送方帐号(open_id)String fromUserName = requestMap.get("FromUserName");// 公众帐号String toUserName = requestMap.get("ToUserName");// 消息类型String msgType = requestMap.get("MsgType");// 消息内容String content = requestMap.get("Content");// 回复文本消息TextMessage textMessage = new TextMessage();textMessage.setToUserName(fromUserName);textMessage.setFromUserName(toUserName);textMessage.setCreateTime(new Date().getTime());textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);// 文本消息if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {if (content.equals(MessageUtil.RESP_MESSAGE_TYPE_HELP)) {System.out.println("您发送的是文本消息!");respContent = "您发送的是文本消息!";respContent = "您获得了一条帮助消息!";}else {respContent = "您发送的关键字不对,请重新发送。";}System.out.println("您发送的是文本消息!");respContent = "您发送的是文本消息!";}textMessage.setContent(respContent);respMessage = MessageUtil.textMessageToXml(textMessage);} catch (Exception e) {e.printStackTrace();}return respMessage;}}


最后完成之后结构目录就是现在可以看到的样子

所以最后啰嗦一句,从web.xml中就可以看出来,在服务器配置中需要填入的url为:http://你自己的域名/Weixin/servlet/CoreServlet




0 0