這篇文章轉自《http://www.qtdebug.com/spring-weixin/》
微信有兩種模式,編輯模式和開發者模式,有些功能是互斥的,不可以同時使用,微信開發需要在開發者模式下進行(開發者模式下仍然可以去微信的網頁上群發消息)。下面介紹的功能能滿足大部分的需求,響應文本消息,圖文消息,創建菜單,響應菜單消息等。
我們給微信提供服務有兩種消息模式,被動和主動
- 被動: 例如用戶輸入文本,點擊菜單,微信服務器會訪問我們的 Web 服務對應的 URL,我們返回對應的消息給微信服務器
 - 主動: 例如創建菜單,群發消息,這種模式需要我們主動去觸發,給微信服務器發送消息,可以是執行某個定時任務觸發,或者我們訪問某個 URL 然后在其響應的代碼里觸發
 -  
幾個關鍵點
- 微信服務器和我們的服務器綁定驗證時使用 GET 發送一個帶有 
echostr參數的請求 - 其他消息使用的是 POST,常用的消息有 
            
- 事件消息 event: subscribe, unsubscribe, location 等
 - 文本消息 text: 可以回復文本,鏈接,圖文
 - 點擊菜單回復的 click
 - 點擊菜單跳轉為 view
 
 - 微信服務器訪問我們的服務器的 URL 只有一個,就是在配置頁中配置的 URL
 - 使用 app_id + app_secret 從微信服務器獲取 access_token,有效時間是 7200 秒
 - 微信的接口:用於我們主動的從微信服務器獲取信息或者主動的向微信服務器寫入信息
 
准備外網能訪問的域名
- 使用 ngrok.cc 讓本地服務能在外網訪問,這樣微信服務器才能訪問到我們的 Web 服務器
 
准備公眾號
- 訪問 https://mp.weixin.qq.com 注冊開發者賬號
 - 登陸后到最下面 
開發 > 基本配置中啟用開發者模式,並點擊修改配置配置我們自己給微信提供服務的 Web 地址、Token 等,然后使用appID和appsecret就可以進行開發了- 如果我們申請的是 
個人訂閱號,很多功能接口都沒有,例如自定義菜單都沒有,為了使用所有的功能進行開發練習,可以使用微信提供的公眾平台測試帳號:開發 > 開發者工具 > 公眾平台測試帳號 > 進入,就可以看到appID和appsecret,也需要在這里配置給微信提供服務的 Web 地址,Token 等,然后在頁面中部找到請用微信掃描關注測試公眾號,掃描關注即可 - 查看公眾號接口權限說明: 
開發 > 開發者工具 > 開發者文檔 > 進入 > 公眾號接口權限說明 個人訂閱號是不能進行認證的-  
Gradle 依賴
1compile( "com.github.sd4324530:fastweixin:1.3.11")Fastweixin 的 git: https://git.oschina.net/pyinjava/fastweixin
 -  
給微信提供服務的 Controller
1public class WeixinController extends WeixinControllerSupport消息響應
在 WeixinController 中重載下面幾個函數
-  
響應訂閱消息
1protected BaseMsg handleSubscribe(BaseEvent event) -  
響應文本消息
1protected BaseMsg handleTextMsg(TextReqMsg msg) -  
響應點擊菜單消息
1protected BaseMsg handleMenuClickEvent(MenuEvent event) 
創建消息
-  
創建文本消息
1new TextMsg("你好: <a href=\"http://www.baidu.com\">百度</a>"); -  
創建圖文消息(單圖文,多圖文都可以)
1234String picUrl = "http://image.17car.com.cn/image/20120810/20120810092133_13289.jpg"; // 消息中顯示的圖片String url = "http://news.17car.com.cn/saishi/20120810/336283.html"; // 點擊消息后跳轉的網頁的地址String description = "700 馬力道路賽車 DDMWorks 打造最強 Atom";NewsMsg msg = new NewsMsg(Arrays.asList(new Article("Atom", description, picUrl, url), new Article("Atom", description, picUrl, url))); 
創建菜單
 -  
 -  
