springboot实现公众号接收回复消息和超过5秒被动回复消息
作者:豆趣编程
本次就是记录一下我的开发过程,不是教程,纯属自己做个笔记。
现在项目有个需求,需要用户在公众号发送图片消息的时候,我后台程序能接收到这个图片,并用ai处理图片并返回信息。
1.首先第一步要接收微信消息,需要在公众号里设置与开发-基本配置里配置一下服务器配置
这个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.配置好公众号以后,开始接收微信消息
官方文档在这里:文本消息 | 微信开放文档
也就是说微信会给你发送xml格式的消息,后台需要能接收这个消息
要接收xml消息和以后发送消息啥的,需要先引入一些依赖:
<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: 解析微信发来的请求(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(); // 得到根元素的所有子节点 List<Element> elementList = root.elements(); // 遍历所有子节点 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秒它就报一下服务出现故障,一共请求三次,三次都超时就给你报三次故障。
后来客户不愿意报这玩意儿,就只好改成直接返回success再异步调用处理图片方法,处理完再用客服消息主动给用户发消息。
因为要处理图片花费的时间比较多,所以开个线程搞成异步调用处理图片再推送消息,这样的话直接返回字符串success
在controller里面写个方法接收一下,用@RequestBody 直接把发来的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; }
微信的破文档找个推送客户消息太费劲了,网上都到的基本都是发送模板消息,但我只是想发个文本消息呀!!
后来才找到发送客服消息的url是:https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=
发送模板消息的url是:https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=
官方接口介绍
发送文本信息
所以我们需要的就是这个url和发送的文本格式,就这么简单几个值而已:
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发送GET方法的请求 * * @param url * 发送请求的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("发送GET请求出现异常!" + e); e.printStackTrace(); } // 使用finally块来关闭输入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return result; } /** * 向指定 URL 发送POST方法的请求 * * @param url * 发送请求的 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)"); // 发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流 OutputStreamWriter outWriter = new OutputStreamWriter(conn.getOutputStream(), "utf-8"); out = new PrintWriter(outWriter); // 发送请求参数 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("发送 POST 请求出现异常!"+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) { //发送 GET 请求 /* String s=HttpRequest.sendGet("http://localhost:6144/Home/RequestString", "key=123&v=456"); System.out.println(s);*/ //发送 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实现公众号接收回复消息和超过5秒被动回复消息的文章就介绍到这了,更多相关springboot 公众号接收回复 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!