springboot實(shí)現(xiàn)公眾號(hào)接收回復(fù)消息和超過5秒被動(dòng)回復(fù)消息
本次就是記錄一下我的開發(fā)過程,不是教程,純屬自己做個(gè)筆記。
現(xiàn)在項(xiàng)目有個(gè)需求,需要用戶在公眾號(hào)發(fā)送圖片消息的時(shí)候,我后臺(tái)程序能接收到這個(gè)圖片,并用ai處理圖片并返回信息。
1.首先第一步要接收微信消息,需要在公眾號(hào)里設(shè)置與開發(fā)-基本配置里配置一下服務(wù)器配置
這個(gè)url配置了以后,所以微信公眾號(hào)的消息都會(huì)被推送到這個(gè)url對(duì)應(yīng)的接口上,Token感覺沒啥用,隨便寫一個(gè)完事,加密隨機(jī)生成,不加密消息的話也沒用。
最坑爹的是在提交配置的時(shí)候,微信要根據(jù)你填的這個(gè)url驗(yàn)證一下token認(rèn)證,而這個(gè)url實(shí)際是后臺(tái)處理消息的接口,搞不清楚咋肥四,我就先把這個(gè)接口寫成驗(yàn)證token的,等提交完配置再改回我的處理消息接口代碼。驗(yàn)證token這里隨便找了段代碼,親測(cè)有效。
@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("請(qǐng)求進(jìn)來(lái)了..."); 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");/// 時(shí)間戳 String nonce = request.getParameter("nonce"); /// 隨機(jī)數(shù) String echostr = request.getParameter("echostr"); // 隨機(jī)字符串 PrintWriter out = response.getWriter(); out.print(echostr); out.close(); }
2.配置好公眾號(hào)以后,開始接收微信消息
官方文檔在這里:文本消息 | 微信開放文檔
也就是說(shuō)微信會(huì)給你發(fā)送xml格式的消息,后臺(tái)需要能接收這個(gè)消息
要接收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>
以為對(duì)應(yīng)的圖標(biāo)消息是這樣的:
所以寫個(gè)消息的實(shí)體類:
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; }
還是先記錄一下如果不怕超時(shí)直接給用戶返回消息的情況:
@PostMapping(value = "analyzeImg2",consumes = "text/xml", produces = "text/xml;charset=utf-8") @ResponseBody public Object analyzeImg2(@RequestBody WxMessageImg wxMessageImg){ //拼一下要返回的信息對(duì)象 WxMessageImg resultMessage=new WxMessageImg(); try { //忽略圖片邏輯,直接鬧個(gè)結(jié)果 String resultStr="處理完圖片返回的信息"; String openid = wxMessageImg.getFromUserName(); //用戶 openid String mpid = wxMessageImg.getToUserName(); //公眾號(hào)原始 ID resultMessage.setToUserName(openid); resultMessage.setFromUserName(mpid); resultMessage.setCreateTime(new Date().getTime()); resultMessage.setContent(resultStr); resultMessage.setMsgType("text"); //用這個(gè)工具類處理出一串玩意直接返回 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"; /** * 返回消息類型:語(yǔ)音 */ public static final String RESP_MESSAGE_TYPE_Voice = "voice"; /** * 返回消息類型:視頻 */ public static final String RESP_MESSAGE_TYPE_Video = "video"; /** * 請(qǐng)求消息類型:文本 */ public static final String REQ_MESSAGE_TYPE_TEXT = "text"; /** * 請(qǐng)求消息類型:圖片 */ public static final String REQ_MESSAGE_TYPE_IMAGE = "image"; /** * 請(qǐng)求消息類型:鏈接 */ public static final String REQ_MESSAGE_TYPE_LINK = "link"; /** * 請(qǐng)求消息類型:地理位置 */ public static final String REQ_MESSAGE_TYPE_LOCATION = "location"; /** * 請(qǐng)求消息類型:音頻 */ public static final String REQ_MESSAGE_TYPE_VOICE = "voice"; /** * 請(qǐng)求消息類型:視頻 */ public static final String REQ_MESSAGE_TYPE_VIDEO = "video"; /** * 請(qǐng)求消息類型:推送 */ 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(自定義菜單點(diǎn)擊事件) */ public static final String EVENT_TYPE_CLICK = "CLICK"; /** * 事件類型:VIEW(自定義菜單URl視圖) */ public static final String EVENT_TYPE_VIEW = "VIEW"; /** * 事件類型:LOCATION(上報(bào)地理位置事件) */ public static final String EVENT_TYPE_LOCATION = "LOCATION"; /** * 事件類型:LOCATION(上報(bào)地理位置事件) */ public static final String EVENT_TYPE_SCAN = "SCAN"; /** * @Description: 解析微信發(fā)來(lái)的請(qǐng)求(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) { // 將解析結(jié)果存儲(chǔ)在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é)點(diǎn) List<Element> elementList = root.elements(); // 遍歷所有子節(jié)點(diǎn) 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: 文本消息對(duì)象轉(zhuǎn)換成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調(diào)用的需要這樣:
以上就是接收消息和被動(dòng)回復(fù)消息,但是有個(gè)大坑,一開始我想著處理完消息在直接返回信息回去,但是處理時(shí)間總是超過5秒,每次超過5秒它就報(bào)一下服務(wù)出現(xiàn)故障,一共請(qǐng)求三次,三次都超時(shí)就給你報(bào)三次故障。
后來(lái)客戶不愿意報(bào)這玩意兒,就只好改成直接返回success再異步調(diào)用處理圖片方法,處理完再用客服消息主動(dòng)給用戶發(fā)消息。
因?yàn)橐幚韴D片花費(fèi)的時(shí)間比較多,所以開個(gè)線程搞成異步調(diào)用處理圖片再推送消息,這樣的話直接返回字符串success
在controller里面寫個(gè)方法接收一下,用@RequestBody 直接把發(fā)來(lái)的xml變成對(duì)象
@RequestMapping(value = "analyzeImg") @ResponseBody public String analyzeImg(@RequestBody WxMessageImg wxMessageImg) throws Exception { //異步調(diào)用,先直接返回success,不然會(huì)顯示程序異常 new Thread(new Runnable() { public void run() { getImgData(wxMessageImg); } }).start(); return "SUCCESS"; //return null; }
public Object getImgData(WxMessageImg wxMessageImg){ try { //忽略圖片邏輯,直接鬧個(gè)結(jié)果 String resultStr="處理完圖片返回的信息"; String openid = wxMessageImg.getFromUserName(); //用戶 openid postMessage(openid,resultStr); } catch (Exception e) { e.printStackTrace(); } return null; }
微信的破文檔找個(gè)推送客戶消息太費(fèi)勁了,網(wǎng)上都到的基本都是發(fā)送模板消息,但我只是想發(fā)個(gè)文本消息呀??!
后來(lái)才找到發(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ā)送文本信息
所以我們需要的就是這個(gè)url和發(fā)送的文本格式,就這么簡(jiǎn)單幾個(gè)值而已:
public String postMessage(String openid,String content) throws Exception { //String access_token=WxMessageUtil.obtainAccessToken(); //appid和appsecret為公眾號(hào)里面的 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; }
這樣就能成功給用戶異步回復(fù)消息,不會(huì)擔(dān)心超過5秒報(bào)異常的問題了
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方法的請(qǐng)求 * * @param url * 發(fā)送請(qǐng)求的URL * @param param * 請(qǐng)求參數(shù),請(qǐng)求參數(shù)應(yīng)該是 name1=value1&name2=value2 的形式。 * @return URL 所代表遠(yuǎn)程資源的響應(yīng)結(jié)果 */ 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(); // 設(shè)置通用的請(qǐng)求屬性 connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 建立實(shí)際的連接 connection.connect(); // 獲取所有響應(yīng)頭字段 Map<String, List<String>> map = connection.getHeaderFields(); // 遍歷所有的響應(yīng)頭字段 for (String key : map.keySet()) { System.out.println(key + "--->" + map.get(key)); } // 定義 BufferedReader輸入流來(lái)讀取URL的響應(yīng) in = new BufferedReader(new InputStreamReader( connection.getInputStream())); String line; while ((line = in.readLine()) != null) { result += line; } } catch (Exception e) { System.out.println("發(fā)送GET請(qǐng)求出現(xiàn)異常!" + e); e.printStackTrace(); } // 使用finally塊來(lái)關(guān)閉輸入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return result; } /** * 向指定 URL 發(fā)送POST方法的請(qǐng)求 * * @param url * 發(fā)送請(qǐng)求的 URL * @param param * 請(qǐng)求參數(shù),請(qǐng)求參數(shù)應(yīng)該是 name1=value1&name2=value2 的形式。 * @return 所代表遠(yuǎn)程資源的響應(yīng)結(jié)果 */ 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(); // 設(shè)置通用的請(qǐng)求屬性 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請(qǐng)求必須設(shè)置如下兩行 conn.setDoOutput(true); conn.setDoInput(true); // 獲取URLConnection對(duì)象對(duì)應(yīng)的輸出流 OutputStreamWriter outWriter = new OutputStreamWriter(conn.getOutputStream(), "utf-8"); out = new PrintWriter(outWriter); // 發(fā)送請(qǐng)求參數(shù) out.print(param); // flush輸出流的緩沖 out.flush(); // 定義BufferedReader輸入流來(lái)讀取URL的響應(yīng) 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 請(qǐng)求出現(xiàn)異常!"+e); e.printStackTrace(); } //使用finally塊來(lái)關(guān)閉輸出流、輸入流 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 請(qǐng)求 /* String s=HttpRequest.sendGet("http://localhost:6144/Home/RequestString", "key=123&v=456"); System.out.println(s);*/ //發(fā)送 POST 請(qǐng)求 /* 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] } }
到此這篇關(guān)于springboot實(shí)現(xiàn)公眾號(hào)接收回復(fù)消息和超過5秒被動(dòng)回復(fù)消息的文章就介紹到這了,更多相關(guān)springboot 公眾號(hào)接收回復(fù) 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于SpringBoot實(shí)現(xiàn)發(fā)送帶附件的郵件
這篇文章主要介紹了基于SpringBoot實(shí)現(xiàn)發(fā)送帶附件的郵件,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11Java實(shí)現(xiàn)飛機(jī)大戰(zhàn)-II游戲詳解
《飛機(jī)大戰(zhàn)-II》是一款融合了街機(jī)、競(jìng)技等多種元素的經(jīng)典射擊手游。游戲是用java語(yǔ)言實(shí)現(xiàn),采用了swing技術(shù)進(jìn)行了界面化處理,感興趣的可以了解一下2022-02-02SpringBoot應(yīng)用War包形式部署到外部Tomcat的方法
這篇文章主要介紹了SpringBoot應(yīng)用War包形式部署到外部Tomcat的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2018-08-08Java驗(yàn)證碼功能的實(shí)現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了Java驗(yàn)證碼功能的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11jvm crash的崩潰日志詳細(xì)分析及注意點(diǎn)
本篇文章主要介紹了jvm crash的崩潰日志詳細(xì)分析及注意點(diǎn)。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-04-04詳解Java函數(shù)式編程和lambda表達(dá)式
這篇文章主要介紹了Java函數(shù)式編程和lambda表達(dá)式,對(duì)lambda感興趣的同學(xué),一定要看一下2021-04-04Spring如何通過注解存儲(chǔ)和讀取對(duì)象詳解
在Spring中,要想更簡(jiǎn)單的存儲(chǔ)和讀取對(duì)象的核心是使用注解,這篇文章主要給大家介紹了關(guān)于Spring如何通過注解存儲(chǔ)和讀取對(duì)象的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07學(xué)習(xí)Java模擬實(shí)現(xiàn)百度文檔在線瀏覽
這片文章介紹了如何使用Java模擬實(shí)現(xiàn)百度文檔在線瀏覽,文章思路清晰,需要的朋友可以參考下2015-07-07