本框架基於Spring Boot實現,使用註解完成快速開發,能夠快速的完成一個微信公衆號,從新定義公衆號開發。html
在使用本框架前建議對微信公衆號開發文檔有所瞭解,不過在不瞭解公衆號文檔的狀況下使用本框架也能完成一個簡單的微信公衆號。java
目前已兼容Spring Boot 1.4+以及Spring Boot 2.x,歡迎你們提issue和contribute,也歡迎加羣627254793討論,開源項目須要你們共同來共享,謝謝~web
1、簡單示例:正則表達式
1. 申請測試公衆號spring
微信測試公衆號申請連接數據庫
2. 準備完成,建立maven項目並加入maven依賴apache
<!-- 支持1.4.0.RELEASE及以上,包括2.x -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
<dependencies>
<!-- fastbootWeixin的核心依賴 -->
<dependency>
<groupId>com.mxixm</groupId>
<artifactId>fastboot-weixin</artifactId>
<version>0.6.2</version>
</dependency>
<!-- SpringBoot的web項目,必須 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 暫時只能使用apache的http,後續可加入其它http支持 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>
3. 編寫示例代碼api
在resource目錄下新建配置文件application.properties或者其餘spring boot支持的配置文件類型,加入配置:服務器
測試代碼:微信
package com.mxixm.fastboot.weixin;
import com.mxixm.fastboot.weixin.annotation.WxApplication;
import com.mxixm.fastboot.weixin.annotation.WxAsyncMessage;
import com.mxixm.fastboot.weixin.annotation.WxButton;
import com.mxixm.fastboot.weixin.module.web.WxRequest;
import com.mxixm.fastboot.weixin.module.event.WxEvent;
import com.mxixm.fastboot.weixin.module.message.WxMessage;
import com.mxixm.fastboot.weixin.module.message.WxMessageBody;
import com.mxixm.fastboot.weixin.module.user.WxUser;
import com.mxixm.fastboot.weixin.mvc.annotation.WxController;
import com.mxixm.fastboot.weixin.mvc.annotation.WxEventMapping;
import com.mxixm.fastboot.weixin.mvc.annotation.WxMessageMapping;
import org.springframework.boot.SpringApplication;
@WxApplication
@WxController
public class WxApp {
public static void main(String[] args) throws Exception {
SpringApplication.run(WxApp.class, args);
}
/**
* 定義微信菜單
*/
@WxButton(group = WxButton.Group.LEFT, main = true, name = "左")
public void left() {
}
/**
* 定義微信菜單
*/
@WxButton(group = WxButton.Group.RIGHT, main = true, name = "右")
public void right() {
}
/**
* 定義微信菜單,並接受事件
*/
@WxButton(type = WxButton.Type.CLICK,
group = WxButton.Group.LEFT,
order = WxButton.Order.FIRST,
name = "文本消息")
public String leftFirst(WxRequest wxRequest, WxUser wxUser) {
return "測試文本消息";
}
/**
* 定義微信菜單,並接受事件
*/
@WxButton(type = WxButton.Type.VIEW,
group = WxButton.Group.LEFT,
order = WxButton.Order.SECOND,
url = "http://baidu.com",
name = "點擊連接")
@WxAsyncMessage
public WxMessage link() {
return WxMessage.newsBuilder().addItem("測試圖文消息", "測試", "https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white.png", "http://baidu.com").build();
}
/**
* 接受微信事件
* @param wxRequest
* @param wxUser
*/
@WxEventMapping(type = WxEvent.Type.UNSUBSCRIBE)
public void unsubscribe(WxRequest wxRequest, WxUser wxUser) {
System.out.println(wxUser.getNickName() + "退訂了公衆號");
}
/**
* 接受用戶文本消息,異步返回文本消息
* @param content
* @return the result
*/
@WxMessageMapping(type = WxMessage.Type.TEXT)
@WxAsyncMessage
public String text(WxRequest wxRequest, String content) {
WxSession wxSession = wxRequest.getWxSession();
if (wxSession != null && wxSession.getAttribute("last") != null) {
return "上次收到消息內容爲" + wxSession.getAttribute("last");
}
return "收到消息內容爲" + content;
}
/**
* 接受用戶文本消息,同步返回圖文消息
* @param content
* @return the result
*/
@WxMessageMapping(type = WxMessage.Type.TEXT, wildcard = "1*")
public WxMessage message(WxSession wxSession, String content) {
wxSession.setAttribute("last", content);
return WxMessage.newsBuilder()
.addItem(WxMessageBody.News.Item.builder().title(content).description("隨便一點")
.picUrl("http://k2.jsqq.net/uploads/allimg/1702/7_170225142233_1.png")
.url("http://baidu.com").build())
.addItem(WxMessageBody.News.Item.builder().title("第二條").description("隨便二點")
.picUrl("http://k2.jsqq.net/uploads/allimg/1702/7_170225142233_1.png")
.url("http://baidu.com").build())
.build();
}
/**
* 接受用戶文本消息,異步返回文本消息
* @param content
* @return the result
*/
@WxMessageMapping(type = WxMessage.Type.TEXT, wildcard = "2*")
@WxAsyncMessage
public String text2(WxRequestBody.Text text, String content) {
boolean match = text.getContent().equals(content);
return "收到消息內容爲" + content + "!結果匹配!" + match;
}
}
4. 配置內網穿透
由於微信公衆號須要配置本身的服務器接口,測試時可直接使用本地進行測試,使用內網穿透能夠令微信公衆平臺訪問到你本身本地的服務器。
軟件可以使用ngrok或者natapp,使用方式請參考二者官方文檔。
啓動後生成的域名url地址能夠配置在wx.callback-url中,以便進行oauth2認證。受權回調頁面域名中也需配置上上面生成的url中的域名。
5. 配置測試公衆號
在測試公衆號的接口配置信息中填寫在第四步中生成的域名,token使用配置文件中的token,保存後,若是不出意外應該會驗證成功。若有問題請及時反饋。
2、示例圖解
菜單示例
消息示例
3、示例說明
上面的示例在啓動後,請關注本身的公衆號,此時公衆號的菜單應該是有兩個主菜單:左、右,同時左有兩個子菜單:文本消息、點擊連接。
在點擊文本消息菜單時,會收到文本消息,內容爲:測試文本消息。
在點擊第二個點擊連接時,會跳轉至百度,並收到一條圖文消息,標題是測試圖文消息。
給公衆號發送文本消息,消息內容不是1開頭時,會收到公衆號回覆內容:"收到消息內容爲" + 發送的內容。
給公衆號發送文本消息,消息內容是1開頭時,會收到圖文消息的回覆。
當有用戶退訂公衆號時,會在System.out中打印用戶暱稱 + "退訂了公衆號"
4、示例講解
註解@WxApplication用於聲明該應用爲微信application,並使用SpringApplication啓動。若已有SpringBoot環境,請在你的@SpringApplication類上加入註解@EnableWxMvc,效果同樣。能夠看源碼。
註解@WxController用於聲明該類爲微信Controller,只有聲明瞭這個註解,纔會綁定在微信服務器的請求映射中,不然該類會被忽略。
註解@WxButton(group = WxButton.Group.LEFT, main = true, name = "左")用於聲明一個按鈕箱,group表明分組,有左中右三個分組,分別對應微信的三個一級菜單。main爲boolean值,表明該菜單項是否爲一級菜單。name就是菜單名。
註解@WxButton(type = WxButton.Type.CLICK, group = WxButton.Group.LEFT, order = WxButton.Order.FIRST, name = "文本消息") 用於聲明左邊分組的子菜單,order表明順序,這裏是第一個。
public String leftFirst(WxRequest wxRequest, WxUser wxUser) { return "測試文本消息"; } 這裏有三個點:
@WxButton(type = WxButton.Type.VIEW, group = WxButton.Group.LEFT, order = WxButton.Order.SECOND, url = "http://baidu.com", name = "點擊連接") 該註解同上面,類型變爲View,具體內容可參考該枚舉註釋,或者公衆號文檔。注意每種菜單類型都有本身的限制,請參考文檔,如不知足條件啓動時就會發生異常。
註解@WxAsyncMessage代表異步回覆消息,參考客服消息,暫時未對多客服進行支持。
WxMessage.News.builder(),在WxMessage類中,有不一樣的靜態內部類,以及他們的builder,經過builder能夠方便的構造微信不一樣類型的消息,請參考被動回覆消息和客服消息。
註解@WxEventMapping(type = WxEvent.Type.UNSUBSCRIBE)綁定取消訂閱事件,當有用戶取消關注時,會進入這個註解下面的邏輯。還有一點要註解,WxEventMapping全部的回覆內容都會被異步發送給用戶。
註解@WxMessageMapping(type = WxMessage.Type.TEXT)表示綁定用戶發送的文本消息到下面的方法邏輯,public String text(String content) { return "收到消息內容爲" + content; }。content會自動被賦值爲用戶發送的文本內容。
註解@WxMessageMapping(type = WxMessage.Type.TEXT, wildcard = "1*")與上面相同,不一樣的是wildcard通配符,該通配符支持對消息內容作通配,覆蓋該通配邏輯的會進入下面的執行邏輯。
5、功能支持
1. Spring Boot風格的啓動方式
與Spring Boot完美整合,若是本身沒有SpringBoot項目,想直接使用該框架搭建公衆號,可直接使用@WxApplication標記啓動類,該註解支持參數:menuAutoCreate,默認爲true。表示自動建立微信菜單,能夠設置爲false關閉自動建立菜單的行爲。若已有Spring Boot項目,想引入此框架,只需在你的任意配置類上標記註解@EnableWxMvc便可,參數同上。
2. Spring Mvc風格的Mapping
共支持三種Mapping:
注意:綁定所在類需聲明爲@WxController
3. Spring Mvc風格的參數綁定
支持如下類型參數:
參數綁定目前支持這幾種,若是有更好的方案須要支持,也能夠直接提出意見與建議,我會及時進行處理的。
4. Spring Mvc風格的返回值
返回值支持如下類型:
上面異步發送消息都是使用的WxMessageTemplate發送的,下面講解。
5. Spring風格的消息發送
本框架提供WxMessageTemplate發送消息,同時在template中提供了WxMessageProcessor支持,做用是在消息發送前對消息作處理。
如同步返回消息時,須要寫入fromUserName字段,而該字段是消息發送時的toUserName字段,沒有必要讓框架的使用者去處理這個字段,在WxCommonMessageProcessor處理器中就對該字段進行了處理,有興趣的能夠參考源碼。
同時還支持如下轉換:對於media類型的消息,能夠直接使用mediaUrl或者mediaPath寫入素材路徑,消息轉換器經過WxMediaManager自動管理素材得到必要的素材id。(關於WxMediaManager下面寫)
注意:全部的消息處理文本消息,建議都使用WxMessage裏的對應消息內容的builder來生成!
6. 素材自動管理
本框架提供WxMediaManager來管理素材,同時使用嵌入式數據庫保存素材與本地文件的對應關係,目前這部分功能我雖然完成了,可是總感受有很大的不妥,但願有人能幫我看看順便提點意見。
上面消息發送中的媒體其實也是經過素材管理器來實現的。
0.2.0.alpha 版本優化存儲,使用接口WxMediaStore來管理媒體存儲,開發者可自行實現該接口並註冊爲Spring的Bean來替換默認的MapDbWxMediaStore。各接口具體使用可參考MapDbWxMediaStore。這裏還能夠提供一個基於內存的實現來替換MapDb。
7. 內置AccessToken管理
本框架提供WxTokenStore接口來存儲token,並提供一個默認的基於內存的實現MemoryWxTokenStore,如有分佈式須要能夠自行實現該接口,並把實現類做爲Bean注入Spring便可。
8. 微信接口調用與返回值自動處理
使用WxApiInvokeSpi接口與WxInvokerProxyFactory工廠類自動生成微信接口調用代理,只須要聲明方法和註解便可,默認使用HttpComponent調用接口。有興趣的小夥伴能夠看看源碼,我寫的也不太好,有更好的建議歡迎提出。
同時對返回值作初步分析,若是接口返回內容的errorCode不爲0,則會做爲異常拋出。異常體系爲WxException及其子類。
PS:你也可使用這種方式任意生成本身的代理調用接口,後續我會加入文檔,暫時懶。。。
9. 菜單自動生成與自動更新
能夠經過開關開啓與關閉,經過@WxButton註解生成菜單結構,並自動調用接口判斷菜單是否改變,自動生成與更新菜單。
10. 內置微信接口認證
能夠正確響應微信發送的Token驗證,方便快捷。
11. 徹底無侵入的MVC模式
使用本框架,不會對SpringMvc本身原生的Mapping產生任何影響,也不會佔用任何獨有的Mapping關係(除了認證請求)。在此框架存在的狀況下,你可使用任何SpringMvc的原生功能,包括根目錄的請求,也不會被微信服務器獨自佔用。
若想使用單獨的地址做爲微信的api調用地址,請配置wx.path爲路徑信息,該路徑與微信公衆號後臺管理裏的接口配置信息裏url的路徑須要一致。
12. 微信Web認證攔截器與URL自動轉換
提供微信OAUTH2認證攔截,經過配置wx.callback-domain填寫OAUTH2受權回調頁面域名,經過配置wx.mvc.interceptor.includePatterns和wx.mvc.interceptor.excludePatterns來配置攔截的目標地址,你能夠提供一個WxOAuth2Callback接口的實現類做爲Bean,在WxOAuth2Interceptor中會自動注入這個bean,並在微信Web認證經過後調用after(WxOAuth2Context context)方法把相關的context傳遞給該Bean的方法,你能夠在該方法中獲取到context了的WxWebUser,並經過WxUserManager把WxWebUser轉換爲WxUser。關係詳細信息請參考:微信網頁受權。
自0.3.6版本後,提供wx.callback-domain的替代屬性wx.callback-url,用於設置回調的url,包含回調域名以及協議類型。比wx.callback-domain多了協議類型,請使用該配置替換callback-domain配置。
附加功能1: view類型的WxButton,自動判斷其中url是否屬於受權回調域名下地址,根據須要自動處理爲包含OAuth2的url。可結合默認的攔截器實現菜單url獲取點擊用戶信息的功能。同時結合wx.callback-url功能,支持菜單中配置相對路徑,無需攜帶域名。
附加功能2:自動判斷消息中的url是否須要添加OAuth重定向,請參考WxRedirectUtils。
13. 內置WxJsConfig管理器
代碼中可以使用WxJsTicketManager來獲取WxJsTicket
@Autowired
WxJsTicketManager wxJsTicketManager;
詳細使用參考其中方法
14. 支持消息加解密
配置wx.encrypt=true,wx.encodingAesKey=公衆號後臺設置的aesKey,即開啓消息加解密模式
注意開啓消息加解密時,須要開啓JCE無限制權限。
JDK7的下載地址: http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
JDK8的下載地址: http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
下載後解壓,能夠看到local_policy.jar和US_export_policy.jar以及readme.txt。若是安裝了JRE,將兩個jar文件放到%JRE_HOME%\lib\security目錄下覆蓋原來的文件。若是安裝了JDK,還要將兩個jar文件也放到%JDK_HOME%\jre\lib\security目錄下覆蓋原來文件。
6、相關連接
7、暫時不支持的功能
1. 自定義客服回覆消息
以後能夠加入支持,使用註解定義消息客服,相似於@RabbitListener的方式
2. 個性化菜單
稍微有點麻煩,用戶分類
3. 高級用戶管理
用戶分組什麼的是否有好的實現?暫時沒有需求
4. 公衆號其餘高級功能
如支付等