(6)微信二次开发 之 微信文本消息接口实现

1、微信文本消息接口-理解

1)接受消息的理解

微信服务器(自有的服务器)接收来自普通微信用户发往微信公众号的消息。

2)发送消息的理解

微信服务器(自有服务器)发往普通微信用户的消息。

3)消息处理的三种模式

  明文模式、兼容模式、安全模式。明文就是微信服务器和微信用户之间的发送消息是明文,安全模式就是发送和接收需要经过加密和解密算法来实现,

兼容模式接收和发送,一者是明文,另一个是密文的方式。

4)微信服务出现异常的情况

按照目前的情况,微信服务器在5秒内收不到响应会断掉连接,并且重新发起请求,总共重试3次。假如服务器无法保证在5秒内处理回复,

可以直接回复空字串,微信服务器不做任何处理。

2、微信文本消息接口-处理过程

1)接收消息

  微信用户请求资源 --> 微信服务器接收用户的发来信息 --> 由微信服务器中转给我们自己的微信服务器(例如自己买的阿里云、

腾讯云等其他配置的web服务器,配置成自己微信服务器)。

2)发送消息(响应消息)

  我们自己的微信服务器发送消息 --> 经过微信服务器 --> 由微信服务器中转给微信用户。

3)对普通文本消息类型的处理流程

  普通微信用户发送文本消息到微信服务器,微信服务器将发送post请求到我们自己的服务器(带上signature,timestamp,nonce三个参数),

部署在我们服务器的程序,首先要获得用户发过来消息的参数(FromUserName、ToUserName、MsgType、CreateTime、Content),

然后将要响应的消息打包(TextMessage对象,这个对象就是响应消息的一些参数),并将TextMessage对象的数据转为符合要求的xml数据进行响应即可。

 

3、微信文本消息接口-代码实现

注意:这里的代码是第四节的开发者模式与请求验证的代码基础上进行编写。

1)在ValidationServlet这个servlet类中重写doPost方法,主要是获取signature、timestamp、nonce这三个字段,掉用之前请求验证方法checkSignature是否通过,通过则进行解析普通用户请求的参数到微信服务器,经微信服务器中转到自己的微信服务器的数据进行解析 。重点看doPost方法:

 

package com.aixunma.wechat;

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.aixunma.wechat.util.ValidationTool;
/**
 * 用来请求微信服务器请求验证
 * <p>类的描述:</p>
 * <p>@Description: TODO</p>
 * <p>@author 小海</p>
 * <p>@time:2017年4月27日 下午10:14:10</p>
 * <p>@Vesion: 1.0</p>
 */
public class ValidationServlet extends HttpServlet{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    
    /**
     * 微信基本配置请求验证
     * 当开发者通过微信公众??在基本配置中提交按钮,执行该方法
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        response.setCharacterEncoding("UTF-8"); // 设置编码
        
        /*
         *获取微信服务器往自己的服务器的请求参数中传递的4个参数 
         */
        
        // 签名之字符串
        final String signature = request.getParameter("signature");
        
        // 时间戳
        final String timestamp = request.getParameter("timestamp");
        
        // 随机数
        final String nonce = request.getParameter("nonce");
        
        // 随机字符串
        final String echostr = request.getParameter("echostr");
        
        StringBuilder builder = new StringBuilder();
        builder.append("签名之字符串:").append(signature)
            .append("\n")
            .append("时间戳:").append(timestamp)
            .append("\n")
            .append("随机数").append(nonce)
            .append("\n")
            .append("随机字符串").append(echostr)
            .append("\n").append("-------------------------");
        // 输出
        System.out.println(builder.toString());
        
        // 验证
        final boolean result = ValidationTool.checkSignature(signature, timestamp, nonce);
        // 输出
        final PrintWriter writer = response.getWriter();
        if (result == true) {
            // 校验成功后返回原样echostr
            writer.println(echostr);
        }
        
        writer.close();
    }
    
    /**
     * 微信消息文本提交是POST方式,用户在公众号提交信息,执行该方法
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置请求编码和响应编码
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        
        // 签名之字符串
        final String signature = request.getParameter("signature");    
        // 时间戳
        final String timestamp = request.getParameter("timestamp");
        // 随机数
        final String nonce = request.getParameter("nonce");
        // 响应输出
        final PrintWriter out = response.getWriter();
        // 声明接受xml的字符串
        
        System.out.println("消息请求方法");
        
        String resultXml = "";
        //验证通过
        if (ValidationTool.checkSignature(signature, timestamp, nonce)) {
            // 解析request请求的过来的参数数据,并且范围xml格式的字符串
            try {
                resultXml = ProcessService.processRequest(request);
                out.write(resultXml);
            } catch (Exception e) {
                try {
                    throw new Exception("解析request的请求数据失败");
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        }
        
        out.close();
    }
    
}

2)编写接收微信用户请求发送的数据进行XML解析的方法,创建一个ProcessService类,是信息核心处理类。该类中创建一个processRequest方法,是用来解析普通微信用户请求发送来的数据进行解析,并且组装数据成XML格式的数据响应给微信用户。

package com.aixunma.wechat;

import java.util.Date;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import com.aixunma.wechat.message.TextMessges;
import com.aixunma.wechat.util.MessageUtil;
/**
 * 信息核心处理类
 * <p>类的描述:</p>
 * <p>@Description: TODO</p>
 * <p>@author 小海</p>
 * <p>@time:2017年4月30日 下午1:53:57</p>
 * <p>@Vesion: 1.0</p>
 */
