WeChat 5.0がリリースされました
2013 年 8 月 5 日、WeChat 5.0 iPhone バージョンのリリースに伴い、パブリック プラットフォームにも次のような重要なアップデートが行われました。
1) 運営主体は組織であり、サービス アカウントになるかサブスクリプション アカウントになるかを選択できます。2) サービス アカウントはカスタマイズされたメニューを適用できます。
3) QQ を使用してログインする公式アカウントは、電子メール ログインにアップグレードできます。4) ログインに電子メールを使用する公開アカウントの場合、ログイン電子メールを変更できます。
5) グラフィックメッセージを編集するときに、オプションで作成者を入力できます。6) グループメッセージは Tencent Weibo に同期できます。
その中で、最初の 2 つは、アカウント タイプとカスタム メニューの更新に関するもので、ここでさらに説明します。
1) 現在、パブリック アカウントにはサービス アカウントとサブスクリプション アカウントの 2 種類があります。8 月 5 日のプラットフォームの更新後は、すべてのアカウントがデフォルトでサブスクリプション アカウントになり、サービス アカウントに変換することができます。2) サービス アカウントは主に企業、政府、その他の組織を対象としていますが、サブスクリプション アカウントは主にメディアや個人を対象としています。
3) カスタム メニューに適用できるのはサービス アカウントのみです。サブスクリプション アカウントは適用できません。4) サービス アカウントは月に 1 つのメッセージしか送信できませんが、サブスクリプション アカウントは 1 日に 1 つのメッセージを送信できます。
プラットフォームが更新された後、多くの人がメニューのカスタマイズと毎日のグループメッセージの送信の間の非互換性に苦労しました。これについてはあまりコメントしたくありません。
はじめにと概要
WeChat 5.0 が登場する前は、カスタム メニューが内部テストの資格として使用されていたのは、メニューを持っている公開アカウントがわずかしかなかったため、多くの企業がメニューを取得するために多額の費用を費やしていました。現在、多くのアカウントがサブスクリプションアカウントからサービスアカウントに移行しており、その多くがメニューのカスタマイズを急いでいます。さらに、テストの結果、WeChat の最近の審査は大幅に緩和されていることが判明しました。サービス アカウントを申請し、メニューをカスタマイズしている限り、入力された情報の信頼性に関係なく、基本的に成功します。将来的にはWeChatが崩壊して会社情報の完成を求めるようになるのだろうか。それは子供にキャンディーを与えて殴って泣くようなものだ。 。 。
カスタムメニューの作成方法と使い方は?ここ数日、公式 WeChat コミュニケーション グループでも、私のブログのコメントでも、多くの開発者がこのことを心配していることがわかります。この記事では、この問題をすべての人に解決します。
カスタムメニューを作成する手順
1. AppId と AppSecret を見つけます。カスタム メニューの適用が成功すると、「高度な機能」-「開発モード」-「インターフェース構成情報」の最後の 2 つの項目が次のようになります。
2. AppId と AppSecret に従って、https get メソッドを使用して、特別なインターフェイスにアクセスするために必要な認証情報を取得します。3. access_tokenに従って、https postを通じてメニューデータをjson形式で送信します。
メニュー作成の難しさを分析する
メニューの作成はとても簡単で、3 つのステップで完了できることがわかりましたか?象を冷蔵庫に入れるようなものです。ははは、もちろんそれほど単純ではありません。では、何が難しいのかを順を追って見てみましょう。
まず、ステップ 1 はまったく問題ありません。カスタム メニューの申請に成功している限り、AppId と AppSecret の値は確実に取得されます。
ステップ 2 をもう一度見てみましょう。access_token は get メソッドで取得されるため、多くの人は組み立てた URL をブラウザに直接入れて実行し、access_token を取得します。プログラム的に実装されているかどうかに関係なく、これは非常に良いアイデアであり、明らかに誰もが 2 番目のステップに問題はありません。
最後に、ステップ3のメニューデータをjson形式で組み立てます。少し複雑ですが、公式が例を示しているので、基本的には猫と虎をコピーするだけで問題ありません。この問題は https ポスト送信で発生する必要があります。
結論: カスタム メニューの作成方法を知らない友人は、主に次の 3 つの状況に当てはまる可能性があります:
1) パブリック プラットフォーム API ドキュメントの「一般インターフェイス」、「カスタマイズされたメニュー インターフェイス」、および「使用制限」の説明を読んでいない、または理解していません。
2) HTTPS リクエストを開始する方法がわかりません (通常の http リクエストは HttpUrlConnection を使用して直接処理できますが、https リクエストは少し複雑です)。3) POSTでメニューデータをJSON形式で送信する方法がわかりません。
記事を読んでいて、どれに該当するかわからない場合、または複数の状況がある場合は、メッセージを残すか、アンケートを行ってください。どのような状況であっても、この記事を読んでいただければ、それを理解するのに役立つと思います。
共通インターフェース文書の解釈---認証情報の取得
まず、以下の図に示すように、一般的なインターフェースドキュメントの導入部分を見てみましょう。
平たく言えば、この導入は次のように理解できます。パブリック プラットフォームには、カスタム メニューの作成、音声ファイルの取得、メッセージのプロアクティブな送信など、多くの特別なインターフェイスもあります。開発者がこれらの特別なインターフェイスにアクセスしたい場合は、 HTTP リクエストを介してアクセスするには、access_token というアクセス資格情報が必要です。
それでは、インターフェイスのアクセス認証情報 access_token を取得するにはどうすればよいでしょうか?次へ移りましょう。
この図は、access_token を取得するには、GET を通じて次のリンクにアクセスする必要があることを示しています。 リーリー
リンクには、grant_type、appid、secret という 3 つのパラメータがあります。図のパラメータの説明によると、grant_type には固定値 client_credential が渡され、appid と Secret はカスタム メニューの申請後に WeChat によって割り当てられます。
リクエストが正常に送信されると、WeChat サーバーは、access_token とexpires_in の 2 つの要素を含む json 文字列を返します。このうち、access_token は最終的に必要な証明書で、expires_in は証明書の有効期間 (秒単位) です。7200 秒は 2 時間です。つまり、特別なインターフェイスにアクセスするたびに、access_token を再度取得する必要がなく、access_token が有効である限り、いつでも使用できます。
カスタムメニューインターフェースドキュメントを解釈する
同様に、下の図に示すように、カスタム メニュー インターフェイスの導入を見てみましょう。
写真から次の情報を取得できます:
1) 認証情報 access_token を取得したら、メニューで作成、クエリ、削除の 3 つの操作を実行できます。
2) カスタム メニューは現在クリック イベントのみをサポートしています。つまり、ユーザーはクリック後に特定の種類のメッセージに応答します。メニュー項目をクリックしてページを直接開くことはできません (type=view は開きません。現在はマイクロライフでのみ利用可能です);
3) WeChatクライアントのキャッシュのため、メニューは作成後すぐにWeChatに表示されず、24時間かかります。テストメニューを作成する場合は、フォローを解除して再度フォローするとすぐにメニューが表示されます。
下の図に示すように、メニューの作成方法の紹介については、以下を読み続けてください。
実際には、JSON 形式のメニュー文字列をアドレス https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN に POST モードで送信します。
後で、パラメータの説明を画像を使って一つずつ説明するのではなく、重要なポイントについてのみ説明します。1) カスタム メニューは 3x5 構造です。つまり、メニューには最大 2 番目のレベル、最大 3 つの第 1 レベルのメニュー、および各第 1 レベルのメニューの下に最大 5 つの第 2 レベルのメニュー項目しか含めることができません。 ;
2) 各メニュー項目にはキー値があります。ユーザーがメニュー項目をクリックすると、WeChat はメニュー項目のキー値をイベント プッシュの形式でバックグラウンド ハンドラーに送信します。
クエリとメニューの作成については説明しません。これら 2 つのインターフェイスはめったに使用されず、通常は使用されません。必要に応じて、上で示したアイデアに従って理解することは難しくありません。
APIドキュメントの使用制限を解釈する
この写真を見た多くの友人は、ユーザー数が増えているのに、なぜメニューの使用回数も制限するのかと疑問に思い始めました。この制限はインターフェイス呼び出し、つまり開発者に対するものであり、ユーザーの数や使用回数とは関係がないことを明確にしてください。
証明書取得インターフェイスを例に挙げてみましょう。呼び出しは 1 日あたり 200 件に制限されています。 access_token には有効期間があると前述しましたが、有効期間は 2 時間です。つまり、access_token を取得してから 2 時間以内は引き続き使用できるため、理想的には 1 日 24 時間以内だけが必要になります。 12回取得すれば十分ですか? 200回じゃ足りませんか?
1 日に 100 回しか呼び出すことができないメニュー作成インターフェイスの制限を見てみましょう。このように説明すると、一度メニューを作成した後は、モードを切り替えたり (編集モードと開発モードの切り替えを指します)、削除インターフェイスを呼び出したりしない限り、このメニューは永久に存在します。何もすることがなくて1日100回メニューを作成しなければならない人はいますか? テストだからといって、10回と8回のテストで十分ですよね?
メニューのクエリと削除のインターフェイスの制限については説明しません。これら 2 つのインターフェイスはこれまで使用したことがありません。たとえそのような需要があったとしても、一日に何度も電話をかければ十分です。
一般的なリクエストメソッドをカプセル化します
これを読んだ後は、誰もが上記のカスタム メニューに関する理論的な知識をすべて習得していることを前提としています。次に、コードの実践的な説明に入ります。
メニューを作成するには 2 つのインターフェイスを呼び出す必要があり、どちらも http ではなく https リクエストであることを以前に学びました。一般的なリクエスト メソッドをカプセル化したい場合、メソッドには少なくとも次の機能が必要です:
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; }
调用封装的方法创建自定义菜单
// 菜单创建(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; }
注意:在运行以上代码时,需要将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一旦告诉别人,你的公众号的菜单控制权就在别人手上了,总会有别有用心的人出来搞点事的。