微信公众号java开发沉淀(三)接收返回消息


本节先记录一下原理,代码写的诸多不妥帖。开源项目fastweixin 有封装的很不错的方法。可以直接用。

之前接入开发的时候,配置的那个URL,就是接收公众号消息的接口。但是接入的时候,发送的是GET请求。而接收消息的时候发送的是POST请求,所以写一个Controller接口,配置method 一个GET一个POST区分开。

下面是一个接收普通文本消息,返回文本消息的简单demo

消息体是明文模式。无论收到什么消息,都返回”接收成功“的文本消息。

/**
  * 接入开发走这个GET
  */
@RequestMapping(value = "/joinDev", method = RequestMethod.GET)
public Object delMsg(HttpServletRequest request) {
    // 微信加密签名
    String signature = request.getParameter("signature");
    // 时间戳
    String timestamp = request.getParameter("timestamp");
    // 随机数
    String nonce = request.getParameter("nonce");
    // 随机字符串
    String echostr = request.getParameter("echostr");
    //如果验证消息是来自微信,返回echostr
    boolean check = checkSignature(signature, timestamp, nonce);
    if (check) {
        return echostr;
    }
    return null;
}

/**
  * 配置RequestMethod.POST,用于接收处理消息
  */
@RequestMapping(value = "/joinDev", method = RequestMethod.POST)
public Object delMsg(HttpServletRequest request, @RequestBody InMsgEntity msg) {
    return delMsg(msg);
}

private OutMsgEntity delMsg(InMsgEntity msg) {
    log.info("接收消息为:{}", msg);
    //创建消息响应对象
    OutMsgEntity out = new OutMsgEntity();
    //把原来的发送方设置为接收方
    out.setToUserName(msg.getFromUserName());
    //把原来的接收方设置为发送方
    out.setFromUserName(msg.getToUserName());
    //设置消息的响应类型
    out.setMsgType("text");
    //设置消息创建时间
    out.setCreateTime(new Date().getTime());
    out.setContent("接收成功");
    return out;
}

如下

在这里插入图片描述

普通消息

通过微信中转过来的消息都是xml格式的。例如文本消息的格式为:官网文档 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140453

<?xml version="1.0" encoding="utf-8"?>
<xml> 
  <ToUserName>toUser</ToUserName>  
  <FromUserName>fromUser</FromUserName>  
  <CreateTime>1348831860</CreateTime>  
  <MsgType>text</MsgType>  
  <Content>this is a test</Content>  
  <MsgId>1234567890123456</MsgId> 
</xml>

我直接用了xml解析的注解,所以接收的xml可以直接转为对象。返回的对象也会转为xml。不建议这么做,对于不同类型的消息不具备通用性。可以使用后面介绍的fastweixin. 消息的解析,加解密都已经做好了。直接使用即可。

@Data
@ToString
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class InMsgEntity {
    // 开发者微信号
    protected String FromUserName;

    // 发送方帐号(一个OpenID)
    protected String ToUserName;

    // 消息创建时间
    protected Long CreateTime;

    // 消息类型 text 文本消息 image 图片消息 voice 语音消息 video 视频消息 event 事件消息
    protected String MsgType;

    // 消息id
    protected Long MsgId;

    // 文本内容
    private String Content;

    // 图片链接(由系统生成)
    private String PicUrl;

    // 图片消息媒体id,可以调用多媒体文件下载接口拉取数据
    private String MediaId;

    /**
     * 事件类型
     * 自定义菜单事件 CLICK 拉取消息 VIEW 跳转链接
     * 关注/取消关注事件 subscribe 订阅 unsubscribe 取关
     * 扫描带参数二维码事件  用户扫描带场景值二维码时,可能推送以下两种事件:
     *                           如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。
     *                           如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。
     * 上报地理位置事件 LOCATION 上报位置
     */
    private String Event;

    // 事件KEY值
    private String EventKey;
}

普通消息中的图片消息、语音消息、视频消息等都是通过MsgType 来区分的。text 文本消息 image 图片消息 voice 语音消息 video 视频消息 。

事件消息