public class ProcessService {
    /**
     * 接受微信用户请求发送来的数据进行解析
     * 然后进行组装数据成xml格式的数据响应给微信用户
     * @param request
     * @return
     * @throws Exception
     */
    public static String processRequest(HttpServletRequest request) throws Exception {
        
        // 使用消息工具类方法进行请求参数解析存储在Map集合中
        final Map<String, String> resultMap = MessageUtil.parseXML(request);
        // 获取想要的数据
        // 开发者微信号:接收者,就是自己的服务器
        final String toUserName = resultMap.get("ToUserName");
        // 发送方帐号(一个OpenID):微信用户
        final String fromUserName = resultMap.get("FromUserName");
        // 消息创建时间 (整型)
        final String createTime = resultMap.get("CreateTime");
        // 文本类型
        final String msgType = resultMap.get("MsgType");
        // 文本消息内容
        final String content = resultMap.get("Content");
        // 消息id,64位整型
        final String msgId = resultMap.get("MsgId");
        // 输入获取的信息
        final StringBuilder reqData = new StringBuilder();
        reqData.append("----------接收开始----------").append("\n")
            .append("消息的微信发送者:").append(fromUserName).append("\n")
            .append("消息的接收者:").append(toUserName).append("\n")
            .append("消息的发送时间:").append(createTime).append("\n")
            .append("消息的类型:").append(msgType).append("\n")
            .append("消息的内容:").append(content).append("\n")
            .append("消息的id:").append(msgId).append("\n")
            .append("----------接收结束----------").append("\n");
        System.out.println(reqData.toString());
        
        String resultXml = "";
        // 这里对消息类型进行判断,如果是文本类型就恢复给用户,其它图文,视频等暂时不回复。
        if (MessageUtil.REQ_MESSAGE_TYPE_TEXT.equals(msgType)) {
            // 组装数据信息响应给微信用户
            TextMessges messges = new TextMessges();
            messges.setToUserName(fromUserName); // 发送给之前给服务器的OpenID
            messges.setFromUserName(toUserName); // 发送者是开发者
            messges.setCreateTime(new Date().getTime()); // 恢复时间
            StringBuilder send = new StringBuilder("我们已经收到您发来的信息:");
            send.append(content).append("\n")
                .append("非常感谢您的来信,我们尽快与您进行沟通交流,谢谢!").append("\n")
                .append("时间:").append(new Date().toLocaleString());
            messges.setContent(send.toString()); // 发送的消息内容
            messges.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT); // 响应消息的类型
            
            resultXml = MessageUtil.messageToXml(messges);
        }
        return resultXml;
    }
}

3)需要创建一个TextMessges类作为自己的微信服务器的消息文本数据响应封装,在讲这个对象数据进行生成XML格式的数据返回。在MessageUtil类的messageToXml的方法就是将一个TextMessges对象转换成XML格式的字符串。该类的中的parseXML方法是将请求获取的XML格式进行解析。

MessageUtil消息文本处理工具类代码:

package com.aixunma.wechat.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.LinkedHashMap;
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.aixunma.wechat.message.TextMessges;
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;

/**
 * 消息文本处理工具类
 * <p>类的描述:</p>
 * <p>@Description: TODO</p>
 * <p>@author 小海</p>
 * <p>@time:2017年4月30日 下午1:28:16</p>
 * <p>@Vesion: 1.0</p>
 */
public class MessageUtil {
    // 请求的文本类型
    public static final String REQ_MESSAGE_TYPE_TEXT = "text";
    
    // 响应的文本类型
    public static final String RESP_MESSAGE_TYPE_TEXT = "text";
    
    /**
     * 解析从请求的获取的xml格式的字符串
     * 使用dom4j的方式进行解析
     * @param request
     * @return
     * @throws IOException 
     * @throws Exception 
     */
    public static Map<String, String> parseXML(HttpServletRequest request) throws Exception {
        /*
         * 请求中的XML的格式:请查询微信开发文档
         *<xml>
          *<ToUserName><![CDATA[toUser]]></ToUserName>
          *<FromUserName><![CDATA[fromUser]]></FromUserName>
          *<CreateTime>1348831860</CreateTime>
          *<MsgType><![CDATA[text]]></MsgType>
          *<Content><![CDATA[this is a test]]></Content>
          *<MsgId>1234567890123456</MsgId>
          *</xml>
         */
        
        // 记录XML解析处理的数据进行存储:key是ToUserName,value是<![CDATA[toUser]]>....
        Map<String, String> map = new LinkedHashMap<String,String>();
        
        // 根据请求request对象获取流对象
        final InputStream inputStream = request.getInputStream();
        
        /* DOM4J解析 */
        // 创建SAX解析构造器对象
        final SAXReader reader = new SAXReader();
        // 通过读取流的对象,获取文档对象
        final Document document = reader.read(inputStream);
        // 获取跟节点:<xml> 这个是就更节点
        final Element root = document.getRootElement();
        // 获取跟节点下的子节点
        final List<Element> elements = root.elements();
        // 遍历解析
        for (Element element : elements) {
            // 节点名称和节点的值
            map.put(element.getName(), element.getText());
        }
        
        // 关闭流
        if (inputStream != null) {
            inputStream.close();
        }
        return map;
    }
    
