我們平時開發計畫的時候,經常會需要遠端呼叫下其他服務提供的介面,於是我們會使用一些 HTTP 工具類比如 Hutool 提供的 HttpUtil。SpringBoot 3.0 出了一個
Http Interface
的新特性,它允許我們使用聲明式服務呼叫的方式來呼叫遠端介面,今天我們就來聊聊它的使用!
簡介
Http Interface
讓你可以像定義 Java 介面那樣定義 HTTP 服務,而且用法和你平時寫 Controller 中方法完全一致。它會為這些 HTTP 服務介面自動生成代理實作類,底層是基於 Webflux 的 WebClient 實作的。
使用聲明式服務呼叫確實夠優雅,下面是一段使用
Http Interface
聲明的Http服務程式碼。
使用
在 SpringBoot 3.0 中使用
Http Interface
是非常簡單的,下面我們就來體驗下。
依賴整合
首先在計畫的
pom.xml
中定義好 SpringBoot 的版本為
3.0.0
;
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
<relativePath/><!-- lookup parent from repository -->
</parent>
由於 SpringBoot 最低要求為
Java 17
,我們需要先安裝好 JDK 17,安裝完成後配置計畫的 SDK 版本為
Java 17
,JDK 下載地址:https://www.oracle.com/cn/java/technologies/downloads/
由於
Http Interface
需要依賴 webflux 來實作,我們還需添加它的依賴。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
基本使用
下面,我們來體驗下
Http Interface
的基本使用。
首先我們準備一個服務來方便遠端呼叫,開啟 Swagger 看下,裏面有一個登入介面和需要登入認證的商品品牌 CRUD 介面
先在
application.yml
中配置好服務地址;
remote:
baseUrl:http://localhost:8088/
再透過
@HttpExchange
聲明一個 Http 服務,使用
@PostExchange
註解表示進行 POST 請求;
/**
* 定義Http介面,用於呼叫遠端的UmsAdmin服務
* Created by macro on 2022/1/19.
*/
@HttpExchange
publicinterfaceUmsAdminApi{
@PostExchange("admin/login")
CommonResult<LoginInfo> login(@RequestParam("username") String username, @RequestParam("password") String password);
}
再建立一個遠端呼叫品牌服務的介面,參數註解使用我們平時寫Controller 方法用的那些即可;
/**
* 定義Http介面,用於呼叫遠端的PmsBrand服務
* Created by macro on 2022/1/19.
*/
@HttpExchange
publicinterfacePmsBrandApi{
@GetExchange("brand/list")
CommonResult<CommonPage<PmsBrand>> list(@RequestParam("pageNum") Integer pageNum, @RequestParam("pageSize") Integer pageSize);
@GetExchange("brand/{id}")
CommonResult<PmsBrand> detail(@PathVariable("id") Long id);
@PostExchange("brand/create")
CommonResult create(@RequestBody PmsBrand pmsBrand);
@PostExchange("brand/update/{id}")
CommonResult update(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrand);
@GetExchange("brand/delete/{id}")
CommonResult delete(@PathVariable("id") Long id);
}
為方便後續呼叫需要登入認證的介面,我建立了
TokenHolder
這個類,把 token 儲存到了 Session 中;
/**
* 登入token儲存(在Session中)
* Created by macro on 2022/1/19.
*/
@Component
public classTokenHolder{
/**
* 添加token
*/
publicvoidputToken(String token){
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) ra).getRequest();
request.getSession().setAttribute("token", token);
}
/**
* 獲取token
*/
public String getToken(){
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) ra).getRequest();
Object token = request.getSession().getAttribute("token");
if(token!=null){
return (String) token;
}
returnnull;
}
}
建立 Java 配置,配置好請求用的客戶端 WebClient 及 Http 服務物件即可,由於品牌服務需要添加認證頭才能正常存取,所以使用了過濾器進行統一添加;
@Configuration
public classHttpInterfaceConfig{
@Value("${remote.baseUrl}")
private String baseUrl;
@Autowired
private TokenHolder tokenHolder;
@Bean
WebClient webClient(){
return WebClient.builder()
//添加全域預設請求頭
.defaultHeader("source", "http-interface")
//給請求添加過濾器,添加自訂的認證頭
.filter((request, next) -> {
ClientRequest filtered = ClientRequest.from(request)
.header("Authorization", tokenHolder.getToken())
.build();
return next.exchange(filtered);
})
.baseUrl(baseUrl).build();
}
@Bean
UmsAdminApi umsAdminApi(WebClient client){
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
return factory.createClient(UmsAdminApi. class);
}
@Bean
PmsBrandApi pmsBrandApi(WebClient client){
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
return factory.createClient(PmsBrandApi. class);
}
}
接下來在 Controller 中註入 Http 服務物件,然後進行呼叫即可;
/**
* HttpInterface測試介面
* Created by macro on 2022/1/19.
*/
@RestController
@Api(tags = "HttpInterfaceController")
@Tag(name = "HttpInterfaceController", description = "HttpInterface測試介面")
@RequestMapping("/remote")
public classHttpInterfaceController{
@Autowired
private UmsAdminApi umsAdminApi;
@Autowired
private PmsBrandApi pmsBrandApi;
@Autowired
private TokenHolder tokenHolder;
@ApiOperation(value = "呼叫遠端登入介面獲取token")
@PostMapping(value = "/admin/login")
public CommonResult<LoginInfo> login(@RequestParam String username, @RequestParam String password){
CommonResult<LoginInfo> result = umsAdminApi.login(username, password);
LoginInfo loginInfo = result.getData();
if (result.getData() != null) {
tokenHolder.putToken(loginInfo.getTokenHead() + " " + loginInfo.getToken());
}
return result;
}
@ApiOperation("呼叫遠端介面分頁查詢品牌列表")
@GetMapping(value = "/brand/list")
public CommonResult<CommonPage<PmsBrand>> listBrand(@RequestParam(value = "pageNum", defaultValue = "1")
@ApiParam("頁碼") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "3")
@ApiParam("每頁數量") Integer pageSize) {
return pmsBrandApi.list(pageNum, pageSize);
}
@ApiOperation("呼叫遠端介面獲取指定id的品牌詳情")
@GetMapping(value = "/brand/{id}")
public CommonResult<PmsBrand> brand(@PathVariable("id") Long id) {
return pmsBrandApi.detail(id);
}
@ApiOperation("呼叫遠端介面添加品牌")
@PostMapping(value = "/brand/create")
public CommonResult createBrand(@RequestBody PmsBrand pmsBrand){
return pmsBrandApi.create(pmsBrand);
}
@ApiOperation("呼叫遠端介面更新指定id品牌資訊")
@PostMapping(value = "/brand/update/{id}")
public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrand) {
return pmsBrandApi.update(id,pmsBrand);
}
@ApiOperation("呼叫遠端介面刪除指定id的品牌")
@GetMapping(value = "/delete/{id}")
public CommonResult deleteBrand(@PathVariable("id") Long id) {
return pmsBrandApi.delete(id);
}
}
測試
下面我們透過 Postman 進行測試,首先呼叫登入介面獲取到遠端服務返回的 token 了;
再呼叫下需要登入認證的品牌列表介面,發現可以正常存取。
總結
Http Interface
讓我們只需定義介面,無需定義方法實作就能進行遠端 HTTP 呼叫,確實非常方便!但是其實作依賴 Webflux 的 WebClient,在我們使用 SpringMVC 時會造成一定的麻煩,如果能獨立出來就更好了!
參考資料
官方文件:https://docs.spring.io/spring-framework/reference/integration/rest-clients.html
👇🏻 點選下方閱讀原文,獲取魚皮往期編程幹貨。
往期推薦