創建菜單
- 微信不會訪問這個URL,需要我們自己訪問創建菜單的 URL,然后才能向微信發送創建菜單信息。
 - 微信只能保證菜單 24 小時之類生效,想要馬上看到菜單效果,先取消關注,然后再次關注就可以了
 CLICK類型的菜單會發送消息到我們的服務器,handleMenuClickEvent() 進行響應,根據 key 來判斷是哪個菜單被點擊VIEW類型的菜單不回發送消息到我們的服務器,而是直接跳轉到對應的 URL- 菜單分一級菜單和二級菜單 
                 
- 最多有 3 個一級菜單,5 個二級菜單
 - 一級菜單最多 4 個漢字,二級菜單最多 7 個漢字
 
 
123456789101112131415161718192021222324252627282930313233343536373839public String createMenu() {// 准備一級主菜單MenuButton main1 = new MenuButton();main1.setType(MenuType.CLICK); // 可點擊的菜單main1.setKey( "main1");main1.setName( "主菜單一");MenuButton main2 = new MenuButton();main2.setType(MenuType.VIEW); // 鏈接的菜單,點擊后跳轉到對應的 URLmain2.setName( "主菜單二");main2.setUrl( "http://www.baidu.com");MenuButton main3 = new MenuButton();main3.setType(MenuType.CLICK);main3.setName( "真題");// 帶有子菜單MenuButton sub1 = new MenuButton();sub1.setType(MenuType.CLICK); // 帶有子菜單sub1.setName( "2016 語文");sub1.setKey( "sub1");MenuButton sub2 = new MenuButton();sub2.setType(MenuType.CLICK);sub2.setName( "2016 數學");sub2.setKey( "sub2");main3.setSubButton(Arrays.asList(sub1, sub2));Menu menu = new Menu();menu.setButton(Arrays.asList(main1, main2, main3));//創建菜單ApiConfig config = new ApiConfig(APP_ID, APP_SECRET);MenuAPI menuAPI = new MenuAPI(config);ResultType resultType = menuAPI.createMenu(menu);return resultType.toString();}使用 Json 字符串創建菜單
上面的程序創建菜單太麻煩了,可以使用 Json 字符串,然后反序列化為菜單對象,下面使用 Jackson 來實現。
Jackson 依賴:
compile("com.fasterxml.jackson.core:jackson-databind:2.7.4")菜單的 Json 字符串可以放在文件,數據庫中等,方便修改,而且比使用對象的方式更直觀,例如下面這樣:
1234567891011121314151617181920212223{"button": [{"type": "CLICK","name": "主菜單一","key": "main1"}, {"type": "VIEW","name": "主菜單二","url": "http://www.baidu.com"}, {"type": "CLICK","name": "真題","subButton": [{"type": "CLICK","name": "2016 語文","key": "sub1"}, {"type": "CLICK","name": "2016 數學","key": "sub2"}]}]}1234567891011121314151617181920212223242526272829303132333435public String createMenu() throws Exception {String json = "{\n" +" \"button\": [{\n" +" \"type\": \"CLICK\",\n" +" \"name\": \"今日歌曲\",\n" +" \"key\": \"V1001_TODAY_MUSIC\"\n" +" }, {\n" +" \"name\": \"菜單\",\n" +" \"subButton\": [{\n" +" \"type\": \"VIEW\",\n" +" \"name\": \"搜索\",\n" +" \"url\": \"http://www.soso.com/\"\n" +" }, {\n" +" \"type\": \"VIEW\",\n" +" \"name\": \"視頻\",\n" +" \"url\": \"http://v.qq.com/\"\n" +" }, {\n" +" \"type\": \"CLICK\",\n" +" \"name\": \"贊一下我們\",\n" +" \"key\": \"V1001_GOOD\"\n" +" }]\n" +" }]\n" +"}";ObjectMapper mapper = new ObjectMapper();Menu menu = mapper.readValue(json, Menu.class);//創建菜單ApiConfig config = new ApiConfig(APP_ID, APP_SECRET);MenuAPI menuAPI = new MenuAPI(config);ResultType resultType = menuAPI.createMenu(menu);return resultType.toString();}示例程序
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122package com.xtuer.controller;import com.github.sd4324530.fastweixin.api.MenuAPI;import com.github.sd4324530.fastweixin.api.config.ApiConfig;import com.github.sd4324530.fastweixin.api.entity.Menu;import com.github.sd4324530.fastweixin.api.entity.MenuButton;import com.github.sd4324530.fastweixin.api.enums.MenuType;import com.github.sd4324530.fastweixin.api.enums.ResultType;import com.github.sd4324530.fastweixin.message.Article;import com.github.sd4324530.fastweixin.message.BaseMsg;import com.github.sd4324530.fastweixin.message.NewsMsg;import com.github.sd4324530.fastweixin.message.TextMsg;import com.github.sd4324530.fastweixin.message.req.MenuEvent;import com.github.sd4324530.fastweixin.message.req.TextReqMsg;import com.github.sd4324530.fastweixin.servlet.WeixinControllerSupport;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.bind.annotation.RestController;import java.util.Arrays;public class WeixinController extends WeixinControllerSupport {private static final Logger LOG = LoggerFactory.getLogger(WeixinController.class);private static final String TOKEN = "xxxxxx"; // 你的 tokenprivate static final String APP_ID = "yyyyyy"; // 你的 appIDprivate static final String APP_SECRET = "zzzzzz"; // 你的 appsecret// 重寫父類方法,處理對應的微信消息protected BaseMsg handleTextMsg(TextReqMsg msg) {String content = msg.getContent(); // content 是用戶輸入的信息switch (content.toUpperCase()) {case "URL":// 消息中有鏈接return new TextMsg("你好: <a href=\"http://www.baidu.com\">百度</a>");case "ATOM":// 圖文消息String picUrl = "http://image.17car.com.cn/image/20120810/20120810092133_13289.jpg"; // 消息中顯示的圖片String url = "http://news.17car.com.cn/saishi/20120810/336283.html"; // 點擊消息后跳轉的網頁的地址String description = "700 馬力道路賽車 DDMWorks 打造最強 Atom";return new NewsMsg(Arrays.asList(new Article("Atom", description, picUrl, url), new Article("Atom", description, picUrl, url)));default:return new TextMsg("不識別的命令, 您輸入的內容是: " + content);}}protected BaseMsg handleMenuClickEvent(MenuEvent event) {String key = event.getEventKey();switch (key.toUpperCase()) {case "MAIN1":return new TextMsg("點擊按鈕");case "SUB1":return new TextMsg("2016 語文");case "SUB2":return new TextMsg("2016 數學");default:return new TextMsg("不識別的菜單命令");}}// 設置 TOKEN,用於綁定微信服務器protected String getToken() {return TOKEN;}// 獲取 access token: http://localhost:8080/weixin/access-tokenpublic String getAccessToken() {ApiConfig config = new ApiConfig(APP_ID, APP_SECRET);return config.getAccessToken();}// 創建菜單, 訪問 http://localhost:8080/weixincreate-menu 就會把菜單信息發送給微信服務器public String createMenu() {// 准備一級主菜單MenuButton main1 = new MenuButton();main1.setType(MenuType.CLICK); // 可點擊的菜單main1.setKey( "main1");main1.setName( "主菜單一");MenuButton main2 = new MenuButton();main2.setType(MenuType.VIEW); // 鏈接的菜單,點擊后跳轉到對應的 URLmain2.setName( "主菜單二");main2.setUrl( "http://www.baidu.com");MenuButton main3 = new MenuButton();main3.setType(MenuType.CLICK); // 帶有子菜單main3.setName( "真題");// 帶有子菜單MenuButton sub1 = new MenuButton();sub1.setType(MenuType.CLICK);sub1.setName( "2016 語文");sub1.setKey( "sub1");MenuButton sub2 = new MenuButton();sub2.setType(MenuType.CLICK);sub2.setName( "2016 數學");sub2.setKey( "sub2");main3.setSubButton(Arrays.asList(sub1, sub2));Menu menu = new Menu();menu.setButton(Arrays.asList(main1, main2, main3));//創建菜單ApiConfig config = new ApiConfig(APP_ID, APP_SECRET);MenuAPI menuAPI = new MenuAPI(config);ResultType resultType = menuAPI.createMenu(menu);return resultType.toString();}} 
 - 如果我們申請的是 
 
 - 微信服務器和我們的服務器綁定驗證時使用 GET 發送一個帶有 
 
