微信公众号开发(三)------消息的接收与回复

微信公众号开发(三)------消息的接收与回复

前言

现在,我们已经封装好了各种消息对应的javaBean,下面就可以实现消息的识别,以及对各类消息的回复.

正文

我们查看微信的官方文档可以看到:

当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。

请注意:

1、关于重试的消息排重,推荐使用msgid排重。

2、微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。假如服务器无法保证在五秒内处理并回复,

可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。详情请见“发送消息-被动回复消息”。

3、如果开发者需要对用户消息在5秒内立即做出回应,即使用“发送消息-被动回复消息”接口向用户被动回复消息时,可以在

公众平台官网的开发者中心处设置消息加密。开启加密后,用户发来的消息和开发者回复的消息都会被加密(但开发者通过客服

接口等API调用形式向用户发送消息,则不受影响)。

也就是说,用户在给我们的公众号发送消息的时候,微信服务器会将消息的内容封装成xml,且以POST请求发送到我们的服务器,因此,我们就可以在servlet的doPost方法中实现我们的处理逻辑.

xml和java对象的相互转化

在这里,我们需要考虑一个问题,就是xml消息和我们封装的javaBean的转化,我们可以使用java原生的API来转化,但是这里我推荐使用开源的工具包dom4jxStream.

关于这两个小工具的使用,百度即可,下面贴上工具包的MAVEN地址:

<!--引入dom4j解析xml-->
    <!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.thoughtworks.xstream/xstream -->
    <dependency>
      <groupId>com.thoughtworks.xstream</groupId>
      <artifactId>xstream</artifactId>
      <version>1.4.10</version>
    </dependency>

我们现在自己定义一个小工具类,用来方便我们处理消息"

消息处理工具类
package com.xiaojian.utils;

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.xiaojian.bean.commons.Article;
import com.xiaojian.bean.response.*;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 消息处理工具类
 */
public class MessageUtil {

    //请求消息的类型------------------------------------------
    //文本
    public static final String REQ_MESSAGE_TYPE_TEXT = "text";

    //图片
    public static final String REQ_MESSAGE_TYPE_IMAGE = "image";

    //语音
    public static final String REQ_MESSAGE_TYPE_VOICE = "voice";

    //视频
    public static final String REQ_MESSAGE_TYPE_VIDEO = "video";

    //小视频
    public static final String REQ_MESSAGE_TYPE_SHORTVIDEO = "shortvideo";

    //地理位置
    public static final String REQ_MESSAGE_TYPE_LOCATION = "location";

    //链接
    public static final String REQ_MESSAGE_TYPE_LINK = "link";

    //事件推送
    public static final String REQ_MESSAGE_TYPE_EVENT = "event";

    //事件类型--订阅
    public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";

    //取消订阅
    public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";

    //已关注用户扫描带参数二维码
    public static final String EVENT_TYPE_SCAN = "SCAN";

    //上报地理位置
    public  static final String EVENT_TYPE_LOCATION = "LOCATION";

    //自定义菜单事件
    public static final String EVENT_TYPE_CLICK = "CLICK";

    //响应消息类型
    //文本
    public static final String RESP_MESSAGE_TYPE_TEXT = "text";

    //图片
    public static final String RESP_MESSAGE_TYPE_IMAGE = "image";

    //语音
    public static final String RESP_MESSAGE_TYPE_VOICE = "voice";

    //视频
    public static final String RESP_MESSAGE_TYPE_VIDEO = "video";

    //音乐
    public static final String RESP_MESSAGE_TYPE_MUSIC = "music";

    //图文
    public static final String RESP_MESSAGE_TYPE_ARTICLE = "news";

    //---------------------------------------------------------------------

    /**
     * 解析xml请求
     */
    public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
        Map<String, String> map = new HashMap<>();
        //取出输入流,使用dom4J获取文档对象
        InputStream inputStream = request.getInputStream();
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        Element rootElement = document.getRootElement();
        //获取根元素的所有节点
        List<Element> elements = rootElement.elements();

        //遍历节点,将数据存入map
        for (Element e: elements) {
            map.put(e.getName(), e.getText());
        }

        inputStream.close();
        inputStream = null;
        return map;
    }


    /**
     * 扩展xstream,支持CDATA
     */
    private static XStream xStream = new XStream(new XppDriver() {
        @Override
        public HierarchicalStreamWriter createWriter(Writer out) {
            return new PrettyPrintWriter(out) {
                boolean cdate = true;

                public void startNode(String name, Class clazz) {
                    super.startNode(name, clazz);
                }

                protected void writeText(QuickWriter writer, String text) {
                    if (cdate) {
                        writer.write("<![CDATA[");
                        writer.write(text);
                        writer.write("]]>");
                    }else {
                        writer.write(text);
                    }
                }
            };
        }
    });


    /**
     * 文本消息转换成xml
     */
    public static String messageToXml(TextMessageResponse textMessageResponse) {
        xStream.alias("xml", textMessageResponse.getClass());
        return xStream.toXML(textMessageResponse);
    }

    /**
     * 图片消息对象转换xml
     */
    public static String messageToXml(ImageMessageResponse imageMessageResponse) {
        xStream.alias("xml", imageMessageResponse.getClass());
        return xStream.toXML(imageMessageResponse);
    }

    /**
     * 语音消息转换为xml
     */
    public static String messageToXml(VoiceMessageResponse voiceMessageResponse) {
        xStream.alias("xml", voiceMessageResponse.getClass());
        return xStream.toXML(voiceMessageResponse);
    }

    /**
     * 视频消息转换成xml
     */
    public static String messageToXml(VideoMessageResponse videoMessageResponse) {
        xStream.alias("xml", videoMessageResponse.getClass());
        return xStream.toXML(videoMessageResponse);
    }

    /**
     * 音乐消息转换xml
     */
    public static String messageToXml(MusicMessageResponse musicMessageResponse) {
        xStream.alias("xml", musicMessageResponse.getClass());
        return xStream.toXML(musicMessageResponse);
    }

    /**
     * 图文消息转换成xml
     */
    public static String messageToXml(ArticleMessageResponse articleMessageResponse) {
        xStream.alias("xml", articleMessageResponse.getClass());
        xStream.alias("item", new Article().getClass());
        return xStream.toXML(articleMessageResponse);
    }
}
完成CoreServlet的POST方法

