2024年 全新 HTTP 客户端 你用了?

我们平时开发项目的时候,经常会需要远程调用下其他服务提供的接口,于是我们会使用一些 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
public interface UmsAdminApi {

    @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
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);
}
  • 为方便后续调用需要登录认证的接口,我创建了TokenHolder这个类,把 token 存储到了 Session 中;

/**
 * 登录token存储(在Session中)
 * Created by macro on 2022/1/19.
 */
@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;
    }

}
  • 创建 Java 配置,配置好请求用的客户端 WebClient 及 Http 服务对象即可,由于品牌服务需要添加认证头才能正常访问,所以使用了过滤器进行统一添加;

@Configuration
public class HttpInterfaceConfig {

    @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 class HttpInterfaceController {

    @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 时会造成一定的麻烦,如果能独立出来就更好了!

相关推荐

  1. IntelliJ IDEA 的 HTTP 客户的高级

    2024-01-26 22:36:03       37 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-26 22:36:03       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-26 22:36:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-26 22:36:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-26 22:36:03       18 阅读

热门阅读

  1. mysql sql查询

    2024-01-26 22:36:03       39 阅读
  2. 学习 canvas标签 这一篇文章足以

    2024-01-26 22:36:03       22 阅读
  3. Git的常用命令

    2024-01-26 22:36:03       35 阅读
  4. JVM实战(33)——内存溢出之内存使用率过高

    2024-01-26 22:36:03       39 阅读
  5. 三、详解Redis分布式锁&Redisson分布式锁

    2024-01-26 22:36:03       33 阅读
  6. 本人原创写的用PHPps支付宝支付凭证截图的源码

    2024-01-26 22:36:03       29 阅读