springboot实现公众号接收回复消息和超过5秒被动回复消息

网友投稿 541 2022-07-29


本次就是记录一下我的开发过程,不是教程,纯属自己做个笔记。

最坑爹的是在提交配置的时候,微信要根据你填的这个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();

}

官方文档在这里:文本消息 | 微信开放文档

也就是说微信会给你发送xml格式的消息,后台需要能接收这个消息

要接收xml消息和以后发送消息啥的,需要先引入一些依赖:

com.fasterxml.jackson.dataformat

jackson-dataformat-xml

com.fasterxml.jackson.core

jackson-databind

2.10.0

com.fasterxml.jackson.core

jackson-core

2.10.0

com.fasterxml.jackson.core

jackson-annotations

2.10.0

com.fasterxml.jackson.jaxrs

jackson-jaxrs-xml-provider

org.projectlombok

lombok

org.apache.httpcomponents

httpcore

4.4.10

org.apache.httpcomponents

httpclient

4.5.6

dom4j

dom4j

1.6

com.thoughtworks.xstream

xstream

1.4.4

以为对应的图标消息是这样的:

所以写个消息的实体类:

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;

@JackiZGlfXsonXmlProperty(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

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 parseXml(HttpServletRequest request) {

// 将解析结果存储在HashMap中

Map map = new HashMap();

// 读取输入流

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 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();

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> 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://cheshouye.com/api/weizhang/get_all_config","");

JSONObject jsStr = JSONObject.fromObject(sr);

JSONArray jsonarray = jsStr.getJSONArray("configs");

for(int i=0;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]

}

}

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]

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:springboot整合mybatis流程详解(mybatis和springboot整合)
下一篇:Java深入浅出讲解多线程的概念到使用(深入浅出java多线程豆瓣)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~