    /**
     * 将消息文本对象的数据转换成XML格式的字符串
     * @param messges 消息文本对象
     * @return
     */
    public static String messageToXml(TextMessges messges) {
        // 给对象取个别名
        xStream.alias("xml", messges.getClass());
        final String resultMml = xStream.toXML(messges);
        System.out.println(resultMml);
        return resultMml;
    }
    
    /*@Test
    public void test() {
        TextMessges messges = new TextMessges();
        messges.setToUserName("1");
        messges.setFromUserName("2");
        messges.setCreateTime(new Date().getTime());
        messges.setContent("3");
        messges.setMsgType("text");
        MessageUtil.messageToXml(messges);
    }*/
    
    /**
     * 用于扩展节点数据按照<ToUserName><![CDATA[toUser]]></ToUserName>,中间加上CDATA。
     */
    private static XStream xStream = new XStream(new XppDriver() {
        public HierarchicalStreamWriter createWriter(Writer out) {
            return new PrettyPrintWriter(out) {
                boolean cdata = true;
                
                public void startNode(String name, Class claszz) {
                    // 将字符串的首个字母转换成大写:微信规定是标签首个字母大写,除了根标签xml外
                    /*if (!"xml".equals(name)) {
                        name = toUpperCaseFirstOne(name);
                    }*/
                    super.startNode(name, claszz);
                }
                
                protected void writerText(QuickWriter writer, String text) {
                    if (cdata) {
                        writer.write("<![CDATA[");
                        writer.write(text);
                        writer.write("]]>");
                    } else {
                        writer.write(text);
                    }
                }
            };
        }
    });
    
    /**
     * 将字符串的的首个字符变成大写
     * @param name
     * @return
     */
    public static String toUpperCaseFirstOne(String name) {
        if (name != null) {
            char[] ch = name.toCharArray();
            for (int i = 0; i < ch.length; i++) {
                if (i == 0) {
                    ch[0] = Character.toUpperCase(ch[0]);
                } else {
                    ch[i] = Character.toLowerCase(ch[i]);
                }
            }
            StringBuffer a = new StringBuffer();
            a.append(ch);
            return a.toString();
        }
        return name;
    }
}

TextMessages对象代码:该类继承BaseMessage的响应消息基类

package com.aixunma.wechat.message;
/**
 * 消息文本类
 * 可以做数据的接收和响应
 * <p>类的描述:</p>
 * <p>@Description: TODO</p>
 * <p>@author 小海</p>
 * <p>@time:2017年4月30日 下午2:22:20</p>
 * <p>@Vesion: 1.0</p>
 */
public class TextMessges extends BaseMessage{
    private String Content; // 消息的文本内容

    public String getContent() {
        return Content;
    }

    public void setContent(String content) {
        Content = content;
    }
    
}

BaseMessage响应消息基类代码:

package com.aixunma.wechat.message;
/**
 * 响应消息的基类:服务器发给微信用户
 * <p>类的描述:</p>
 * <p>@Description: TODO</p>
 * <p>@author 小海</p>
 * <p>@time:2017年4月30日 下午2:17:04</p>
 * <p>@Vesion: 1.0</p>
 */
public class BaseMessage {
    private String ToUserName; // 消息接收者
    private String FromUserName; // 消息发送者
    private long CreateTime; // 发送时间
    private String MsgType; // 消息类型
    public String getToUserName() {
        return ToUserName;
    }
    public void setToUserName(String toUserName) {
        ToUserName = toUserName;
    }
    public String getFromUserName() {
        return FromUserName;
    }
    public void setFromUserName(String fromUserName) {
        FromUserName = fromUserName;
    }
    public long getCreateTime() {
        return CreateTime;
    }
    public void setCreateTime(long createTime) {
        CreateTime = createTime;
    }
    public String getMsgType() {
        return MsgType;
    }
    public void setMsgType(String msgType) {
        MsgType = msgType;
    }
}

4、项目所用的jar包

(6)微信二次开发 之 微信文本消息接口实现

 

5、将项目进行打包上传服务器测试结果

我是在腾讯云服务器下部署在tomcat7下,启动,测试结果显示。

(6)微信二次开发 之 微信文本消息接口实现

5、致谢

感谢您的关注,有相关代码问题,联系我,希望我们一起共同进步。谢谢!

 

(6)微信二次开发 之 微信文本消息接口实现

上一篇:JAVA微信服务号开发简记


下一篇:Android版微信小代码(转)