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;
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";
public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";
public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
public static final String EVENT_TYPE_CLICK = "CLICK";
public static final String EVENT_TYPE_VIEW = "VIEW";
public static final String EVENT_TYPE_LOCATION = "LOCATION";
public static final String EVENT_TYPE_SCAN = "SCAN";
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;
}
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 {
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", "*
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, *
//发送 POST 请求
//(JSONObject)jsonarray[i]
}
}
到此这篇关于SpringBoot实现公众号接收回复消息和超过5秒被动回复消息的文章就介绍到这了,更多相关springboot 公众号接收回复 内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
相关文章