與微信對接的url要具有如下條件:html
(1)在公網上可以訪問java
(2)端口只支持80端口git
在這裏若是是公網可以訪問的服務最好,也能夠經過花生殼或者其餘外網映射工具進行映射,好比ngrok。github
開發模式與編輯模式是互斥的,打開開發模式的時候,編輯模式的自動回覆與自定義菜單失效;打開編輯模式的自動回覆或者自定義菜單的時候開發模式會失效。web
開發模式的數據交互原理以下:面試
咱們須要開發的任務就是維信公衆號服務器,包括業務邏輯、身份驗證等操做。spring
參考公衆號開發文檔: 開發->開發者工具-》開發者文檔,裏面有相似於對接釘釘的文檔,有接入指南以及其餘接口文檔。數據庫
https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.htmlapache
到公衆號後臺: https://mp.weixin.qq.com/springboot
開發者提交信息後,微信服務器將發送GET請求到填寫的服務器地址URL上,GET請求攜帶參數以下表所示:
開發者經過檢驗signature對請求進行校驗(下面有校驗方式)。若確認這次GET請求來自微信服務器,請原樣返回echostr參數內容,則接入生效,成爲開發者成功,不然接入失敗。加密/校驗流程以下:
1)將token、timestamp、nonce三個參數進行字典序排序 2)將三個參數字符串拼接成一個字符串進行sha1加密 3)開發者得到加密後的字符串可與signature對比,標識該請求來源於微信
SpringMVC接收代碼以下:
package cn.qlq.controller.weixin; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import cn.qlq.controller.UserController; import cn.qlq.utils.weixin.WeixinCheckUtils; @Controller @RequestMapping("weixin") public class WeixinController { private static final Logger logger = LoggerFactory.getLogger(UserController.class); @ResponseBody @RequestMapping("index") public String index(@RequestParam(required = false) String signature, @RequestParam(required = false) String timestamp, @RequestParam(required = false) String nonce, @RequestParam(required = false) String echostr) { logger.debug("signature: {}, timestamp: {}, nonce: {}, echostr: {}", signature, timestamp, nonce, echostr); if (StringUtils.isNoneBlank(signature, timestamp, nonce) && WeixinCheckUtils.checkSignature(signature, timestamp, nonce)) { return echostr; } return "error"; } }
驗證工具以下:
package cn.qlq.utils.weixin; import java.security.MessageDigest; import java.util.Arrays; public class WeixinCheckUtils { // token,與微信公衆號後臺的一致 private static final String token = "devqiaolq"; public static boolean checkSignature(String signature, String timestamp, String nonce) { String[] arr = new String[] { token, timestamp, nonce }; // 排序 Arrays.sort(arr); // 生成字符串 StringBuffer content = new StringBuffer(); for (int i = 0; i < arr.length; i++) { content.append(arr[i]); } // sha1加密 String temp = getSha1(content.toString()); return temp.equals(signature); } public static String getSha1(String str) { if (str == null || str.length() == 0) { return null; } char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; try { MessageDigest mdTemp = MessageDigest.getInstance("SHA1"); mdTemp.update(str.getBytes("UTF-8")); byte[] md = mdTemp.digest(); int j = md.length; char buf[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; buf[k++] = hexDigits[byte0 >>> 4 & 0xf]; buf[k++] = hexDigits[byte0 & 0xf]; } return new String(buf); } catch (Exception e) { return null; } } }
注意:若是有登陸過濾器,記得在過濾器中放行微信請求.
當普通微信用戶向公衆帳號發消息時,微信服務器將POST消息的XML數據包到開發者填寫的URL上。
文本消息的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>
參數解釋:
參數 | 描述 |
---|---|
ToUserName | 開發者微信號 |
FromUserName | 發送方賬號(一個OpenID) |
CreateTime | 消息建立時間 (整型) |
MsgType | 消息類型,文本爲text |
Content | 文本消息內容 |
MsgId | 消息id,64位整型 |
(1)創建後臺對應的TextMessage實體類:
package cn.qlq.bean.weixin; public class TextMessage { /** * 開發者微信號 */ private String ToUserName; /** * 發送方賬號(一個OpenID) */ private String FromUserName; /** * 消息建立時間 (整型) */ private long CreateTime; /** * text */ private String MsgType; /** * 文本消息內容 */ private String Content; /** * 消息id,64位整型 */ private String MsgId; @Override public String toString() { return "TextMessage{" + "ToUserName='" + ToUserName + '\'' + ", FromUserName='" + FromUserName + '\'' + ", CreateTime=" + CreateTime + ", MsgType='" + MsgType + '\'' + ", Content='" + Content + '\'' + ", MsgId='" + MsgId + '\'' + '}'; } 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; } public String getContent() { return Content; } public void setContent(String content) { Content = content; } public String getMsgId() { return MsgId; } public void setMsgId(String msgId) { MsgId = msgId; } }
(2)編寫工具類實現xml(接收的是xml格式的數據)轉map和TextMessage對象轉換成xml(響應數據格式爲xml)
pom加入:
<!--微信轉換XML所需包 --> <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 cn.qlq.utils.weixin; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import com.thoughtworks.xstream.XStream; import cn.qlq.bean.weixin.TextMessage; public class MessageUtils { /** * xml數據轉map * * @param request * @return * @throws IOException * @throws DocumentException */ public static Map<String, String> xmlToMap(HttpServletRequest request) throws IOException, DocumentException { Map<String, String> map = new HashMap<>(); SAXReader reader = new SAXReader(); InputStream inputStream = request.getInputStream(); Document document = reader.read(inputStream); Element root = document.getRootElement(); List<Element> list = root.elements(); for (Element element : list) { map.put(element.getName(), element.getText()); } inputStream.close(); return map; } /** * 將文本消息對象轉換成xml * * @param textMessage * @return */ public static String textMessageToXml(TextMessage textMessage) { XStream xStream = new XStream(); // 將xml的根元素替換成xml xStream.alias("xml", textMessage.getClass()); return xStream.toXML(textMessage); } }
(3)重寫Controller接收消息和響應消息
package cn.qlq.controller.weixin; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.dom4j.DocumentException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import cn.qlq.bean.weixin.TextMessage; import cn.qlq.controller.UserController; import cn.qlq.utils.weixin.MessageUtils; import cn.qlq.utils.weixin.WeixinCheckUtils; @Controller @RequestMapping("weixin") public class WeixinController { private static final Logger logger = LoggerFactory.getLogger(UserController.class); @RequestMapping(value = "index", method = { RequestMethod.GET, RequestMethod.POST }) public void index(HttpServletRequest request, HttpServletResponse response) throws IOException { // 將請求、響應的編碼均設置爲UTF-8(防止中文亂碼) request.setCharacterEncoding("UTF-8"); // 微信服務器POST消息時用的是UTF-8編碼,在接收時也要用一樣的編碼,不然中文會亂碼; response.setCharacterEncoding("UTF-8"); // 在響應消息(回覆消息給用戶)時,也將編碼方式設置爲UTF-8,原理同上; String method = request.getMethod().toLowerCase(); logger.info("method: {}", method); // 驗證是不是微信請求 if ("get".equals(method)) { doGet(request, response); return; } // POST請求接收消息,且給客戶響應消息 doPost(request, response); } /** * Post請求用於接收消息且處理消息以後回傳消息 * * @param request * @param response * @throws IOException */ private void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { PrintWriter out = response.getWriter(); try { Map<String, String> map = MessageUtils.xmlToMap(request); String fromUserName = map.get("FromUserName"); String toUserName = map.get("ToUserName"); String msgType = map.get("MsgType"); String content = map.get("Content"); logger.info("map: {}", map); if (StringUtils.isNotBlank(content)) { System.out.println("接收的的消息爲:" + content + ",你能夠根據關鍵字進行搜索或者作其餘"); } String message = null; if ("text".equals(msgType)) { TextMessage textMessage = new TextMessage(); // 回傳消息,因此講fromuser和toUser交換 textMessage.setFromUserName(toUserName); textMessage.setToUserName(fromUserName); textMessage.setMsgType(msgType); textMessage.setCreateTime(new Date().getTime()); textMessage.setContent("您發送的消息爲: " + content); logger.info("textMessage: {}", textMessage); message = MessageUtils.textMessageToXml(textMessage); } out.print(message);// 把消息發送到客戶端 } catch (DocumentException e) { logger.error("dispose post request error", e); } finally { out.close(); } } /** * Get請求用於微信配置驗證 * * @param request * @param response * @throws IOException */ private void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { String signature = request.getParameter("signature");// 微信加密簽名 String timestamp = request.getParameter("timestamp");// 時間戳 String nonce = request.getParameter("nonce");// 隨機數 String echostr = request.getParameter("echostr");// 隨機字符串 logger.info("signature: {}, timestamp: {}, nonce: {}, echostr: {}", signature, timestamp, nonce, echostr); if (StringUtils.isNoneBlank(signature, timestamp, nonce) && WeixinCheckUtils.checkSignature(signature, timestamp, nonce)) { response.getWriter().write(echostr); } } }
測試:
Java服務器日誌以下:
2019-10-23 23:34:15.625 INFO 244500 --- [nio-8088-exec-9] cn.qlq.controller.UserController : method: post
2019-10-23 23:34:15.633 INFO 244500 --- [nio-8088-exec-9] cn.qlq.controller.UserController : map: {MsgId=22503405793257008, FromUserName=o_qAo0u6Snhoc7Z45RfSxYatMWpo, CreateTime=1571844638, Content=怎麼了, ToUserName=gh_fc4bd5c2fda8, MsgType=text}
接收的的消息爲:怎麼了,你能夠根據關鍵字進行搜索或者作其餘
2019-10-23 23:34:15.635 INFO 244500 --- [nio-8088-exec-9] cn.qlq.controller.UserController : textMessage: TextMessage{ToUserName='o_qAo0u6Snhoc7Z45RfSxYatMWpo', FromUserName='gh_fc4bd5c2fda8', CreateTime=1571844855635, MsgType='text', Content='您發送的消息爲: 怎麼了', MsgId='null'}
2019-10-23 23:34:20.288 INFO 244500 --- [nio-8088-exec-3] cn.qlq.controller.UserController : method: post
2019-10-23 23:34:20.295 INFO 244500 --- [nio-8088-exec-3] cn.qlq.controller.UserController : map: {MsgId=22503406289173072, FromUserName=o_qAo0u6Snhoc7Z45RfSxYatMWpo, CreateTime=1571844642, Content=什麼意思, ToUserName=gh_fc4bd5c2fda8, MsgType=text}
接收的的消息爲:什麼意思,你能夠根據關鍵字進行搜索或者作其餘
2019-10-23 23:34:20.296 INFO 244500 --- [nio-8088-exec-3] cn.qlq.controller.UserController : textMessage: TextMessage{ToUserName='o_qAo0u6Snhoc7Z45RfSxYatMWpo', FromUserName='gh_fc4bd5c2fda8', CreateTime=1571844860296, MsgType='text', Content='您發送的消息爲: 什麼意思', MsgId='null'}
公衆號能夠接受的消息大概有文本消息(在上面已經接收)、圖片(image)消息、語音(voice,在後臺接口開啓語音識別能夠自動識別文字)消息、視頻(video)消息、短視頻(shortvideo)消息、地理位置(location)消息、連接(link)消息、事件(event,事件類型:subscribe(訂閱)、unsubscribe(取消訂閱))消息。
公衆號能夠回覆的消息有: 文本(text)消息、圖片消息(image)、語音消息(voice)、視頻消息(video)、音樂消息(music)、圖文(news)消息。
注意:這裏須要注意接收的消息和發送的消息數據數據格式不同。以下是回覆的圖片消息和圖文消息的數據格式:
回傳圖片消息數據格式:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>12345678</CreateTime> <MsgType><![CDATA[image]]></MsgType> <Image> <MediaId><![CDATA[media_id]]></MediaId> </Image> </xml>
回傳圖文消息數據格式:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>12345678</CreateTime> <MsgType><![CDATA[news]]></MsgType> <ArticleCount>1</ArticleCount> <Articles> <item> <Title><![CDATA[title1]]></Title> <Description><![CDATA[description1]]></Description> <PicUrl><![CDATA[picurl]]></PicUrl> <Url><![CDATA[url]]></Url> </item> </Articles> </xml>
本身封裝的controller接收數據和消息處理的工具類以下:
package cn.qlq.controller.weixin; import java.io.IOException; import java.io.PrintWriter; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.dom4j.DocumentException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import cn.qlq.bean.weixin.response.AbstractResponseMessage; import cn.qlq.controller.UserController; import cn.qlq.utils.weixin.MessageHandler; import cn.qlq.utils.weixin.MessageUtils; import cn.qlq.utils.weixin.WeixinCheckUtils; @Controller @RequestMapping("weixin") public class WeixinController { private static final Logger logger = LoggerFactory.getLogger(UserController.class); @RequestMapping(value = "index", method = { RequestMethod.GET, RequestMethod.POST }) public void index(HttpServletRequest request, HttpServletResponse response) throws IOException { // 將請求、響應的編碼均設置爲UTF-8(防止中文亂碼) request.setCharacterEncoding("UTF-8"); // 微信服務器POST消息時用的是UTF-8編碼,在接收時也要用一樣的編碼,不然中文會亂碼; response.setCharacterEncoding("UTF-8"); // 在響應消息(回覆消息給用戶)時,也將編碼方式設置爲UTF-8,原理同上; String method = request.getMethod().toLowerCase(); logger.info("method: {}", method); // 驗證是不是微信請求 if ("get".equals(method)) { doGet(request, response); return; } // POST請求接收消息,且給客戶響應消息 doPost(request, response); } /** * Post請求用於接收消息且處理消息以後回傳消息 * * @param request * @param response * @throws IOException */ private void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { PrintWriter out = response.getWriter(); try { // 消息轉map Map<String, Object> map = MessageUtils.xmlToMap(request); logger.info("接收到的消息map: {}", map); // 調用工具類處理完以後顯示回傳消息 AbstractResponseMessage responseMessage = MessageHandler.handlMessage(map); logger.info("回傳的消息responseMessage: {}", responseMessage); if (responseMessage == null) { return; } String messageToXml = MessageUtils.messageToXml(responseMessage); logger.info("回傳的消息responseMessage messageToXml: {}", messageToXml); out.print(messageToXml);// 把消息發送到客戶端 } catch (DocumentException e) { logger.error("dispose post request error", e); } finally { out.close(); } } /** * Get請求用於微信配置驗證 * * @param request * @param response * @throws IOException */ private void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { String signature = request.getParameter("signature");// 微信加密簽名 String timestamp = request.getParameter("timestamp");// 時間戳 String nonce = request.getParameter("nonce");// 隨機數 String echostr = request.getParameter("echostr");// 隨機字符串 logger.info("signature: {}, timestamp: {}, nonce: {}, echostr: {}", signature, timestamp, nonce, echostr); if (StringUtils.isNoneBlank(signature, timestamp, nonce) && WeixinCheckUtils.checkSignature(signature, timestamp, nonce)) { response.getWriter().write(echostr); } } }
package cn.qlq.utils.weixin; import java.util.Date; import java.util.Map; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import cn.qlq.bean.weixin.EventMessage; import cn.qlq.bean.weixin.ImageMessage; import cn.qlq.bean.weixin.LinkMessage; import cn.qlq.bean.weixin.LocationMessage; import cn.qlq.bean.weixin.TextMessage; import cn.qlq.bean.weixin.VideoMessage; import cn.qlq.bean.weixin.VoiceMessage; import cn.qlq.bean.weixin.response.AbstractResponseMessage; import cn.qlq.bean.weixin.response.ImageResponseMessage; import cn.qlq.bean.weixin.response.NewsResponseMessage; import cn.qlq.bean.weixin.response.NewsResponseMessageArticle; import cn.qlq.bean.weixin.response.NewsResponseMessageArticleItem; import cn.qlq.bean.weixin.response.TextResponseMessage; import cn.qlq.utils.BeanUtils; public class MessageHandler { public static final String MESSAGE_TEXT = "text"; public static final String MESSAGE_IMAGE = "image"; public static final String MESSAGE_VOICE = "voice"; public static final String MESSAGE_VIDEO = "video"; public static final String MESSAGE_LINK = "link"; public static final String MESSAGE_LOCATION = "location"; public static final String MESSAGE_EVENT = "event";// 事件 public static final String MESSAGE_SUBSCRIBE = "subscribe";// 關注 public static final String MESSAGE_UNSUBSCRIBE = "unsubscribe";// 取消關注 public static final String MESSAGE_CLICK = "CLICK"; public static final String MESSAGE_VIEW = "VIEW"; private static final Logger logger = LoggerFactory.getLogger(MessageHandler.class); public static AbstractResponseMessage handlMessage(Map<String, Object> messageMap) { if (MapUtils.isEmpty(messageMap)) { logger.error("message is empty"); return null; } String msgType = (String) messageMap.get("MsgType"); if (MESSAGE_TEXT.equals(msgType)) { return handleTextMessage(messageMap); } else if (MESSAGE_IMAGE.equals(msgType)) { return handleImageMessage(messageMap); } else if (MESSAGE_LOCATION.equals(msgType)) { return handleLocationMessage(messageMap); } else if (MESSAGE_EVENT.equals(msgType)) { return handleEventMessage(messageMap); } else if (MESSAGE_LINK.equals(msgType)) { return handleLinkMessage(messageMap); } else if (MESSAGE_VOICE.equals(msgType)) { return handleVoiceMessage(messageMap); } else if (MESSAGE_VIDEO.equals(msgType)) { return handleVideoMessage(messageMap); } return null; } private static AbstractResponseMessage handleVideoMessage(Map<String, Object> messageMap) { VideoMessage message = BeanUtils.map2Bean(messageMap, VideoMessage.class, true); String thumbMediaId = message.getThumbMediaId(); // 能夠用圖片路徑作其餘操做 if (StringUtils.isNotBlank(thumbMediaId)) { System.out.println("您接收到視頻消息, thumbMediaId爲: " + thumbMediaId); } String responseMsg = "您發送了一條視頻消息,thumbMediaId爲: " + thumbMediaId; return MessageUtils.initTextMessage(message.getToUserName(), message.getFromUserName(), responseMsg); } private static AbstractResponseMessage handleVoiceMessage(Map<String, Object> messageMap) { VoiceMessage message = BeanUtils.map2Bean(messageMap, VoiceMessage.class, true); String recognition = message.getRecognition(); String format = message.getFormat(); // 能夠用圖片路徑作其餘操做 if (StringUtils.isNotBlank(recognition)) { System.out.println("您接收到語音消息, 格式爲: " + format + ", 轉換後的文字爲: " + recognition); } String responseMsg = "您發送了一條語音消息,格式爲: " + format + ", 轉換後的文字爲: " + recognition; return MessageUtils.initTextMessage(message.getToUserName(), message.getFromUserName(), responseMsg); } /** * 處理連接消息(回覆一條圖文消息) * * @param message * @return */ private static AbstractResponseMessage handleLinkMessage(Map<String, Object> messageMap) { LinkMessage message = BeanUtils.map2Bean(messageMap, LinkMessage.class, true); String desc = message.getDescription(); String title = message.getTitle(); String url = message.getUrl(); // 能夠用圖片路徑作其餘操做 if (StringUtils.isNotBlank(url)) { System.out.println("您接收到連接消息, title: " + title + ", desc: " + desc + ", url: " + url); } // 回覆一條圖文消息 NewsResponseMessage news = new NewsResponseMessage(); news.setCreateTime(System.currentTimeMillis()); news.setFromUserName(message.getToUserName()); news.setToUserName(message.getFromUserName()); news.setArticleCount("1"); news.setMsgType("news"); NewsResponseMessageArticle article = new NewsResponseMessageArticle(); news.setArticles(article); // 建立多條圖文消息 for (int i = 0; i < 1; i++) { NewsResponseMessageArticleItem item = new NewsResponseMessageArticleItem(); item.setTitle("18年寫的面試心得"); item.setPicUrl("https://images.cnblogs.com/cnblogs_com/qlqwjy/1031659/o_9.bmp"); item.setUrl("https://www.cnblogs.com/qlqwjy/p/9194434.html"); item.setDescription("18年畢設心血來潮寫的畢設心得,1年後再看有點東西。"); article.addNewsResponseMessageArticleItem(item); } return news; } /** * 處理事件消息(訂閱和取消訂閱) * * @param messageMap * @return */ private static AbstractResponseMessage handleEventMessage(Map<String, Object> messageMap) { EventMessage message = BeanUtils.map2Bean(messageMap, EventMessage.class, true); String event = message.getEvent(); if (StringUtils.isNotBlank(event)) { System.out.println("您接收到事件消息, 事件類型爲: " + event); } if (MESSAGE_SUBSCRIBE.equals(event)) { // 關注的時候 System.out.println("這裏能夠向數據庫插入數據"); String responseMsg = MessageUtils.subscribeWelcomeText(); return MessageUtils.initTextMessage(message.getToUserName(), message.getFromUserName(), responseMsg); } else { // 取消關注(不用回傳消息.須要將用戶產生的數據刪除) System.out.println("這時須要從數據刪除 " + message.getFromUserName() + " 用戶產生的相關數據"); return null; } } private static AbstractResponseMessage handleLocationMessage(Map<String, Object> messageMap) { LocationMessage message = BeanUtils.map2Bean(messageMap, LocationMessage.class, true); String label = message.getLabel(); if (StringUtils.isNotBlank(label)) { System.out.println("您接收到位置消息, 地理位置信息爲: " + message); } String responseMsg = "您發送了一條位置消息, 您的地理位置信息爲:" + label; return MessageUtils.initTextMessage(message.getToUserName(), message.getFromUserName(), responseMsg); } /** * 處理圖片消息(回覆一條圖片消息) * * @param message * @return */ private static AbstractResponseMessage handleImageMessage(Map<String, Object> message) { ImageMessage imageMessage = BeanUtils.map2Bean(message, ImageMessage.class, true); String url = imageMessage.getPicUrl(); // 能夠用圖片路徑作其餘操做 if (StringUtils.isNotBlank(url)) { System.out.println("您接收到的圖片消息url爲: " + url); } // 回傳一條圖片消息 ImageResponseMessage responseMessage = new ImageResponseMessage(); responseMessage.setCreateTime(System.currentTimeMillis()); responseMessage.setFromUserName(imageMessage.getToUserName()); responseMessage.setToUserName(imageMessage.getFromUserName()); responseMessage.setMediaId(imageMessage.getMediaId()); responseMessage.setMsgType(MESSAGE_IMAGE); return responseMessage; } /** * 處理文本消息 * * @param message * @return */ private static AbstractResponseMessage handleTextMessage(Map<String, Object> message) { TextMessage textMessage = BeanUtils.map2Bean(message, TextMessage.class, true); String content = textMessage.getContent(); // 能夠根據文本消息去查庫或者進行其餘操做 if (StringUtils.isNotBlank(content)) { System.out.println("您接收到的文本消息內容爲: " + content); } // 設置回傳的消息內容 TextResponseMessage responseMessage = new TextResponseMessage(); responseMessage.setContent("服務器已接收到您的消息,內容爲: " + content); responseMessage.setCreateTime(new Date().getTime()); responseMessage.setFromUserName(textMessage.getToUserName()); responseMessage.setToUserName(textMessage.getFromUserName()); responseMessage.setMsgType(MESSAGE_TEXT); return responseMessage; } }
package cn.qlq.utils.weixin; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import com.thoughtworks.xstream.XStream; import cn.qlq.bean.weixin.response.AbstractResponseMessage; import cn.qlq.bean.weixin.response.TextResponseMessage; public class MessageUtils { /** * xml數據轉map * * @param request * @return * @throws IOException * @throws DocumentException */ public static Map<String, Object> xmlToMap(HttpServletRequest request) throws IOException, DocumentException { Map<String, Object> map = new HashMap<>(); SAXReader reader = new SAXReader(); InputStream inputStream = request.getInputStream(); Document document = reader.read(inputStream); Element root = document.getRootElement(); List<Element> list = root.elements(); for (Element element : list) { map.put(element.getName(), element.getText()); } inputStream.close(); return map; } /** * 將文本消息對象轉換成xml * * @param message * @return */ public static String messageToXml(AbstractResponseMessage message) { XStream xStream = new XStream(); // 將xml的根元素替換成xml xStream.alias("xml", message.getClass()); xStream.alias("item", cn.qlq.bean.weixin.response.NewsResponseMessageArticleItem.class); xStream.addImplicitArray(cn.qlq.bean.weixin.response.NewsResponseMessageArticle.class, "items"); return xStream.toXML(message); } /** * 將文本消息對象轉換成xml * * @param message * @return */ public static String messageToXml2(AbstractResponseMessage message) { XStream xStream = new XStream(); // 將xml的根元素替換成xml xStream.alias("xml", message.getClass()); return xStream.toXML(message); } /** * 訂閱後的歡迎信息 * * @return */ public static String subscribeWelcomeText() { StringBuffer sb = new StringBuffer(); sb.append("歡迎您的關注,這裏是喬治我的平臺:\n\n"); sb.append("1.推薦一些優秀的文章\n");// \n表明換行 sb.append("2.記錄一些美好時刻\n\n"); return sb.toString(); } /** * 生成文本消息 * * @param fromUserName * @param toUserName * @param content * @return */ public static TextResponseMessage initTextMessage(String fromUserName, String toUserName, String content) { TextResponseMessage textResponseMessage = new TextResponseMessage(); textResponseMessage.setFromUserName(fromUserName); textResponseMessage.setToUserName(toUserName); textResponseMessage.setMsgType(MessageHandler.MESSAGE_TEXT); textResponseMessage.setCreateTime(System.currentTimeMillis()); textResponseMessage.setContent(content); return textResponseMessage; } }
結果以下:
1.首先在接口設置開啓經緯度事件(開啓以後在用戶進入公衆號以後會主動拉取用戶的位置信息)
用戶贊成上報地理位置後,每次進入公衆號會話時,都會在進入時上報地理位置,或在進入會話後每5秒上報一次地理位置,公衆號能夠在公衆平臺網站中修改以上設置。上報地理位置時,微信會將上報地理位置事件推送到開發者填寫的URL。
推送的XML數據包格式以下:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>123456789</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[LOCATION]]></Event> <Latitude>23.137466</Latitude> <Longitude>113.352425</Longitude> <Precision>119.385040</Precision> </xml>
java處理:
封裝工具類:
package cn.qlq.bean.weixin; /** * 上報地理位置事件 * * @author Administrator * */ public class LocationEventMessage extends EventMessage { /** * 地理位置緯度 */ private String Latitude; /** * 地理位置經度 */ private String Longitude; /** * 地理位置精度 */ private String Precision; public String getLatitude() { return Latitude; } public void setLatitude(String latitude) { Latitude = latitude; } public String getLongitude() { return Longitude; } public void setLongitude(String longitude) { Longitude = longitude; } public String getPrecision() { return Precision; } public void setPrecision(String precision) { Precision = precision; } @Override public String toString() { return "LocationEventMessage [Latitude=" + Latitude + ", Longitude=" + Longitude + ", Precision=" + Precision + ", ToUserName=" + ToUserName + ", FromUserName=" + FromUserName + ", CreateTime=" + CreateTime + ", MsgType=" + MsgType + ", Event=" + Event + "]"; } }
修改處理事件消息的代碼:(CLICK和VIEW是用於處理自定義菜單事件)
/** * 處理事件消息(訂閱和取消訂閱) * * @param messageMap * @return */ private static AbstractResponseMessage handleEventMessage(Map<String, Object> messageMap) { EventMessage message = BeanUtils.map2Bean(messageMap, EventMessage.class, true); String event = message.getEvent(); if (StringUtils.isNotBlank(event)) { System.out.println("您接收到事件消息, 事件類型爲: " + event); } // 關注的時候 if (MESSAGE_SUBSCRIBE.equals(event)) { System.out.println("這裏能夠向數據庫插入數據"); String responseMsg = MessageUtils.subscribeWelcomeText(); return MessageUtils.initTextMessage(message.getToUserName(), message.getFromUserName(), responseMsg); } // 取消關注(不用回傳消息.須要將用戶產生的數據刪除) if (MESSAGE_SUBSCRIBE.equals(event)) { System.out.println("這時須要從數據刪除 " + message.getFromUserName() + " 用戶產生的相關數據"); return null; } // 點擊自定義的點擊菜單事件 if (MESSAGE_EVENT_CLICK.equals(event)) { ClickViewEventMessage map2Bean = BeanUtils.map2Bean(messageMap, ClickViewEventMessage.class, true); String eventKey = map2Bean.getEventKey(); String content = "您點擊的按鈕的key爲: " + eventKey; return MessageUtils.initTextMessage(map2Bean.getToUserName(), map2Bean.getFromUserName(), content); } // VIEW菜單的事件 if (MESSAGE_EVENT_VIEW.equals(event)) { ClickViewEventMessage map2Bean = BeanUtils.map2Bean(messageMap, ClickViewEventMessage.class, true); String eventKey = map2Bean.getEventKey(); String content = "您點擊的按鈕跳轉的URL爲: " + eventKey; return MessageUtils.initTextMessage(map2Bean.getToUserName(), map2Bean.getFromUserName(), content); } // LOCATION菜單的事件 if (MESSAGE_EVENT_LOCATION.equals(event)) { LocationEventMessage map2Bean = BeanUtils.map2Bean(messageMap, LocationEventMessage.class, true); String latitude = map2Bean.getLatitude(); String longitude = map2Bean.getLongitude(); String precision = map2Bean.getPrecision(); String content = "您的經度:" + latitude + ", 您的維度:" + longitude + ", 您的精度: " + precision; return MessageUtils.initTextMessage(map2Bean.getToUserName(), map2Bean.getFromUserName(), content); } return null; }
測試結果:(須要開啓GPS才能夠觸發該事件消息發送)
(1)回傳xml數據格式的數據時,能夠用字符串進行拼接,不必定用XStream進行轉換
(2)微信回傳的消息支持換行,換行符爲\n
(3)圖文消息是最多見的消息,回傳消息的時候注意回傳的xml數據格式
完整的代碼接收消息與回傳消息代碼參考:https://github.com/qiao-zhi/springboot-ssm.git