一款直擊痛點(diǎn)的優(yōu)秀http框架,讓我超高效率完成了和第三方接口的對(duì)接

1.背景
因?yàn)闃I(yè)務(wù)關(guān)系,要和許多不同第三方公司進(jìn)行對(duì)接。這些服務(wù)商都提供基于http的api。但是每家公司提供api具體細(xì)節(jié)差別很大。有的基于RESTFUL規(guī)范,有的基于傳統(tǒng)的http規(guī)范;有的需要在header里放置簽名,有的需要SSL的雙向認(rèn)證,有的只需要SSL的單向認(rèn)證;有的以JSON?方式進(jìn)行序列化,有的以XML方式進(jìn)行序列化。類似于這樣細(xì)節(jié)的差別太多了。
不同的公司API規(guī)范不一樣,這很正常。但是對(duì)于我來說,我如果想要代碼變得優(yōu)雅。我就必須解決一個(gè)痛點(diǎn):
不同服務(wù)商API那么多的差異點(diǎn),如何才能維護(hù)一套不涉及業(yè)務(wù)的公共http調(diào)用套件。最好通過配置或者簡單的參數(shù)就能區(qū)分開來。進(jìn)行方便的調(diào)用?
我當(dāng)然知道有很多優(yōu)秀的大名鼎鼎的http開源框架可以實(shí)現(xiàn)任何形式的http調(diào)用,在多年的開發(fā)經(jīng)驗(yàn)中我都有使用過。比如apache的httpClient包,非常優(yōu)秀的Okhttp,jersey client。
這些http開源框架的接口使用相對(duì)來說,都不太一樣。不管選哪個(gè),在我這個(gè)場(chǎng)景里來說,我都不希望在調(diào)用每個(gè)第三方的http api時(shí)寫上一堆http調(diào)用代碼。
所以,在這個(gè)場(chǎng)景里,我得對(duì)每種不同的http api進(jìn)行封裝。這樣的代碼才能更加優(yōu)雅,業(yè)務(wù)代碼和http調(diào)用邏輯耦合度更低。
可惜,我比較懶。一來覺得封裝起來比較費(fèi)時(shí)間,二來覺對(duì)封裝這種底層http調(diào)用來說,應(yīng)該有更好的選擇。不想自己再去造輪子。
于是,我發(fā)現(xiàn)了一款優(yōu)秀的開源http框架,能屏蔽不同細(xì)節(jié)http api所帶來的所有差異。能通過簡單的配置像調(diào)用rpc框架一樣的去完成極為復(fù)雜的http調(diào)用。
Forest(點(diǎn)擊文章末尾閱讀原文也可以直接跳轉(zhuǎn)到項(xiàng)目首頁)
https://gitee.com/dt_flys/forest

2.上手
Forest支持了Springboot的自動(dòng)裝配,所以只需要引入一個(gè)依賴就行
<dependency>
??<groupId>com.dtflys.forestgroupId>
??<artifactId>spring-boot-starter-forestartifactId>
??<version>1.3.0version>
dependency>
定義自己的接口類
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);
??
}
在啟動(dòng)類里配置代理接口類的掃描包
@SpringBootApplication
@ForestScan(basePackages?=?"com.example.demo.forest")
public?class?DemoApplication?{
????public?static?void?main(String[]?args)?{
????????SpringApplication.run(DemoApplication.class,?args);
????}
}
這時(shí)候,你就可以從spring容器中注入你的代理接口,像調(diào)用本地方法一樣去調(diào)用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));
}
日志打印,Forest打印了內(nèi)部所用的http框架,和實(shí)際請(qǐng)求url和返回。當(dāng)然日志可以通過配置去控制開關(guān)。

