之前接入开发的时候,配置的那个URL,就是接收公众号消息的接口。但是接入的时候,发送的是GET请求。而接收消息的时候发送的是POST请求,所以写一个Controller接口,配置method 一个GET一个POST区分开。
消息体是明文模式。无论收到什么消息,都返回”接收成功“的文本消息。
/** * 接入开发走这个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; }https://gitee.com/pyinjava/fastweixin/