本次就是記錄一下我的開(kāi)發(fā)過(guò)程,不是教程,純屬自己做個(gè)筆記。
現(xiàn)在項(xiàng)目有個(gè)需求,需要用戶在公眾號(hào)發(fā)送圖片消息的時(shí)候,我后臺(tái)程序能接收到這個(gè)圖片,并用ai處理圖片并返回信息。
1.首先第一步要接收微信消息,需要在公眾號(hào)里設(shè)置與開(kāi)發(fā)-基本配置里配置一下服務(wù)器配置
這個(gè)url配置了以后,所以微信公眾號(hào)的消息都會(huì)被推送到這個(gè)url對(duì)應(yīng)的接口上,Token感覺(jué)沒(méi)啥用,隨便寫一個(gè)完事,加密隨機(jī)生成,不加密消息的話也沒(méi)用。
最坑爹的是在提交配置的時(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)以后,開(kāi)始接收微信消息
官方文檔在這里:文本消息 | 微信開(kāi)放文檔
也就是說(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";
/**
* 返回消息類型:音樂(lè)
*/
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è)大坑,一開(kāi)始我想著處理完消息在直接返回信息回去,但是處理時(shí)間總是超過(guò)5秒,每次超過(guò)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í)間比較多,所以開(kāi)個(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)心超過(guò)5秒報(bào)異常的問(wèn)題了
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);
// 打開(kāi)和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);
// 打開(kāi)和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ù)消息和超過(guò)5秒被動(dòng)回復(fù)消息的文章就介紹到這了,更多相關(guān)springboot 公眾號(hào)接收回復(fù) 內(nèi)容請(qǐng)搜索html5模板網(wǎng)以前的文章希望大家以后多多支持html5模板網(wǎng)!