3.特點(diǎn)
我覺得對(duì)于尤其是做對(duì)接第三方api的開發(fā)同學(xué)來說,這款開源框架能幫你提高很多效率。
Forest?底層封裝了2種不同的http框架:Apache httpClient和OKhttp。所以這個(gè)開源框架并沒有對(duì)底層實(shí)現(xiàn)進(jìn)行重復(fù)造輪子,而是在易用性上面下足了功夫。
我用Forest最終完成了和多個(gè)服務(wù)商api對(duì)接的項(xiàng)目,這些風(fēng)格迥異的API,我僅用了1個(gè)小時(shí)時(shí)間就把他們轉(zhuǎn)化為了本地方法。然后項(xiàng)目順利上線。
Forest作為一款更加高層的http框架,其實(shí)你并不需要寫很多代碼,大多數(shù)時(shí)候,你僅通過一些配置就能完成http的本地化調(diào)用。而這個(gè)框架所能覆蓋的面,卻非常之廣,滿足你絕大多數(shù)的http調(diào)用請(qǐng)求。
Forest有以下特點(diǎn):
以 Httpclient和OkHttp為后端框架通過調(diào)用本地方法的方式去發(fā)送Http請(qǐng)求, 實(shí)現(xiàn)了業(yè)務(wù)邏輯與Http協(xié)議之間的解耦 相比Feign更輕量,不依賴 Spring Cloud和任何注冊(cè)中心支持所有請(qǐng)求方法: GET,?HEAD,?OPTIONS,?TRACE,?POST,?DELETE,?PUT,?PATCH支持靈活的模板表達(dá)式 支持過濾器來過濾傳入的數(shù)據(jù) 基于注解、配置化的方式定義 Http請(qǐng)求支持 Spring和Springboot集成實(shí)現(xiàn) JSON和XML的序列化和反序列化支持JSON轉(zhuǎn)換框架:? Fastjson,Jackson,?Gson支持 JAXB形式的XML轉(zhuǎn)換支持 SSL的單向和雙向加密支持http連接池的設(shè)定 可以通過 OnSuccess和OnError接口參數(shù)實(shí)現(xiàn)請(qǐng)求結(jié)果的回調(diào)配置簡單,一般只需要 @Request一個(gè)注解就能完成絕大多數(shù)請(qǐng)求的定義支持異步請(qǐng)求調(diào)用
4.兩個(gè)很棒的功能
這里不對(duì)使用方式和配置方式一一描述,有興趣的可以去閱讀詳細(xì)文檔:
https://dt_flys.gitee.io/forest
這里只想分析這個(gè)框架2個(gè)我認(rèn)為比較好的功能
4.1?模板表達(dá)式和參數(shù)的映射綁定功能
模板表達(dá)式在使用的時(shí)候特別方便,舉個(gè)栗子
@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
);
上述是用序號(hào)下標(biāo)進(jìn)行取值,也可以通過名字進(jìn)行取值:
@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
);
以上三種寫法是等價(jià)的
當(dāng)然你也可以把參數(shù)綁定到header和body里去,你甚至于可以用一些表達(dá)式簡單的把對(duì)象序列化成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);
當(dāng)然數(shù)據(jù)綁定這塊詳情請(qǐng)參閱文檔
4.2 對(duì)HTTPS的支持
以前用其他http框架處理https的時(shí)候,總覺得特別麻煩,尤其是雙向證書。每次碰到問題也只能去baidu。然后根據(jù)別人的經(jīng)驗(yàn)來修改自己的代碼。
Forest對(duì)于這方面也想的很周到,底層完美封裝了對(duì)https單雙向證書的支持。也是只要通過簡單的配置就能迅速完成。舉個(gè)雙向證書栗子:
@Request(
????url?=?"${base}/pay",
???contentType?=?"application/json",
????type?=?"post",
????dataType?=?"json",
???keyStore?=?"pay-keystore",
???data?=?"${json($0)}"
)
public?PayResponse?pay(PayRequest?request);
其中pay-keystore對(duì)應(yīng)著application.yml里的ssl-key-stores
forest:
??...
??ssl-key-stores:
????-?id:?pay-keystore
??????file:?test.keystore
??????keystore-pass:?123456
??????cert-pass:?123456
??????protocols:?SSLv3
這樣設(shè)置,就ok了,剩下的,就是本地代碼形式的調(diào)用了。
5.最后
Forest有很多其他的功能設(shè)定,如果感興趣的同學(xué)還請(qǐng)仔細(xì)去閱讀文檔和示例。
但是我想說的是,相信看到這里,很多人一定會(huì)說,這不就是Feign嗎?
我在開發(fā)Spring Cloud項(xiàng)目的時(shí)候,也用過一段時(shí)間Feign,個(gè)人感覺Forest的確在配置和用法上和Feign的設(shè)計(jì)很像,但Feign的角色更多是作為Spring Cloud生態(tài)里的一個(gè)成員。充當(dāng)RPC通信的角色,其承擔(dān)的不僅是http通訊,還要對(duì)注冊(cè)中心下發(fā)的調(diào)用地址進(jìn)行負(fù)載均衡。
而Forest這個(gè)開源項(xiàng)目其定位則是一個(gè)高階的http工具,主打友好和易用性。從使用角度出發(fā),個(gè)人感覺Forest配置性更加簡單直接。提供的很多功能也能解決很多人的痛點(diǎn)。
開源精神難能可貴,好的開源需要大家的添磚加瓦和支持。希望這篇文章能給大家在選擇http客戶端框架時(shí)帶來一個(gè)新的選擇:Forest
來源:元人部落
?--END--
? 推薦
公眾號(hào)ID|javabaiwen
小編微信|619531440
每天分享技術(shù)干貨
視頻|電子書|面試題|開發(fā)經(jīng)驗(yàn)

