[038] 微信公眾帳號開發教程第14篇-自定義選單的建立及選單事件響應
微信5.0釋出
2013年8月5日,伴隨著微信5.0 iPhone版的釋出,公眾平臺也進行了重要的更新,主要包括:
1)運營主體為組織,可選擇成為服務號或者訂閱號;
2)服務號可以申請自定義選單;
3)使用QQ登入的公眾號,可以升級為郵箱登入;
4)使用郵箱登入的公眾號,可以修改登入郵箱;
5)編輯圖文訊息可選填作者;
6)群發訊息可以同步到騰訊微博。
其中,大家議論最多的當屬前兩條,就是關於帳號型別和自定義選單的更新,我這裡做幾點補充說明:
1)目前公眾號型別分為兩種:服務號和訂閱號,8月5日平臺更新後所有的帳號預設為訂閱號,有一次轉換成服務號的機會;
2)服務號主要面向企業、政府和其他組織,而訂閱號主要面向媒體和個人;
3)只有服務號可以申請自定義選單,訂閱號不能申請;
4)服務號每月只能群發一條訊息,而訂閱號每天能群發一條訊息。
平臺更新後,讓很多人糾結的是自定義選單和每天群發一條訊息不可兼得,對此,我不想過多評論。
引言及內容概要
在微信5.0以前,自定義選單是作為一種內測資格使用的,只有少數公眾帳號擁有選單,因此出現很多企業為了弄到選單不惜重金求購。現如今,一大批帳號從訂閱號轉為服務號,很多都是奔著自定義選單去的。而且,經測試發現,微信最近的稽核放鬆很多,只要申請服務號、自定義選單的基本都成功了,根本不管填寫的資料真偽。不知道以後微信會不會翻臉,要求補全企業資料,那將會是一種給小孩一顆糖吃再把他打哭的感覺。。。
自定義選單是申請到了,到底該怎麼建立、怎麼使用呢?最近幾天不管是微信官方交流群,還是在我部落格留言裡,都能夠看到不少開發者都在為這個發愁。本篇文章就為大家解決這個難題。
自定義選單的建立步驟
1、找到AppId和AppSecret。自定義選單申請成功後,在“高階功能”-“開發模式”-“介面配置資訊”的最後兩項就是;
2、根據AppId和AppSecret,以https get方式獲取訪問特殊介面所必須的憑證access_token;
3、根據access_token,將json格式的選單資料通過https post方式提交。
分析建立選單的難點
原來建立選單這麼簡單,三步就能搞定?跟把大象放冰箱差不多。呵呵,當然沒有這麼簡單,那我們一步步來看,到底難在哪裡?
首先,第1步肯定都沒有問題,只要成功申請了自定義選單,一定能拿到AppId和AppSecret這兩個值。
再來看第2步,由於是get方式獲取access_token,很多人直接把拼好的url放在瀏覽器裡執行,access_token就拿到了。拋開是不是用程式設計方式實現的來說,這真是個好辦法,顯然大家在第二步上也沒有問題。
最後再看第3步,拼裝json格式的選單資料,雖然繁鎖一點,但基本上也都沒有什麼問題的,因為官方給了個例子,照貓畫虎就行了。那問題一定就出現在https post提交上了。
結論:不知道如何建立自定義選單的朋友,大都可以歸為以下三種情況:
1)根本不看或者沒看懂公眾平臺API文件中關於“通用介面”、“自定義選單介面”和“使用限制”部分的說明;
2)不知道如何發起HTTPS請求(平時的http請求,直接使用HttpUrlConnection就可以輕鬆搞定,但https請求要複雜一點);
3)不知道如何通過POST方式提交json格式的選單資料。
正在看文章的你,不知道是屬於哪一種,或者幾種情況都有,不妨留言說出來,也可以做個調查。不管屬於哪一種情況,既然看到了這篇文章,相信一定會讓你弄明白的。
解讀通用介面文件---憑證的獲取
我們先來看通用介面文件的簡介部分,如下圖所示。
通俗點講,這段簡介可以這麼理解:公眾平臺還有很多特殊的介面,像自定義選單的建立、語音檔案的獲取、主動傳送訊息等,如果開發者想通過HTTP請求訪問這些特殊介面,就必須要有訪問憑證,也就是access_token。
那麼,又該如何獲取介面訪問憑證access_token呢?讓我們繼續往下看。
圖中已經表達的很清楚了,獲取access_token是通過GET方式訪問如下連結:
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
連結中有三個引數,分別是grant_type、appid和secret。根據圖中的引數說明,grant_type傳固定值client_credential,而appid和secret就是申請完自定義選單後微信分配給我們的。
請求傳送成功後,微信伺服器會返回一個json串,包含access_token和expires_in兩個元素。其中,access_token就是我們最終需要的憑證,而expires_in是憑證的有效期,單位是秒,7200秒也就是2個小時。這就意味著,不是每次訪問特殊介面,都需要重新獲取一次access_token,只要access_token還在有效期內,就一直可以使用。
解讀自定義選單介面文件
還是一樣,先來看看自定義選單介面的簡介部分,如下圖所示。
從圖中我們能夠獲取到以下資訊:
1)拿到憑證access_token後,我們能對選單執行三種操作:建立、查詢和刪除;
2)自定義選單目前只支援click一種事件,即使用者點選後回覆某種型別的訊息;不能夠實現點選選單項直接開啟頁面(type=view未開放,目前只是微生活有);
3)由於微信客戶端快取的原因,選單建立後並不會立即在微信上顯示出來,需要過24小時。在測試選單建立時,可以通過取消關注後,再關注的方式達到立即看選單的目的。
繼續往下看,就是關於選單怎麼建立的介紹了,如下圖所示。
後面,關於引數說明的部分我就不一一貼圖說明了,把重點說一下:
1)自定義選單是一個3x5結構的,即選單最多隻能有二級,一級選單最多隻能有3個,每個一級選單下最多可以有5個二級選單項;
2)選單項都有一個key值。當用戶點選某個選單項時,微信會將該選單項的key值以事件推送的方式傳送給我們的後臺處理程式。
關於選單的查詢、建立我就不提了,這兩個介面使用的頻率非常小,一般都用不上。如果需要,再按照我上面提供的思路也不難理解。
解讀API文件之使用限制
很多小夥伴看到這張圖就開始疑惑了:怎麼選單還限制使用次數,使用者量越來越大的時候,根本不夠用啊。看清楚,這個限制是針對介面呼叫的,也就是針對開發者的,和使用者數、使用次數半點關係也沒有。
就先拿獲取憑證介面來說吧,限制一天只能呼叫200次。還記得前面提到過access_token是有有效期的,並且有效期為兩小時,也就是獲取一次access_token後的兩小時內,都可以繼續使用,那麼理想情況一天24小時內,是不是隻需要獲取12次就夠了?難道200次還不夠用?
再來看下選單建立介面限制一天只能呼叫100次。我就這麼解釋吧,選單建立一次後,只要你不切換模式(指的是在編輯模式和開發模式間切換)、不呼叫刪除介面,這個選單會永遠存在的。誰沒事幹,一天要建立100次選單,就算是測試,測個10次8次足夠了吧?
選單的查詢和刪除介面的限制我就不解釋了,至今為止這二個介面我都沒使用過一次。就算有這樣的使用需求,一天這麼多次的呼叫,完全足夠了。
封裝通用的請求方法
讀到這裡,就預設大家已經掌握了上面講到的所有關於自定義選單的理論知識,下面就進入程式碼實戰講解的部分。
先前我們瞭解到,建立選單需要呼叫二個介面,並且都是https請求,而非http。如果要封裝一個通用的請求方法,該方法至少需要具備以下能力:
1)支援HTTPS請求;
2)支援GET、POST兩種方式;
3)支援引數提交,也支援無引數的情況;
對於https請求,我們需要一個證書信任管理器,這個管理器類需要自己定義,但需要實現X509TrustManager介面,程式碼如下:
package org.liufeng.weixin.util;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
/**
* 證書信任管理器(用於https請求)
*
* @author liufeng
* @date 2013-08-08
*/
public class MyX509TrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
這個證書管理器的作用就是讓它信任我們指定的證書,上面的程式碼意味著信任所有證書,不管是否權威機構頒發。
證書有了,通用的https請求方法就不難實現了,實現程式碼如下:
package org.liufeng.weixin.util;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 公眾平臺通用介面工具類
*
* @author liuyq
* @date 2013-08-09
*/
public class WeixinUtil {
private static Logger log = LoggerFactory.getLogger(WeixinUtil.class);
/**
* 發起https請求並獲取結果
*
* @param requestUrl 請求地址
* @param requestMethod 請求方式(GET、POST)
* @param outputStr 提交的資料
* @return JSONObject(通過JSONObject.get(key)的方式獲取json物件的屬性值)
*/
public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
StringBuffer buffer = new StringBuffer();
try {
// 建立SSLContext物件,並使用我們指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 從上述SSLContext物件中得到SSLSocketFactory物件
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
// 設定請求方式(GET/POST)
httpUrlConn.setRequestMethod(requestMethod);
if ("GET".equalsIgnoreCase(requestMethod))
httpUrlConn.connect();
// 當有資料需要提交時
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
// 注意編碼格式,防止中文亂碼
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 將返回的輸入流轉換成字串
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 釋放資源
inputStream.close();
inputStream = null;
httpUrlConn.disconnect();
jsonObject = JSONObject.fromObject(buffer.toString());
} catch (ConnectException ce) {
log.error("Weixin server connection timed out.");
} catch (Exception e) {
log.error("https request error:{}", e);
}
return jsonObject;
}
}
程式碼說明:
1)41~50行:解決https請求的問題,很多人問題就出在這裡;
2)55~59行:相容GET、POST兩種方式;
3)61~67行:相容有資料提交、無資料提交兩種情況,也有相當一部分人不知道如何POST提交資料;
Pojo類的封裝
在獲取憑證建立選單前,我們還需要封裝一些pojo,這會讓我們的程式碼更美觀,有條理。
首先是呼叫獲取憑證介面後,微信伺服器會返回json格式的資料:{"access_token":"ACCESS_TOKEN","expires_in":7200},我們將其封裝為一個AccessToken物件,物件有二個屬性:token和expiresIn,程式碼如下:
package org.liufeng.weixin.pojo;
/**
* 微信通用介面憑證
*
* @author liufeng
* @date 2013-08-08
*/
public class AccessToken {
// 獲取到的憑證
private String token;
// 憑證有效時間,單位:秒
private int expiresIn;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public int getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}
}
接下來是對選單結構的封裝。因為我們是採用面向物件的程式設計方式,最終提交的json格式選單資料就應該是由物件直接轉換得到,而不是在程式程式碼中拼一大堆json資料。選單結構封裝的依據是公眾平臺API文件中給出的那一段json格式的選單結構,如下所示:
{
"button":[
{
"type":"click",
"name":"今日歌曲",
"key":"V1001_TODAY_MUSIC"
},
{
"type":"click",
"name":"歌手簡介",
"key":"V1001_TODAY_SINGER"
},
{
"name":"選單",
"sub_button":[
{
"type":"click",
"name":"hello word",
"key":"V1001_HELLO_WORLD"
},
{
"type":"click",
"name":"贊一下我們",
"key":"V1001_GOOD"
}]
}]
}
首先是選單項的基類,所有一級選單、二級選單都共有一個相同的屬性,那就是name。選單項基類的封裝程式碼如下:
package org.liufeng.weixin.pojo;
/**
* 按鈕的基類
*
* @author liufeng
* @date 2013-08-08
*/
public class Button {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
接著是子選單項的封裝。這裡對子選單是這樣定義的:沒有子選單的選單項,有可能是二級選單項,也有可能是不含二級選單的一級選單。這類子選單項一定會包含三個屬性:type、name和key,封裝的程式碼如下:
package org.liufeng.weixin.pojo;
/**
* 普通按鈕(子按鈕)
*
* @author liufeng
* @date 2013-08-08
*/
public class CommonButton extends Button {
private String type;
private String key;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
再往下是父選單項的封裝。對父選單項的定義:包含有二級選單項的一級選單。這類選單項包含有二個屬性:name和sub_button,而sub_button以是一個子選單項陣列。父選單項的封裝程式碼如下:
package org.liufeng.weixin.pojo;
/**
* 複雜按鈕(父按鈕)
*
* @author liufeng
* @date 2013-08-08
*/
public class ComplexButton extends Button {
private Button[] sub_button;
public Button[] getSub_button() {
return sub_button;
}
public void setSub_button(Button[] sub_button) {
this.sub_button = sub_button;
}
}
最後是整個選單物件的封裝,選單物件包含多個選單項(最多隻能有3個),這些選單項即可以是子選單項(不含二級選單的一級選單),也可以是父選單項(包含二級選單的選單項),如果能明白上面所講的,再來看封裝後的程式碼就很容易理解了:
package org.liufeng.weixin.pojo;
/**
* 選單
*
* @author liufeng
* @date 2013-08-08
*/
public class Menu {
private Button[] button;
public Button[] getButton() {
return button;
}
public void setButton(Button[] button) {
this.button = button;
}
}
關於POJO類的封裝就介紹完了。
憑證access_token的獲取方法
繼續在先前通用請求方法的類WeixinUtil.java中加入以下程式碼,用於獲取介面訪問憑證:
// 獲取access_token的介面地址(GET) 限200(次/天)
public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
/**
* 獲取access_token
*
* @param appid 憑證
* @param appsecret 金鑰
* @return
*/
public static AccessToken getAccessToken(String appid, String appsecret) {
AccessToken accessToken = null;
String requestUrl = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret);
JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
// 如果請求成功
if (null != jsonObject) {
try {
accessToken = new AccessToken();
accessToken.setToken(jsonObject.getString("access_token"));
accessToken.setExpiresIn(jsonObject.getInt("expires_in"));
} catch (JSONException e) {
accessToken = null;
// 獲取token失敗
log.error("獲取token失敗 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));
}
}
return accessToken;
}
自定義選單的建立方法
繼續在先前通用請求方法的類WeixinUtil.java中加入以下程式碼,用於建立自定義選單:
// 選單建立(POST) 限100(次/天)
public static String menu_create_url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
/**
* 建立選單
*
* @param menu 選單例項
* @param accessToken 有效的access_token
* @return 0表示成功,其他值表示失敗
*/
public static int createMenu(Menu menu, String accessToken) {
int result = 0;
// 拼裝建立選單的url
String url = menu_create_url.replace("ACCESS_TOKEN", accessToken);
// 將選單物件轉換成json字串
String jsonMenu = JSONObject.fromObject(menu).toString();
// 呼叫介面建立選單
JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);
if (null != jsonObject) {
if (0 != jsonObject.getInt("errcode")) {
result = jsonObject.getInt("errcode");
log.error("建立選單失敗 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));
}
}
return result;
}
呼叫封裝的方法建立自定義選單
package org.liufeng.weixin.main;
import org.liufeng.weixin.pojo.AccessToken;
import org.liufeng.weixin.pojo.Button;
import org.liufeng.weixin.pojo.CommonButton;
import org.liufeng.weixin.pojo.ComplexButton;
import org.liufeng.weixin.pojo.Menu;
import org.liufeng.weixin.util.WeixinUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 選單管理器類
*
* @author liufeng
* @date 2013-08-08
*/
public class MenuManager {
private static Logger log = LoggerFactory.getLogger(MenuManager.class);
public static void main(String[] args) {
// 第三方使用者唯一憑證
String appId = "000000000000000000";
// 第三方使用者唯一憑證金鑰
String appSecret = "00000000000000000000000000000000";
// 呼叫介面獲取access_token
AccessToken at = WeixinUtil.getAccessToken(appId, appSecret);
if (null != at) {
// 呼叫介面建立選單
int result = WeixinUtil.createMenu(getMenu(), at.getToken());
// 判斷選單建立結果
if (0 == result)
log.info("選單建立成功!");
else
log.info("選單建立失敗,錯誤碼:" + result);
}
}
/**
* 組裝選單資料
*
* @return
*/
private static Menu getMenu() {
CommonButton btn11 = new CommonButton();
btn11.setName("天氣預報");
btn11.setType("click");
btn11.setKey("11");
CommonButton btn12 = new CommonButton();
btn12.setName("公交查詢");
btn12.setType("click");
btn12.setKey("12");
CommonButton btn13 = new CommonButton();
btn13.setName("周邊搜尋");
btn13.setType("click");
btn13.setKey("13");
CommonButton btn14 = new CommonButton();
btn14.setName("歷史上的今天");
btn14.setType("click");
btn14.setKey("14");
CommonButton btn21 = new CommonButton();
btn21.setName("歌曲點播");
btn21.setType("click");
btn21.setKey("21");
CommonButton btn22 = new CommonButton();
btn22.setName("經典遊戲");
btn22.setType("click");
btn22.setKey("22");
CommonButton btn23 = new CommonButton();
btn23.setName("美女電臺");
btn23.setType("click");
btn23.setKey("23");
CommonButton btn24 = new CommonButton();
btn24.setName("人臉識別");
btn24.setType("click");
btn24.setKey("24");
CommonButton btn25 = new CommonButton();
btn25.setName("聊天嘮嗑");
btn25.setType("click");
btn25.setKey("25");
CommonButton btn31 = new CommonButton();
btn31.setName("Q友圈");
btn31.setType("click");
btn31.setKey("31");
CommonButton btn32 = new CommonButton();
btn32.setName("電影排行榜");
btn32.setType("click");
btn32.setKey("32");
CommonButton btn33 = new CommonButton();
btn33.setName("幽默笑話");
btn33.setType("click");
btn33.setKey("33");
ComplexButton mainBtn1 = new ComplexButton();
mainBtn1.setName("生活助手");
mainBtn1.setSub_button(new CommonButton[] { btn11, btn12, btn13, btn14 });
ComplexButton mainBtn2 = new ComplexButton();
mainBtn2.setName("休閒驛站");
mainBtn2.setSub_button(new CommonButton[] { btn21, btn22, btn23, btn24, btn25 });
ComplexButton mainBtn3 = new ComplexButton();
mainBtn3.setName("更多體驗");
mainBtn3.setSub_button(new CommonButton[] { btn31, btn32, btn33 });
/**
* 這是公眾號xiaoqrobot目前的選單結構,每個一級選單都有二級選單項<br>
*
* 在某個一級選單下沒有二級選單的情況,menu該如何定義呢?<br>
* 比如,第三個一級選單項不是“更多體驗”,而直接是“幽默笑話”,那麼menu應該這樣定義:<br>
* menu.setButton(new Button[] { mainBtn1, mainBtn2, btn33 });
*/
Menu menu = new Menu();
menu.setButton(new Button[] { mainBtn1, mainBtn2, mainBtn3 });
return menu;
}
}
注意:在執行以上程式碼時,需要將appId和appSecret換成你自己公眾號的。
整個工程的結構
為了保證文章的完整獨立性和可讀性,我是新建了一個Java Project(Java web工程也可以,沒有太大關係),沒有在前幾篇文章所講到的weixinCourse工程中新增程式碼。如果需要,讀者可以自己實現將選單建立的程式碼移到自己已有的工程中去。
圖中所有Java檔案的原始碼都在文章中貼出並進行了說明,圖中使用到的jar也是Java開發中通用的jar包,很容易在網上下載到。
工程中引入的jar包主要分為兩類:
1)第一類是json開發工具包,用於Java物件和Json字串之間的轉換;json開發工具包一共有3個jar:ezmorph-1.0.6.jar,json-lib-2.2.3-jdk13.jar和morph-1.1.1.jar。
2)第二類是slf4j日誌工具包,用於記錄系統執行所產生的日誌,日誌可以輸出到控制檯或檔案中。
整個工程中,唯一沒有講到的是src下的log4j.properties的配置,也把它貼出來,方便大家參考,這樣才是一個完整的工程原始碼。log4j.properties檔案的內容如下:
log4j.rootLogger=info,console,file
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-5p] %m%n
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.DatePattern='-'yyyy-MM-dd
log4j.appender.file.File=./logs/weixinmpmenu.log
log4j.appender.file.Append=true
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%-5p] %d %37c %3x - %m%n
如何響應選單點選事件
自定義選單的建立工作已經完成,那麼該如何接收和響應選單的點選事件呢,也就是說在公眾帳號後臺處理程式中,如何識別使用者點選的是哪個選單,以及做出響應。這部分內容其實在教程的第5篇各種訊息的接收與響應中已經講解清楚了。
來看一下第一篇教程weixinCourse專案中的CoreService類要怎麼改寫,才能接收響應選單點選事件,該類修改後的完整程式碼如下:
package org.liufeng.course.service;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.liufeng.course.message.resp.TextMessage;
import org.liufeng.course.util.MessageUtil;
/**
* 核心服務類
*
* @author liufeng
* @date 2013-05-20
*/
public class CoreService {
/**
* 處理微信發來的請求
*
* @param request
* @return
*/
public static String processRequest(HttpServletRequest request) {
String respMessage = null;
try {
// 預設返回的文字訊息內容
String respContent = "請求處理異常,請稍候嘗試!";
// xml請求解析
Map<String, String> requestMap = MessageUtil.parseXml(request);
// 傳送方帳號(open_id)
String fromUserName = requestMap.get("FromUserName");
// 公眾帳號
String toUserName = requestMap.get("ToUserName");
// 訊息型別
String msgType = requestMap.get("MsgType");
// 回覆文字訊息
TextMessage textMessage = new TextMessage();
textMessage.setToUserName(fromUserName);
textMessage.setFromUserName(toUserName);
textMessage.setCreateTime(new Date().getTime());
textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
textMessage.setFuncFlag(0);
// 文字訊息
if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
respContent = "您傳送的是文字訊息!";
}
// 圖片訊息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) {
respContent = "您傳送的是圖片訊息!";
}
// 地理位置訊息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)) {
respContent = "您傳送的是地理位置訊息!";
}
// 連結訊息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) {
respContent = "您傳送的是連結訊息!";
}
// 音訊訊息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) {
respContent = "您傳送的是音訊訊息!";
}
// 事件推送
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
// 事件型別
String eventType = requestMap.get("Event");
// 訂閱
if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {
respContent = "謝謝您的關注!";
}
// 取消訂閱
else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {
// TODO 取消訂閱後用戶再收不到公眾號傳送的訊息,因此不需要回復訊息
}
// 自定義選單點選事件
else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {
// 事件KEY值,與建立自定義選單時指定的KEY值對應
String eventKey = requestMap.get("EventKey");
if (eventKey.equals("11")) {
respContent = "天氣預報選單項被點選!";
} else if (eventKey.equals("12")) {
respContent = "公交查詢選單項被點選!";
} else if (eventKey.equals("13")) {
respContent = "周邊搜尋選單項被點選!";
} else if (eventKey.equals("14")) {
respContent = "歷史上的今天選單項被點選!";
} else if (eventKey.equals("21")) {
respContent = "歌曲點播選單項被點選!";
} else if (eventKey.equals("22")) {
respContent = "經典遊戲選單項被點選!";
} else if (eventKey.equals("23")) {
respContent = "美女電臺選單項被點選!";
} else if (eventKey.equals("24")) {
respContent = "人臉識別選單項被點選!";
} else if (eventKey.equals("25")) {
respContent = "聊天嘮嗑選單項被點選!";
} else if (eventKey.equals("31")) {
respContent = "Q友圈選單項被點選!";
} else if (eventKey.equals("32")) {
respContent = "電影排行榜選單項被點選!";
} else if (eventKey.equals("33")) {
respContent = "幽默笑話選單項被點選!";
}
}
}
textMessage.setContent(respContent);
respMessage = MessageUtil.textMessageToXml(textMessage);
} catch (Exception e) {
e.printStackTrace();
}
return respMessage;
}
}
程式碼說明:
1)第69行、第81行這兩行程式碼說明了如何判斷選單的點選事件。當訊息型別MsgType=event,並且Event=CLICK時,就表示是自定義選單點選事件;
2)第83行是判斷具體點選的是哪個選單項,根據選單的key值來判斷;
3)第85~109行表示當用戶點選某個選單項後,具體返回什麼訊息,我只是做個簡單示例,統一返回文字訊息,讀者可以根據實際需要來靈活處理。
總結
到這裡關於自定義選單的建立、選單事件的判斷和處理響應就全部介紹完了。我只希望看過文章的人不要只是拷貝程式碼,如果是這樣,我完全不用花這麼多的時間來寫這篇文章,直接把工程放在下載區多簡單。另外,網上是有很多工具,讓你填入appid,appsecret和選單結構,提交就能建立選單,請慎用!因為appid和appsecret一旦告訴別人,你的公眾號的選單控制權就在別人手上了,總會有別有用心的人出來搞點事的。
如果覺得文章對你有所幫助,請通過留言或關注微信公眾帳號xiaoqrobot來支援柳峰!