還在用HttpUtil?SpringBoot 3.0全新HTTP客戶端工具來了,用起來夠優(yōu)雅!
我們平時(shí)開發(fā)項(xiàng)目的時(shí)候,經(jīng)常會(huì)需要遠(yuǎn)程調(diào)用下其他服務(wù)提供的接口,于是我們會(huì)使用一些HTTP工具類比如Hutool提供的HttpUtil。前不久SpringBoot 3.0發(fā)布了,出了一個(gè)
Http Interface
的新特性,它允許我們使用聲明式服務(wù)調(diào)用的方式來調(diào)用遠(yuǎn)程接口,今天我們就來聊聊它的使用!
SpringBoot實(shí)戰(zhàn)電商項(xiàng)目mall(50k+star)地址:github.com/macrozheng/…
簡(jiǎn)介
Http Interface
讓你可以像定義Java接口那樣定義HTTP服務(wù),而且用法和你平時(shí)寫Controller中方法完全一致。它會(huì)為這些HTTP服務(wù)接口自動(dòng)生成代理實(shí)現(xiàn)類,底層是基于Webflux的WebClient實(shí)現(xiàn)的。
使用聲明式服務(wù)調(diào)用確實(shí)夠優(yōu)雅,下面是一段使用Http Interface
聲明的Http服務(wù)代碼。
使用
在SpringBoot 3.0中使用
Http Interface
是非常簡(jiǎn)單的,下面我們就來體驗(yàn)下。
依賴集成
- 首先在項(xiàng)目的
pom.xml
中定義好SpringBoot的版本為3.0.0
;
xml復(fù)制代碼<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,安裝完成后配置項(xiàng)目的SDK版本為Java 17
,JDK下載地址:www.oracle.com/cn/java/tec…
- 由于
Http Interface
需要依賴webflux來實(shí)現(xiàn),我們還需添加它的依賴。
xml復(fù)制代碼<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
基本使用
下面以調(diào)用
mall-tiny-swagger
中的接口為例,我們來體驗(yàn)下Http Interface
的基本使用。
- 首先我們準(zhǔn)備一個(gè)服務(wù)來方便遠(yuǎn)程調(diào)用,使用的是之前的
mall-tiny-swagger
這個(gè)Demo,打開Swagger看下,里面有一個(gè)登錄接口和需要登錄認(rèn)證的商品品牌CRUD接口,項(xiàng)目地址:github.com/macrozheng/…
- 先在
application.yml
中配置好mall-tiny-swagger
的服務(wù)地址;
yaml復(fù)制代碼remote:
baseUrl: http://localhost:8088/
- 再通過
@HttpExchange
聲明一個(gè)Http服務(wù),使用@PostExchange
注解表示進(jìn)行POST請(qǐng)求;
java復(fù)制代碼/**
* @auther macrozheng
* @description 定義Http接口,用于調(diào)用遠(yuǎn)程的UmsAdmin服務(wù)
* @date 2022/1/19
* @github https://github.com/macrozheng
*/
@HttpExchange
public interface UmsAdminApi {
@PostExchange("admin/login")
CommonResult<LoginInfo> login(@RequestParam("username") String username, @RequestParam("password") String password);
}
- 再創(chuàng)建一個(gè)遠(yuǎn)程調(diào)用品牌服務(wù)的接口,參數(shù)注解使用我們平時(shí)寫Controller方法用的那些即可;
java復(fù)制代碼/**
* @auther macrozheng
* @description 定義Http接口,用于調(diào)用遠(yuǎn)程的PmsBrand服務(wù)
* @date 2022/1/19
* @github https://github.com/macrozheng
*/
@HttpExchange
public interface PmsBrandApi {
@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);
}
- 為方便后續(xù)調(diào)用需要登錄認(rèn)證的接口,我創(chuàng)建了
TokenHolder
這個(gè)類,把token存儲(chǔ)到了Session中;
java復(fù)制代碼/**
* @auther macrozheng
* @description 登錄token存儲(chǔ)(在Session中)
* @date 2022/1/19
* @github https://github.com/macrozheng
*/
@Component
public class TokenHolder {
/**
* 添加token
*/
public void putToken(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;
}
return null;
}
}
- 創(chuàng)建Java配置,配置好請(qǐng)求用的客戶端WebClient及Http服務(wù)對(duì)象即可,由于品牌服務(wù)需要添加認(rèn)證頭才能正常訪問,所以使用了過濾器進(jìn)行統(tǒng)一添加;
java復(fù)制代碼@Configuration
public class HttpInterfaceConfig {
@Value("${remote.baseUrl}")
private String baseUrl;
@Autowired
private TokenHolder tokenHolder;
@Bean
WebClient webClient() {
return WebClient.builder()
//添加全局默認(rèn)請(qǐng)求頭
.defaultHeader("source", "http-interface")
//給請(qǐng)求添加過濾器,添加自定義的認(rèn)證頭
.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服務(wù)對(duì)象,然后進(jìn)行調(diào)用即可;
java復(fù)制代碼/**
* @auther macrozheng
* @description HttpInterface測(cè)試接口
* @date 2022/1/19
* @github https://github.com/macrozheng
*/
@RestController
@Api(tags = "HttpInterfaceController")
@Tag(name = "HttpInterfaceController", description = "HttpInterface測(cè)試接口")
@RequestMapping("/remote")
public class HttpInterfaceController {
@Autowired
private UmsAdminApi umsAdminApi;
@Autowired
private PmsBrandApi pmsBrandApi;
@Autowired
private TokenHolder tokenHolder;
@ApiOperation(value = "調(diào)用遠(yuǎn)程登錄接口獲取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("調(diào)用遠(yuǎn)程接口分頁查詢品牌列表")
@GetMapping(value = "/brand/list")
public CommonResult<CommonPage<PmsBrand>> listBrand(@RequestParam(value = "pageNum", defaultValue = "1")
@ApiParam("頁碼") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "3")
@ApiParam("每頁數(shù)量") Integer pageSize) {
return pmsBrandApi.list(pageNum, pageSize);
}
@ApiOperation("調(diào)用遠(yuǎn)程接口獲取指定id的品牌詳情")
@GetMapping(value = "/brand/{id}")
public CommonResult<PmsBrand> brand(@PathVariable("id") Long id) {
return pmsBrandApi.detail(id);
}
@ApiOperation("調(diào)用遠(yuǎn)程接口添加品牌")
@PostMapping(value = "/brand/create")
public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) {
return pmsBrandApi.create(pmsBrand);
}
@ApiOperation("調(diào)用遠(yuǎn)程接口更新指定id品牌信息")
@PostMapping(value = "/brand/update/{id}")
public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrand) {
return pmsBrandApi.update(id,pmsBrand);
}
@ApiOperation("調(diào)用遠(yuǎn)程接口刪除指定id的品牌")
@GetMapping(value = "/delete/{id}")
public CommonResult deleteBrand(@PathVariable("id") Long id) {
return pmsBrandApi.delete(id);
}
}
測(cè)試
- 下面我們通過Postman進(jìn)行測(cè)試,首先調(diào)用登錄接口獲取到遠(yuǎn)程服務(wù)返回的token了;
- 再調(diào)用下需要登錄認(rèn)證的品牌列表接口,發(fā)現(xiàn)可以正常訪問。
總結(jié)
Http Interface
讓我們只需定義接口,無需定義方法實(shí)現(xiàn)就能進(jìn)行遠(yuǎn)程HTTP調(diào)用,確實(shí)非常方便!但是其實(shí)現(xiàn)依賴Webflux的WebClient,在我們使用SpringMVC時(shí)會(huì)造成一定的麻煩,如果能獨(dú)立出來就更好了!
參考資料
官方文檔:docs.spring.io/spring-fram…文章來源:http://www.zghlxwxcb.cn/news/detail-835766.html
項(xiàng)目源碼地址
github.com/macrozheng/…文章來源地址http://www.zghlxwxcb.cn/news/detail-835766.html
到了這里,關(guān)于還在用HttpUtil?SpringBoot 3.0全新HTTP客戶端工具來了,用起來夠優(yōu)雅!的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!