一款優(yōu)秀、開源的 http 框架
如果你因為業(yè)務關系,要和許多不同第三方公司進行對接。這些服務商都提供基于http的api。但是每家公司提供api具體細節(jié)差別很大。有的基于RESTFUL規(guī)范,有的基于傳統(tǒng)的http規(guī)范;有的需要在header里放置簽名,有的需要SSL的雙向認證,有的只需要SSL的單向認證;有的以JSON 方式進行序列化,有的以XML方式進行序列化。類似于這樣細節(jié)的差別太多了。
不同的公司API規(guī)范不一樣,這很正常。但是對于我來說,我如果想要代碼變得優(yōu)雅。我就必須解決一個痛點:
不同服務商API那么多的差異點,如何才能維護一套不涉及業(yè)務的公共http調用套件。最好通過配置或者簡單的參數(shù)就能區(qū)分開來。進行方便的調用?
我當然知道有很多優(yōu)秀的大名鼎鼎的http開源框架可以實現(xiàn)任何形式的http調用,在多年的開發(fā)經(jīng)驗中我都有使用過。比如apache的httpClient包,非常優(yōu)秀的Okhttp,jersey client。
這些http開源框架的接口使用相對來說,都不太一樣。不管選哪個,在我這個場景里來說,我都不希望在調用每個第三方的http api時寫上一堆http調用代碼。
所以,在這個場景里,我得對每種不同的http api進行封裝。這樣的代碼才能更加優(yōu)雅,業(yè)務代碼和http調用邏輯耦合度更低。
可惜,我比較懶。一來覺得封裝起來比較費時間,二來覺對封裝這種底層http調用來說,應該有更好的選擇。不想自己再去造輪子。
于是,我發(fā)現(xiàn)了一款優(yōu)秀的開源http框架,能屏蔽不同細節(jié)http api所帶來的所有差異。能通過簡單的配置像調用rpc框架一樣的去完成極為復雜的http調用。
https://gitee.com/dromara/forest
上手
Forest支持了Springboot的自動裝配,所以只需要引入一個依賴就行
- <dependency>
- <groupId>com.dtflys.forest</groupId>
- <artifactId>spring-boot-starter-forest</artifactId>
- <version>1.3.0</version>
- </dependency>
- ```java
- 定義自己的接口類
- public interface MyClient {
- @Request(url = "http://baidu.com")
- String simpleRequest();
- @Request(
- url = "http://ditu.amap.com/service/regeo",
- dataType = "json"
- )
- Map getLocation(@DataParam("longitude") String longitude, @DataParam("latitude") String latitude);
- }
- 在啟動類里配置代理接口類的掃描包
- ```jva
- @SpringBootApplication
- @ForestScan(basePackages = "com.example.demo.forest")
- public class DemoApplication {
- public static void main(String[] args) {
- SpringApplication.run(DemoApplication.class, args);
- }
- }
這時候,你就可以從spring容器中注入你的代理接口,像調用本地方法一樣去調用http的api了
- @Autowired
- private MyClient myClient;
- @Override
- public void yourMethod throws Exception {
- Map result = myClient.getLocation("124.730329","31.463683");
- System.out.println(JSON.toJSONString(result,true));
- }
日志打印,F(xiàn)orest打印了內部所用的http框架,和實際請求url和返回。當然日志可以通過配置去控制開關。
特點
我覺得對于尤其是做對接第三方api的開發(fā)同學來說,這款開源框架能幫你提高很多效率。
Forest 底層封裝了2種不同的http框架:Apache httpClient和OKhttp。所以這個開源框架并沒有對底層實現(xiàn)進行重復造輪子,而是在易用性上面下足了功夫。
我用Forest最終完成了和多個服務商api對接的項目,這些風格迥異的API,我僅用了1個小時時間就把他們轉化為了本地方法。然后項目順利上線。
Forest作為一款更加高層的http框架,其實你并不需要寫很多代碼,大多數(shù)時候,你僅通過一些配置就能完成http的本地化調用。而這個框架所能覆蓋的面,卻非常之廣,滿足你絕大多數(shù)的http調用請求。
Forest有以下特點:
- 以Httpclient和OkHttp為后端框架
- 通過調用本地方法的方式去發(fā)送Http請求, 實現(xiàn)了業(yè)務邏輯與Http協(xié)議之間的解耦
- 相比Feign更輕量,不依賴Spring Cloud和任何注冊中心
- 支持所有請求方法:GET, HEAD, OPTIONS, TRACE, POST, DELETE, PUT, PATCH
- 支持靈活的模板表達式
- 支持過濾器來過濾傳入的數(shù)據(jù)
- 基于注解、配置化的方式定義Http請求
- 支持Spring和Springboot集成
- 實現(xiàn)JSON和XML的序列化和反序列化
- 支持JSON轉換框架: Fastjson,Jackson, Gson
- 支持JAXB形式的XML轉換
- 支持SSL的單向和雙向加密
- 支持http連接池的設定
- 可以通過OnSuccess和OnError接口參數(shù)實現(xiàn)請求結果的回調
- 配置簡單,一般只需要@Request一個注解就能完成絕大多數(shù)請求的定義
- 支持異步請求調用
兩個很棒的功能
這里不對使用方式和配置方式一一描述,有興趣的可以去閱讀詳細文檔:
這里只想分析這個框架2個我認為比較好的功能
- 模板表達式和參數(shù)的映射綁定功能
模板表達式在使用的時候特別方便,舉個栗子
- @Request(
- url = "${0}/send?un=${1}&pw=${2}&ph=${3}&ct=${4}",
- type = "get",
- dataType = "json"
- )
- public Map send(
- String base,
- String userName,
- String password,
- String phone,
- String content
- );
上述是用序號下標進行取值,也可以通過名字進行取值:
- @Request(
- url = "${base}/send?un=${un}&pw=${pw}&ph=${3}&ct=${ct}",
- type = "get",
- dataType = "json"
- )
- public Map send(
- @DataVariable("base") String base,
- @DataVariable("un") String userName,
- @DataVariable("pw") String password,
- @DataVariable("ph") String phone,
- @DataVariable("ct") String content
- );
甚至于可以這樣簡化寫:
- @Request(
- url = "${base}/send",
- type = "get",
- dataType = "json"
- )
- public Map send(
- @DataVariable("base") String base,
- @DataParam("un") String userName,
- @DataParam("pw") String password,
- @DataParam("ph") String phone,
- @DataParam("ct") String content
- );
以上三種寫法是等價的
當然你也可以把參數(shù)綁定到header和body里去,你甚至于可以用一些表達式簡單的把對象序列化成json或者xml:
- @Request(
- url = "${base}/pay",
- contentType = "application/json",
- type = "post",
- dataType = "json",
- headers = {"Authorization: ${1}"},
- data = "${json($0)}"
- )
- public PayResponse pay(PayRequest request, String auth);
當然數(shù)據(jù)綁定這塊詳情請參閱文檔
- 對HTTPS的支持
以前用其他http框架處理https的時候,總覺得特別麻煩,尤其是雙向證書。每次碰到問題也只能去baidu。然后根據(jù)別人的經(jīng)驗來修改自己的代碼。
Forest對于這方面也想的很周到,底層完美封裝了對https單雙向證書的支持。也是只要通過簡單的配置就能迅速完成。舉個雙向證書栗子:
- @Request(
- url = "${base}/pay",
- contentType = "application/json",
- type = "post",
- dataType = "json",
- keyStore = "pay-keystore",
- data = "${json($0)}"
- )
- public PayResponse pay(PayRequest request);
其中pay-keystore對應著application.yml里的ssl-key-stores
- forest:
- ...
- ssl-key-stores:
- - id: pay-keystore
- file: test.keystore
- keystore-pass: 123456
- cert-pass: 123456
- protocols: SSLv3
這樣設置,就ok了,剩下的,就是本地代碼形式的調用了。
最后
Forest有很多其他的功能設定,如果感興趣的同學還請仔細去閱讀文檔和示例。
但是我想說的是,相信看到這里,很多人一定會說,這不就是Feign嗎?
我在開發(fā)Spring Cloud項目的時候,也用過一段時間Feign,個人感覺Forest的確在配置和用法上和Feign的設計很像,但Feign的角色更多是作為Spring Cloud生態(tài)里的一個成員。充當RPC通信的角色,其承擔的不僅是http通訊,還要對注冊中心下發(fā)的調用地址進行負載均衡。
而Forest這個開源項目其定位則是一個高階的http工具,主打友好和易用性。從使用角度出發(fā),個人感覺Forest配置性更加簡單直接。提供的很多功能也能解決很多人的痛點。
開源精神難能可貴,好的開源需要大家的添磚加瓦和支持。希望這篇文章能給大家在選擇http客戶端框架時帶來一個新的選擇:Forest