有了以上的工具类,我们就可以方便的处理微信发送的消息了,我们完善CoreServlet:

package com.xiaojian.servlet;


import com.xiaojian.service.CoreService;
import com.xiaojian.utils.SignUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 微信核心servlet
 */
public class CoreServlet extends HttpServlet {

    private static final Logger logger = LoggerFactory.getLogger(CoreServlet.class);

    /**
     * 确认请求来自微信服务器
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        logger.info("微信请求接入>>>");
        //获取微信加密签名
        String signature = req.getParameter("signature");
        logger.info("微信签名是: {}", signature);
        //获取时间戳
        String timestamp = req.getParameter("timestamp");
        //获取随机数
        String nonce = req.getParameter("nonce");
        //获取随机字符串
        String echostr = req.getParameter("echostr");

        PrintWriter out = resp.getWriter();
        //判断加密后的字符串和签名是否一样.如果一样表示接入成功
        if (SignUtil.checkSignature(signature, timestamp, nonce)) {
            out.print(echostr);
        }
        out.close();
        out = null;
    }

    /**
     * 处理微信消息
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        logger.info("收到用户请求:{}", req);
        //调用service处理请求
        String respXml = CoreService.processRequest(req);

        //响应
        PrintWriter writer = resp.getWriter();
        writer.print(respXml);
        writer.close();

    }
}
创建一个CoreService来处理消息
package com.xiaojian.service;

import com.xiaojian.bean.response.TextMessageResponse;
import com.xiaojian.utils.MessageUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Map;

/**
 * 核心服务类
 */
public class CoreService {

    private static final Logger logger = LoggerFactory.getLogger(CoreService.class);
    /**
     * 处理微信请求
     */
    public static String processRequest(HttpServletRequest request) {
        logger.info("CoreService开始处理请求>>>>>>>>>>");

        String respXml = null;

        //返回体
        String respContent = "未知的消息类型!";

        try {

            Map<String, String> requestMap = MessageUtil.parseXml(request);
            //取出基本参数
            String fromUserName = requestMap.get("FromUserName");
            String toUserName = requestMap.get("ToUserName");
            String msgType = requestMap.get("MsgType");

            //对所有的类型都回复文本消息
            TextMessageResponse textMessageResponse = new TextMessageResponse();
            textMessageResponse.setFromUserName(toUserName);
            textMessageResponse.setToUserName(fromUserName);
            textMessageResponse.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
            textMessageResponse.setCreateTime(new Date().getTime());

            //判断消息类型,获取对应的返回消息
            respContent = setRespContent(requestMap);
            textMessageResponse.setContent(respContent);

            //转换为xml
            respXml = MessageUtil.messageToXml(textMessageResponse);
            logger.info("处理结束,返回参数为: {}", respXml);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return respXml;
    }


    public static String setRespContent(Map<String, String> requestMap) {
        String respContent = null;
        String msgType = requestMap.get("MsgType");
        //判断是事件还是消息
        if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
            //如果是事件,则多一个属性eventType
            String eventType = requestMap.get("Event");
            if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {
                respContent = "谢谢您的关注!";
            } else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {
                //用户取消关注后不用回复消息.
            } else if (eventType.equals(MessageUtil.EVENT_TYPE_SCAN)) {
                //扫描带参数的二维码
            } else if (eventType.equals(MessageUtil.EVENT_TYPE_LOCATION)) {
                //上报地理位置事件
            } else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {
                //处理自定义菜单点击事件
            }
        } else {
            if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
                respContent = "文本消息~~~~~~!";
            } else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) {
                respContent = "图片消息~~~~~~!";
            } else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) {
                respContent = "语音消息~~~~~~!";
            } else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VIDEO)) {
                respContent = "视频消息~~~~~~!";
            } else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_SHORTVIDEO)) {
                respContent = "小视频消息~~~~~~!";
            } else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)) {
                respContent = "地理位置消息~~~~~~!";
            } else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) {
                respContent = "链接消息~~~~~~!";
            }
        }
        return respContent;
    }
}

OK,现在我们可以测试了~~~

在这里插入图片描述
测试成功!!!

总结

至此,我们公众号的简单消息处理已经完成了,下一篇博文将获取access_token,来调用微信提供的其他接口.

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值