springboot實現(xiàn)公眾號接收回復消息和超過5秒被動回復消息
本次就是記錄一下我的開發(fā)過程,不是教程,純屬自己做個筆記。
現(xiàn)在項目有個需求,需要用戶在公眾號發(fā)送圖片消息的時候,我后臺程序能接收到這個圖片,并用ai處理圖片并返回信息。
1.首先第一步要接收微信消息,需要在公眾號里設置與開發(fā)-基本配置里配置一下服務器配置
這個url配置了以后,所以微信公眾號的消息都會被推送到這個url對應的接口上,Token感覺沒啥用,隨便寫一個完事,加密隨機生成,不加密消息的話也沒用。
最坑爹的是在提交配置的時候,微信要根據你填的這個url驗證一下token認證,而這個url實際是后臺處理消息的接口,搞不清楚咋肥四,我就先把這個接口寫成驗證token的,等提交完配置再改回我的處理消息接口代碼。驗證token這里隨便找了段代碼,親測有效。
@RequestMapping(value = "/testToken") public void testToken(HttpServletRequest request, HttpServletResponse response) throws Exception { String token="tokenxxx"; logger.error("WechatController ---- WechatController"); System.out.println("========WechatController========= "); logger.info("請求進來了..."); Enumeration pNames = request.getParameterNames(); while (pNames.hasMoreElements()) { String name = (String) pNames.nextElement(); String value = request.getParameter(name); // out.print(name + "=" + value); String log = "name =" + name + " value =" + value; logger.error(log); } String signature = request.getParameter("signature");/// 微信加密簽名 String timestamp = request.getParameter("timestamp");/// 時間戳 String nonce = request.getParameter("nonce"); /// 隨機數 String echostr = request.getParameter("echostr"); // 隨機字符串 PrintWriter out = response.getWriter(); out.print(echostr); out.close(); }
2.配置好公眾號以后,開始接收微信消息
官方文檔在這里:文本消息 | 微信開放文檔
也就是說微信會給你發(fā)送xml格式的消息,后臺需要能接收這個消息
要接收xml消息和以后發(fā)送消息啥的,需要先引入一些依賴:
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.10.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.jaxrs</groupId> <artifactId>jackson-jaxrs-xml-provider</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--httpUtil--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.4.10</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.6</version> </dependency> <!--解析微信的消息--> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.4</version> </dependency>
以為對應的圖標消息是這樣的:
所以寫個消息的實體類:
package com.bomc.recordLife.entry; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @Data @NoArgsConstructor @AllArgsConstructor @JacksonXmlRootElement(localName = "xml") public class WxMessageImg { @JacksonXmlProperty(localName = "ToUserName") private String ToUserName; @JacksonXmlProperty(localName = "FromUserName") private String FromUserName; @JacksonXmlProperty(localName = "CreateTime") private long CreateTime; @JacksonXmlProperty(localName = "MsgType") private String MsgType; @JacksonXmlProperty(localName = "Event") private String Event; @JacksonXmlProperty(localName = "PicUrl") private String PicUrl; @JacksonXmlProperty(localName = "MediaId") private String MediaId; @JacksonXmlProperty(localName = "MsgId") private long MsgId; @JacksonXmlProperty(localName = "Content") private String Content; }
還是先記錄一下如果不怕超時直接給用戶返回消息的情況:
@PostMapping(value = "analyzeImg2",consumes = "text/xml", produces = "text/xml;charset=utf-8") @ResponseBody public Object analyzeImg2(@RequestBody WxMessageImg wxMessageImg){ //拼一下要返回的信息對象 WxMessageImg resultMessage=new WxMessageImg(); try { //忽略圖片邏輯,直接鬧個結果 String resultStr="處理完圖片返回的信息"; String openid = wxMessageImg.getFromUserName(); //用戶 openid String mpid = wxMessageImg.getToUserName(); //公眾號原始 ID resultMessage.setToUserName(openid); resultMessage.setFromUserName(mpid); resultMessage.setCreateTime(new Date().getTime()); resultMessage.setContent(resultStr); resultMessage.setMsgType("text"); //用這個工具類處理出一串玩意直接返回 String outMesStr = WxMessageUtil.textMessageToXml(resultMessage); System.out.println(outMesStr); return outMesStr; } catch (Exception e) { e.printStackTrace(); } return null; }
工具類:
package com.bomc.recordLife.util; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.bomc.recordLife.entry.WxMessageImg; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomDriver; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Description: 消息工具類 * @Author: lst * @Date 2020-08-19 */ public class WxMessageUtil { /** * 返回消息類型:文本 */ public static final String RESP_MESSAGE_TYPE_TEXT = "text"; /** * 返回消息類型:音樂 */ public static final String RESP_MESSAGE_TYPE_MUSIC = "music"; /** * 返回消息類型:圖文 */ public static final String RESP_MESSAGE_TYPE_NEWS = "news"; /** * 返回消息類型:圖片 */ 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 REQ_MESSAGE_TYPE_TEXT = "text"; /** * 請求消息類型:圖片 */ public static final String REQ_MESSAGE_TYPE_IMAGE = "image"; /** * 請求消息類型:鏈接 */ public static final String REQ_MESSAGE_TYPE_LINK = "link"; /** * 請求消息類型:地理位置 */ public static final String REQ_MESSAGE_TYPE_LOCATION = "location"; /** * 請求消息類型:音頻 */ 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_EVENT = "event"; /** * 事件類型:subscribe(訂閱) */ public static final String EVENT_TYPE_SUBSCRIBE = "subscribe"; /** * 事件類型:unsubscribe(取消訂閱) */ public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe"; /** * 事件類型:CLICK(自定義菜單點擊事件) */ public static final String EVENT_TYPE_CLICK = "CLICK"; /** * 事件類型:VIEW(自定義菜單URl視圖) */ public static final String EVENT_TYPE_VIEW = "VIEW"; /** * 事件類型:LOCATION(上報地理位置事件) */ public static final String EVENT_TYPE_LOCATION = "LOCATION"; /** * 事件類型:LOCATION(上報地理位置事件) */ public static final String EVENT_TYPE_SCAN = "SCAN"; /** * @Description: 解析微信發(fā)來的請求(XML) * @param @param request * @param @return * @param @throws Exception * @author dapengniao * @date 2016年3月7日 上午10:04:02 */ public static Map<String, String> parseXml(HttpServletRequest request) { // 將解析結果存儲在HashMap中 Map<String, String> map = new HashMap<String, String>(); // 讀取輸入流 SAXReader reader = new SAXReader(); Document document = null; InputStream inputStream = null; try { // 從request中取得輸入流 inputStream = request.getInputStream(); document = reader.read(inputStream); // 得到xml根元素 Element root = document.getRootElement(); // 得到根元素的所有子節(jié)點 List<Element> elementList = root.elements(); // 遍歷所有子節(jié)點 elementList.stream().forEach(element -> { map.put(element.getName(), element.getStringValue()); }); } catch (DocumentException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { // 釋放資源 if(null != inputStream){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return map; } /** * @Description: 文本消息對象轉換成xml * @param @param textMessage * @param @return * @author dapengniao * @date 2016年3月8日 下午4:13:22 */ public static String textMessageToXml(WxMessageImg textMessage) { XStream xStream = new XStream(new DomDriver("UTF-8")); //XStream xStream = new XStream(); xStream.alias("xml", textMessage.getClass()); return xStream.toXML(textMessage); } }
如果用postman調用的需要這樣:
以上就是接收消息和被動回復消息,但是有個大坑,一開始我想著處理完消息在直接返回信息回去,但是處理時間總是超過5秒,每次超過5秒它就報一下服務出現(xiàn)故障,一共請求三次,三次都超時就給你報三次故障。
后來客戶不愿意報這玩意兒,就只好改成直接返回success再異步調用處理圖片方法,處理完再用客服消息主動給用戶發(fā)消息。
因為要處理圖片花費的時間比較多,所以開個線程搞成異步調用處理圖片再推送消息,這樣的話直接返回字符串success
在controller里面寫個方法接收一下,用@RequestBody 直接把發(fā)來的xml變成對象
@RequestMapping(value = "analyzeImg") @ResponseBody public String analyzeImg(@RequestBody WxMessageImg wxMessageImg) throws Exception { //異步調用,先直接返回success,不然會顯示程序異常 new Thread(new Runnable() { public void run() { getImgData(wxMessageImg); } }).start(); return "SUCCESS"; //return null; }
public Object getImgData(WxMessageImg wxMessageImg){ try { //忽略圖片邏輯,直接鬧個結果 String resultStr="處理完圖片返回的信息"; String openid = wxMessageImg.getFromUserName(); //用戶 openid postMessage(openid,resultStr); } catch (Exception e) { e.printStackTrace(); } return null; }
微信的破文檔找個推送客戶消息太費勁了,網上都到的基本都是發(fā)送模板消息,但我只是想發(fā)個文本消息呀!!
后來才找到發(fā)送客服消息的url是:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=
發(fā)送模板消息的url是:https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=
官方接口介紹
發(fā)送文本信息
所以我們需要的就是這個url和發(fā)送的文本格式,就這么簡單幾個值而已:
public String postMessage(String openid,String content) throws Exception { //String access_token=WxMessageUtil.obtainAccessToken(); //appid和appsecret為公眾號里面的 String tokenData = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appId+"&secret="+appSecret; // 返回的用戶信息json字符串 String result=HttpUtil.doGet(tokenData); System.out.println(result); JSONObject jsonObject = JSON.parseObject(result); //先獲取access_token String access_token=String.valueOf(jsonObject.get("access_token")); System.out.println(access_token); //消息推送接口 String path = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + access_token; JSONObject jsonData = new JSONObject(); jsonData.put("touser", openid); jsonData.put("msgtype", "text"); JSONObject text = new JSONObject(); text.put("content",content); jsonData.put("text",text); System.out.println(jsonData); System.out.println(path); //HttpUtil.doPostJson(path, jsonData.toJSONString()); HttpRequest.sendPost(path, jsonData.toJSONString()); return "SUCCESS"; //return null; }
這樣就能成功給用戶異步回復消息,不會擔心超過5秒報異常的問題了
package com.bomc.recordLife.util; import java.io.*; import java.net.URL; import java.net.URLConnection; import java.util.List; import java.util.Map; public class HttpRequest { /** * 向指定URL發(fā)送GET方法的請求 * * @param url * 發(fā)送請求的URL * @param param * 請求參數,請求參數應該是 name1=value1&name2=value2 的形式。 * @return URL 所代表遠程資源的響應結果 */ public static String sendGet(String url, String param) { String result = ""; BufferedReader in = null; try { String urlNameString = url + "?" + param; URL realUrl = new URL(urlNameString); // 打開和URL之間的連接 URLConnection connection = realUrl.openConnection(); // 設置通用的請求屬性 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 建立實際的連接 connection.connect(); // 獲取所有響應頭字段 Map<String, List<String>> map = connection.getHeaderFields(); // 遍歷所有的響應頭字段 for (String key : map.keySet()) { System.out.println(key + "--->" + map.get(key)); } // 定義 BufferedReader輸入流來讀取URL的響應 in = new BufferedReader(new InputStreamReader( connection.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("發(fā)送GET請求出現(xiàn)異常!" + e); e.printStackTrace(); } // 使用finally塊來關閉輸入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return result; } /** * 向指定 URL 發(fā)送POST方法的請求 * * @param url * 發(fā)送請求的 URL * @param param * 請求參數,請求參數應該是 name1=value1&name2=value2 的形式。 * @return 所代表遠程資源的響應結果 */ public static String sendPost(String url, String param) { PrintWriter out = null; BufferedReader in = null; String result = ""; try { URL realUrl = new URL(url); // 打開和URL之間的連接 URLConnection conn = realUrl.openConnection(); // 設置通用的請求屬性 conn.setRequestProperty("accept", "application/json, text/javascript, */*; q=0.01"); conn.setRequestProperty("Accept-Encoding", "gzip, deflate"); conn.setRequestProperty("Connection", "keep-alive"); conn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.8"); conn.setRequestProperty("Content-Length", "80"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 發(fā)送POST請求必須設置如下兩行 conn.setDoOutput(true); conn.setDoInput(true); // 獲取URLConnection對象對應的輸出流 OutputStreamWriter outWriter = new OutputStreamWriter(conn.getOutputStream(), "utf-8"); out = new PrintWriter(outWriter); // 發(fā)送請求參數 out.print(param); // flush輸出流的緩沖 out.flush(); // 定義BufferedReader輸入流來讀取URL的響應 in = new BufferedReader( new InputStreamReader(conn.getInputStream(),"UTF-8")); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("發(fā)送 POST 請求出現(xiàn)異常!"+e); e.printStackTrace(); } //使用finally塊來關閉輸出流、輸入流 finally{ try{ if(out!=null){ out.close(); } if(in!=null){ in.close(); } } catch(IOException ex){ ex.printStackTrace(); } } return result; } public static void main(String[] args) { //發(fā)送 GET 請求 /* String s=HttpRequest.sendGet("http://localhost:6144/Home/RequestString", "key=123&v=456"); System.out.println(s);*/ //發(fā)送 POST 請求 /* String sr=HttpRequest.sendPost("http://www.cheshouye.com/api/weizhang/get_all_config",""); JSONObject jsStr = JSONObject.fromObject(sr); JSONArray jsonarray = jsStr.getJSONArray("configs"); for(int i=0;i<jsonarray.size();i++){ JSONObject ob1=(JSONObject)jsonarray.get(i); Integer provinceId=ob1.getInt("provice_id"); String provinceName=ob1.getString("provice_name"); String provinceShortName=ob1.getString("provice_short_name"); JSONArray jsonarray2 = ob1.getJSONArray("citys"); } System.out.println(jsonarray);*/ //(JSONObject)jsonarray[i] } }
到此這篇關于springboot實現(xiàn)公眾號接收回復消息和超過5秒被動回復消息的文章就介紹到這了,更多相關springboot 公眾號接收回復 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
基于SpringBoot實現(xiàn)發(fā)送帶附件的郵件
這篇文章主要介紹了基于SpringBoot實現(xiàn)發(fā)送帶附件的郵件,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-11-11SpringBoot應用War包形式部署到外部Tomcat的方法
這篇文章主要介紹了SpringBoot應用War包形式部署到外部Tomcat的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08