您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“spring boot如何開(kāi)發(fā)微信公眾號(hào)后臺(tái)”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
回復(fù)口令獲取視頻鏈接的實(shí)現(xiàn)原理很簡(jiǎn)單,說(shuō)白了,就是一個(gè)數(shù)據(jù)查詢(xún)操作而已,回復(fù)的口令是查詢(xún)關(guān)鍵字,回復(fù)的內(nèi)容則是查詢(xún)結(jié)果。這個(gè)原理很簡(jiǎn)單。
這是大家在公眾號(hào)后臺(tái)回復(fù)關(guān)鍵字的情況。那么這個(gè)消息是怎么樣一個(gè)傳遞流程呢?我們來(lái)看看下面這張圖:
這張圖,我給大家稍微解釋下:
javaboy4096
這個(gè)字符從公眾號(hào)上發(fā)送到了微信服務(wù)器javaboy4096
轉(zhuǎn)發(fā)到我自己的服務(wù)器上javaboy4096
這個(gè)字符之后,就去數(shù)據(jù)庫(kù)中查詢(xún),將查詢(xún)的結(jié)果,按照騰訊要求的 XML 格式進(jìn)行返回大致的流程就是這個(gè)樣子。
接下來(lái)我們就來(lái)看一下實(shí)現(xiàn)細(xì)節(jié)。
開(kāi)發(fā)的第一步,是微信服務(wù)器要驗(yàn)證我們自己的服務(wù)器是否有效。
首先我們登錄微信公眾平臺(tái)官網(wǎng)后,在公眾平臺(tái)官網(wǎng)的 「開(kāi)發(fā)-基本設(shè)置」 頁(yè)面,勾選協(xié)議成為開(kāi)發(fā)者,然后點(diǎn)擊“修改配置”按鈕,填寫(xiě):
這里的 URL 配置好之后,我們需要針對(duì)這個(gè) URL 開(kāi)發(fā)兩個(gè)接口,一個(gè)是 GET 請(qǐng)求的接口,這個(gè)接口用來(lái)做服務(wù)器有效性驗(yàn)證,另一個(gè)則是 POST 請(qǐng)求的接口,這個(gè)用來(lái)接收微信服務(wù)器發(fā)送來(lái)的消息。也就是說(shuō),微信服務(wù)器的消息都是通過(guò) POST 請(qǐng)求發(fā)給我的。
Token 可由開(kāi)發(fā)者可以任意填寫(xiě),用作生成簽名(該 Token 會(huì)和接口 URL 中包含的 Token 進(jìn)行比對(duì),從而驗(yàn)證安全性)。
EncodingAESKey 由開(kāi)發(fā)者手動(dòng)填寫(xiě)或隨機(jī)生成,將用作消息體加解密密鑰。
同時(shí),開(kāi)發(fā)者可選擇消息加解密方式:明文模式、兼容模式和安全模式。明文模式就是我們自己的服務(wù)器收到微信服務(wù)器發(fā)來(lái)的消息是明文字符串,直接就可以讀取并且解析,安全模式則是我們收到微信服務(wù)器發(fā)來(lái)的消息是加密的消息,需要我們手動(dòng)解析后才能使用。
公眾號(hào)后臺(tái)配置完成后,接下來(lái)我們就可以寫(xiě)代碼了。
我們首先來(lái)創(chuàng)建一個(gè)普通的 Spring Boot 項(xiàng)目,創(chuàng)建時(shí)引入 spring-boot-starter-web
依賴(lài),項(xiàng)目創(chuàng)建成功后,我們創(chuàng)建一個(gè) Controller ,添加如下接口:
@GetMapping("/verify_wx_token")
public void login(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
request.setCharacterEncoding("UTF-8");
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");
PrintWriter out = null;
try {
out = response.getWriter();
if (CheckUtil.checkSignature(signature, timestamp, nonce)) {
out.write(echostr);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
out.close();
}
}
關(guān)于這段代碼,我做如下解釋?zhuān)?/p>
校驗(yàn)代碼如下:
public class CheckUtil {
private static final String token = "123456";
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] str = new String[]{token, timestamp, nonce};
//排序
Arrays.sort(str);
//拼接字符串
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < str.length; i++) {
buffer.append(str[i]);
}
//進(jìn)行sha1加密
String temp = SHA1.encode(buffer.toString());
//與微信提供的signature進(jìn)行匹對(duì)
return signature.equals(temp);
}
}
public class SHA1 {
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
private static String getFormattedText(byte[] bytes) {
int len = bytes.length;
StringBuilder buf = new StringBuilder(len * 2);
for (int j = 0; j < len; j++) {
buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
}
return buf.toString();
}
public static String encode(String str) {
if (str == null) {
return null;
}
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
messageDigest.update(str.getBytes());
return getFormattedText(messageDigest.digest());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
OK,完成之后,我們的校驗(yàn)接口就算是開(kāi)發(fā)完成了。接下來(lái)就可以開(kāi)發(fā)消息接收接口了。
接下來(lái)我們來(lái)開(kāi)發(fā)消息接收接口,消息接收接口和上面的服務(wù)器校驗(yàn)接口地址是一樣的,都是我們一開(kāi)始在公眾號(hào)后臺(tái)配置的地址。只不過(guò)消息接收接口是一個(gè) POST 請(qǐng)求。
我在公眾號(hào)后臺(tái)配置的時(shí)候,消息加解密方式選擇了明文模式,這樣我在后臺(tái)收到的消息直接就可以處理了。微信服務(wù)器給我發(fā)來(lái)的普通文本消息格式如下:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
這些參數(shù)含義如下:
看到這里,大家心里大概就有數(shù)了,當(dāng)我們收到微信服務(wù)器發(fā)來(lái)的消息之后,我們就進(jìn)行 XML 解析,提取出來(lái)我們需要的信息,去做相關(guān)的查詢(xún)操作,再將查到的結(jié)果返回給微信服務(wù)器。
這里我們先來(lái)個(gè)簡(jiǎn)單的,我們將收到的消息解析并打印出來(lái):
@PostMapping("/verify_wx_token")
public void handler(HttpServletRequest request, HttpServletResponse response) throws Exception {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
Map<String, String> parseXml = MessageUtil.parseXml(request);
String msgType = parseXml.get("MsgType");
String content = parseXml.get("Content");
String fromusername = parseXml.get("FromUserName");
String tousername = parseXml.get("ToUserName");
System.out.println(msgType);
System.out.println(content);
System.out.println(fromusername);
System.out.println(tousername);
}
public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
Map<String, String> map = new HashMap<String, String>();
InputStream inputStream = request.getInputStream();
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
Element root = document.getRootElement();
List<Element> elementList = root.elements();
for (Element e : elementList)
map.put(e.getName(), e.getText());
inputStream.close();
inputStream = null;
return map;
}
大家看到其實(shí)都是一些常規(guī)代碼,沒(méi)有什么難度。
做完這些之后,我們將項(xiàng)目打成 jar 包在服務(wù)器上部署啟動(dòng)。啟動(dòng)成功之后,確認(rèn)微信的后臺(tái)配置也沒(méi)問(wèn)題,我們就可以在公眾號(hào)上發(fā)一條消息了,這樣我們自己的服務(wù)端就會(huì)打印出來(lái)剛剛消息的信息。
在討論如何給微信服務(wù)器回復(fù)消息之前,我們需要先來(lái)了解下微信服務(wù)器發(fā)來(lái)的消息主要有哪些類(lèi)型以及我們回復(fù)給微信的消息都有哪些類(lèi)型。
在上文中大家了解到,微信發(fā)送來(lái)的 xml 消息中有一個(gè) MsgType 字段,這個(gè)字段就是用來(lái)標(biāo)記消息的類(lèi)型。這個(gè)類(lèi)型可以標(biāo)記出這條消息是普通消息還是事件消息還是圖文消息等。
普通消息主要是指:
不同的消息類(lèi)型,對(duì)應(yīng)不同的 MsgType,這里我還是以普通消息為例,如下:
消息類(lèi)型 | MsgType |
---|---|
文本消息 | text |
圖片消息 | image |
語(yǔ)音消息 | voice |
視頻消息 | video |
小視頻消息 | shortvideo |
地址位置消息 | location |
鏈接消息 | link |
大家千萬(wàn)不要以為不同類(lèi)型消息的格式是一樣的,其實(shí)是不一樣的,也就是說(shuō),MsgType 為 text 的消息和 MsgType 為 image 的消息,微信服務(wù)器發(fā)給我們的消息內(nèi)容是不一樣的,這樣帶來(lái)一個(gè)問(wèn)題就是我無(wú)法使用一個(gè) Bean 去接收不同類(lèi)型的數(shù)據(jù),因此這里我們一般使用 Map 接收即可。
這是消息的接收,除了消息的接收之外,還有一個(gè)消息的回復(fù),我們回復(fù)的消息也有很多類(lèi)型,可以回復(fù)普通消息,也可以回復(fù)圖片消息,回復(fù)語(yǔ)音消息等,不同的回復(fù)消息我們可以進(jìn)行相應(yīng)的封裝。因?yàn)椴煌姆祷叵?shí)例也是有一些共同的屬性的,例如消息是誰(shuí)發(fā)來(lái)的,發(fā)給誰(shuí),消息類(lèi)型,消息 id 等,所以我們可以將這些共同的屬性定義成一個(gè)父類(lèi),然后不同的消息再去繼承這個(gè)父類(lèi)。
首先我們來(lái)定義一個(gè)公共的消息類(lèi)型:
public class BaseMessage {
private String ToUserName;
private String FromUserName;
private long CreateTime;
private String MsgType;
private long MsgId;
//省略 getter/setter
}
在這里:
這是我們的基本消息類(lèi)型,就是說(shuō),我們返回給用戶(hù)的消息,無(wú)論是什么類(lèi)型的消息,都有這幾個(gè)基本屬性。然后在此基礎(chǔ)上,我們?cè)偃U(kuò)展出文本消息、圖片消息 等。
我們來(lái)看下文本消息的定義:
public class TextMessage extends BaseMessage {
private String Content;
//省略 getter/setter
}
文本消息在前面消息的基礎(chǔ)上多了一個(gè) Content 屬性,因此文本消息繼承自 BaseMessage ,再額外添加一個(gè) Content 屬性即可。
其他的消息類(lèi)型也是類(lèi)似的定義,我就不一一列舉了,至于其他消息的格式,大家可以參考微信開(kāi)放文檔(http://1t.click/aPXK)。
消息類(lèi)型的 Bean 定義完成之后,接下來(lái)就是將實(shí)體類(lèi)生成 XML。
首先我們定義一個(gè)消息工具類(lèi),將常見(jiàn)的消息類(lèi)型枚舉出來(lái):
/**
* 返回消息類(lèi)型:文本
*/
public static final String RESP_MESSAGE_TYPE_TEXT = "text";
/**
* 返回消息類(lèi)型:音樂(lè)
*/
public static final String RESP_MESSAGE_TYPE_MUSIC = "music";
/**
* 返回消息類(lèi)型:圖文
*/
public static final String RESP_MESSAGE_TYPE_NEWS = "news";
/**
* 返回消息類(lèi)型:圖片
*/
public static final String RESP_MESSAGE_TYPE_Image = "image";
/**
* 返回消息類(lèi)型:語(yǔ)音
*/
public static final String RESP_MESSAGE_TYPE_Voice = "voice";
/**
* 返回消息類(lèi)型:視頻
*/
public static final String RESP_MESSAGE_TYPE_Video = "video";
/**
* 請(qǐng)求消息類(lèi)型:文本
*/
public static final String REQ_MESSAGE_TYPE_TEXT = "text";
/**
* 請(qǐng)求消息類(lèi)型:圖片
*/
public static final String REQ_MESSAGE_TYPE_IMAGE = "image";
/**
* 請(qǐng)求消息類(lèi)型:鏈接
*/
public static final String REQ_MESSAGE_TYPE_LINK = "link";
/**
* 請(qǐng)求消息類(lèi)型:地理位置
*/
public static final String REQ_MESSAGE_TYPE_LOCATION = "location";
/**
* 請(qǐng)求消息類(lèi)型:音頻
*/
public static final String REQ_MESSAGE_TYPE_VOICE = "voice";
/**
* 請(qǐng)求消息類(lèi)型:視頻
*/
public static final String REQ_MESSAGE_TYPE_VIDEO = "video";
/**
* 請(qǐng)求消息類(lèi)型:推送
*/
public static final String REQ_MESSAGE_TYPE_EVENT = "event";
/**
* 事件類(lèi)型:subscribe(訂閱)
*/
public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";
/**
* 事件類(lèi)型:unsubscribe(取消訂閱)
*/
public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";
/**
* 事件類(lèi)型:CLICK(自定義菜單點(diǎn)擊事件)
*/
public static final String EVENT_TYPE_CLICK = "CLICK";
/**
* 事件類(lèi)型:VIEW(自定義菜單 URl 視圖)
*/
public static final String EVENT_TYPE_VIEW = "VIEW";
/**
* 事件類(lèi)型:LOCATION(上報(bào)地理位置事件)
*/
public static final String EVENT_TYPE_LOCATION = "LOCATION";
/**
* 事件類(lèi)型:LOCATION(上報(bào)地理位置事件)
*/
public static final String EVENT_TYPE_SCAN = "SCAN";
大家注意這里消息類(lèi)型的定義,以 RESP 開(kāi)頭的表示返回的消息類(lèi)型,以 REQ 表示微信服務(wù)器發(fā)來(lái)的消息類(lèi)型。然后在這個(gè)工具類(lèi)中再定義兩個(gè)方法,用來(lái)將返回的對(duì)象轉(zhuǎn)換成 XML:
public static String textMessageToXml(TextMessage textMessage) {
xstream.alias("xml", textMessage.getClass());
return xstream.toXML(textMessage);
}
private static XStream xstream = new XStream(new XppDriver() {
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
boolean cdata = true;
@SuppressWarnings("rawtypes")
public void startNode(String name, Class clazz) {
super.startNode(name, clazz);
}
protected void writeText(QuickWriter writer, String text) {
if (cdata) {
writer.write("<![CDATA[");
writer.write(text);
writer.write("]]>");
} else {
writer.write(text);
}
}
};
}
});
textMessageToXML 方法用來(lái)將 TextMessage 對(duì)象轉(zhuǎn)成 XML 返回給微信服務(wù)器,類(lèi)似的方法我們還需要定義 imageMessageToXml、voiceMessageToXml 等,不過(guò)定義的方式都基本類(lèi)似,我就不一一列出來(lái)了。
由于用戶(hù)發(fā)來(lái)的消息可能存在多種情況,我們需要分類(lèi)進(jìn)行處理,這個(gè)就涉及到返回消息的分發(fā)問(wèn)題。因此我在這里再定義一個(gè)返回消息分發(fā)的工具類(lèi),如下:
public class MessageDispatcher {
public static String processMessage(Map<String, String> map) {
String openid = map.get("FromUserName"); //用戶(hù) openid
String mpid = map.get("ToUserName"); //公眾號(hào)原始 ID
if (map.get("MsgType").equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
//普通文本消息
TextMessage txtmsg = new TextMessage();
txtmsg.setToUserName(openid);
txtmsg.setFromUserName(mpid);
txtmsg.setCreateTime(new Date().getTime());
txtmsg.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
txtmsg.setContent("這是返回消息");
return MessageUtil.textMessageToXml(txtmsg);
}
return null;
}
public String processEvent(Map<String, String> map) {
//在這里處理事件
}
}
這里我們還可以多加幾個(gè) elseif 去判斷不同的消息類(lèi)型,我這里因?yàn)橹挥衅胀ㄎ谋鞠ⅲ砸粋€(gè) if 就夠用了。
在這里返回值我寫(xiě)死了,實(shí)際上這里需要根據(jù)微信服務(wù)端傳來(lái)的 Content 去數(shù)據(jù)中查詢(xún),將查詢(xún)結(jié)果返回,數(shù)據(jù)庫(kù)查詢(xún)這一套相信大家都能搞定,我這里就不重復(fù)介紹了。
最后在消息接收 Controller 中調(diào)用該方法,如下:
@PostMapping(value = "/verify_wx_token",produces = "application/xml;charset=utf-8")
public String handler(HttpServletRequest request, HttpServletResponse response) throws Exception {
request.setCharacterEncoding("UTF-8");
Map<String, String> map = MessageUtil.parseXml(request);
String msgType = map.get("MsgType");
if (MessageUtil.REQ_MESSAGE_TYPE_EVENT.equals(msgType)) {
return messageDispatcher.processEvent(map);
}else{
return messageDispatcher.processMessage(map);
}
}
在 Controller 中,我們首先判斷消息是否是事件,如果是事件,進(jìn)入到事件處理通道,如果不是事件,則進(jìn)入到消息處理通道。
“spring boot如何開(kāi)發(fā)微信公眾號(hào)后臺(tái)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。