當前位置: 妍妍網 > 碼農

還在用 HttpUtil?全新 HTTP 客戶端來了

2024-01-24碼農


我們平時開發計畫的時候,經常會需要遠端呼叫下其他服務提供的介面,於是我們會使用一些 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

    👇🏻 點選下方閱讀原文,獲取魚皮往期編程幹貨。

    往期推薦