当MsgType 是 event 的时候表示事件消息。事件消息报文中会一个节点Event 表明事件的类型。例如下面 Event 节点的 subscribe 表示这是个订阅消息。其他还有unsubscribe(取消订阅) ,SCAN 扫描带参数二维码事件,CLICK 自定义菜单事件等等。

<xml>
    <ToUserName>< ![CDATA[toUser] ]></ToUserName>
    <FromUserName>< ![CDATA[FromUser] ]></FromUserName>
    <CreateTime>123456789</CreateTime>
    <MsgType>< ![CDATA[event] ]></MsgType>
    <Event>< ![CDATA[subscribe] ]></Event>
</xml>

接收到事件消息根据事件类型和需求处理就行了。如下,当然这么写不好。我只是说明一下原理就是这么搞得。fastweixin 项目中已经封装的很好。可以直接引入jar包用。

@RequestMapping(value = "/joinDev", method = RequestMethod.POST)
    public Object delMsg(HttpServletRequest request) {
        Map<String, String> requestMap = delMsgFromRequest(request); // 从流中读取消息体
        // 发送方帐号(open_id)
        String fromUserName = requestMap.get("FromUserName");
        // 公众帐号
        String toUserName = requestMap.get("ToUserName");
        // 消息类型
        String msgType = requestMap.get("MsgType");
        if (MESSAGE_TYPE_TEXT.equals(msgType)) {
            return delTextMsg(requestMap, fromUserName, toUserName);
        } else if (MESSAGE_TYPE_EVENT.equals(msgType)) {
            Object textMessage = delEventMst(requestMap, fromUserName, toUserName);
            if (textMessage != null) return textMessage;
        }
        return errMsg(fromUserName, toUserName);
    }

    private Object errMsg(String fromUserName, String toUserName) {
        TextMessage textMessage = new TextMessage();
        textMessage.setToUserName(fromUserName);
        textMessage.setFromUserName(toUserName);
        textMessage.setCreateTime(new Date().getTime());
        textMessage.setMsgType(MESSAGE_TYPE_TEXT);
        textMessage.setContent("消息有误");
        return textMessage;
    }

    private Object delEventMst(Map<String, String> requestMap, String fromUserName, String toUserName) {
        // 事件类型
        String eventType = requestMap.get("Event");
        // 订阅
        if (EVENT_TYPE_SUBSCRIBE.equals(eventType)) {
            String respContent = "谢谢您的关注!";
            // 回复关注消息
            TextMessage textMessage = new TextMessage();
            textMessage.setToUserName(fromUserName);
            textMessage.setFromUserName(toUserName);
            textMessage.setCreateTime(new Date().getTime());
            textMessage.setMsgType(MESSAGE_TYPE_TEXT);
            textMessage.setContent(respContent);
            return textMessage;
        }
        // 自定义菜单点击事件
        else if (EVENT_TYPE_CLICK.equals(eventType)) {
            // 事件类型
            String eventKey = requestMap.get("EventKey");
            if ("USER_INFO_CLICK".equals(eventKey)) { // USER_INFO_CLICK
                ResImageMessage imageMessage = new ResImageMessage();
                imageMessage.setMediaId(new String[]{"PSUYMe5hdzqT-JrvKg-FEId9iGBj-648kuMLqDQPGr1ffA0yCCF4HxlmFuzrIfAL"});
                imageMessage.setCreateTime(new Date().getTime());
                imageMessage.setFromUserName(toUserName);
                imageMessage.setToUserName(fromUserName);
                imageMessage.setMsgType("image");
                return imageMessage;
            } else if ("V1001_GOOD".equals(eventKey)) {
				// ... 
            }
        }
        return null;
    }

private Map<String, String> delMsgFromRequest(HttpServletRequest request) {
        // 将解析结果存储在HashMap中
        Map<String, String> map = new HashMap<>();
        // 从request中取得输入流
        InputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            // 读取输入流
            SAXReader reader = new SAXReader();
            Document document = reader.read(inputStream);
            // 得到xml根元素
            Element root = document.getRootElement();
            // 得到根元素的所有子节点
            List<Element> elementList = root.elements();
            // 遍历所有子节点
            for (Element e : elementList) {
                map.put(e.getName(), e.getText());
            }
        } catch (IOException | DocumentException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return map;
    }

fastweixin

https://gitee.com/pyinjava/fastweixin/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值