还在用HttpUtil?SpringBoot 3.0全新HTTP客户端工具来了,用起来够优雅!

news2024/11/30 14:39:43

我们平时开发项目的时候,经常会需要远程调用下其他服务提供的接口,于是我们会使用一些HTTP工具类比如Hutool提供的HttpUtil。前不久SpringBoot 3.0发布了,出了一个Http Interface的新特性,它允许我们使用声明式服务调用的方式来调用远程接口,今天我们就来聊聊它的使用!

SpringBoot实战电商项目mall(50k+star)地址:github.com/macrozheng/…

简介

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下载地址:www.oracle.com/cn/java/tec…

  • 由于Http Interface需要依赖webflux来实现,我们还需添加它的依赖。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
复制代码

基本使用

下面以调用mall-tiny-swagger中的接口为例,我们来体验下Http Interface的基本使用。

  • 首先我们准备一个服务来方便远程调用,使用的是之前的mall-tiny-swagger这个Demo,打开Swagger看下,里面有一个登录接口和需要登录认证的商品品牌CRUD接口,项目地址:github.com/macrozheng/…

  • 先在application.yml中配置好mall-tiny-swagger的服务地址;
remote:
  baseUrl: http://localhost:8088/
复制代码
  • 再通过@HttpExchange声明一个Http服务,使用@PostExchange注解表示进行POST请求;
/**
 * @auther macrozheng
 * @description 定义Http接口,用于调用远程的UmsAdmin服务
 * @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);
}
复制代码
  • 再创建一个远程调用品牌服务的接口,参数注解使用我们平时写Controller方法用的那些即可;
/**
 * @auther macrozheng
 * @description 定义Http接口,用于调用远程的PmsBrand服务
 * @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);
}
复制代码
  • 为方便后续调用需要登录认证的接口,我创建了TokenHolder这个类,把token存储到了Session中;
/**
 * @auther macrozheng
 * @description 登录token存储(在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;
    }

}
复制代码
  • 创建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服务对象,然后进行调用即可;
/**
 * @auther macrozheng
 * @description HttpInterface测试接口
 * @date 2022/1/19
 * @github https://github.com/macrozheng
 */
@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时会造成一定的麻烦,如果能独立出来就更好了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/87479.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Grafana 集成 Prometheum

参考文章&#xff1a; linux 上搭建grafana_摸鱼第一人的博客-CSDN博客_linux安装grafana 目录 一、安装 Grafana 二、安装 Prometheus 三、Prometheus 集成 Grafana 一、安装 Grafana 1、从官网下载rpm包 Download Grafana | Grafana Labs 2、将下载好的包上传到linu…

开源博客项目Blog .NET Core源码学习(2:数据库结构分析)

项目采用17张表以支撑博客内容展示及后台管理功能&#xff0c;本文结合数据库表中的存储数据&#xff0c;学习每张表的用途及其中字段的意义&#xff08;有部分字段暂时还不明白&#xff0c;后续学习代码时再逐步完善&#xff09;。 序号表名用途1CategoryInfo保存文章栏目的定…

vulntarget靶场系列-a-writeup

网络配置外网WIN7&#xff1a;ip1: 192.168.127.91/255.255.255.0 ,gw:192.168.127.2 (NAT模式)ip2:10.0.20.98-vmnet1(仅主机模式)域主机成员&#xff1a;10.0.20.99-vmnet1(仅主机模式)10.0.10.111-vmnet2(仅主机模式)域控&#xff1a;10.0.10.110-vmnet2(仅主机模式)密码配置…

C++11标准模板(STL)- 算法(std::max_element)

定义于头文件 <algorithm> 算法库提供大量用途的函数&#xff08;例如查找、排序、计数、操作&#xff09;&#xff0c;它们在元素范围上操作。注意范围定义为 [first, last) &#xff0c;其中 last 指代要查询或修改的最后元素的后一个元素。 返回范围内的最大元素 st…

2022 12月15日 每日面试题(MyBatis)

&#x1f3e0;个人主页&#xff1a;不会写代码的满满 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是满满&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; 目前状况&#x1f389;&#xff1a;开学即将大三&#xff0c;目标就是半年内找到一份实…

【云计算与大数据技术】资源管理、调度模型策略的讲解

一、资源管理模型 集群资源管理模型通常由两个部分组成&#xff0c;即资源表示模型和资源分配模型&#xff0c;由于这两个部分是耦合的&#xff0c;所有优化集群资源管理时需要同时结合这两个部分考虑&#xff0c;资源表示模型用于描述集群资源的组织方式&#xff0c;是集群资…

【Contrastive Learning:IVIF】

CLF-Net: Contrastive Learning for Infrared and Visible Image Fusion Network &#xff08;LF-Net&#xff1a;红外与可见光图像融合网络的对比学习&#xff09; &#xff08;总结&#xff1a;就是更像谁就选谁&#xff09; 本文提出了一种基于对比学习的红外和可见光图像融…

透过ChatGPT的进化足迹,OpenAI传达了哪些信号?

古希腊神话中&#xff0c;一位名叫赫尔墨斯的神&#xff0c;会充当人神之间的信使&#xff0c;穿着带有双翼的飞鞋&#xff0c;行走在神明与人类之间。根据《荷马史诗》的记载&#xff1a;“在天神中&#xff0c;赫尔墨斯是最喜欢引导凡人前行的。”这句话用来形容OpenAI与AI的…

佳力奇IPO过会:拟募资11亿 西安现代与华控湖北是股东

雷递网 雷建平 12月13日安徽佳力奇先进复合材料科技股份公司&#xff08;简称&#xff1a;“佳力奇”&#xff09;日前IPO过会&#xff0c;准备在深交所创业板上市。佳力奇计划募资11.22亿元&#xff0c;其中&#xff0c;6.2亿元用于先进复合材料数智化生产基地建设项目&#x…

Google Cloud database options (关于GCP数据库怎么选择)

背景 GCP提供了几种您可以从中选择的数据库服务&#xff0c;可是究竟该用哪个呢&#xff1f; Cloud SQL:Cloud Spanner:BigQuery:Cloud Bigtable:Cloud Firestore:Firebase Realtime Database:Cloud Memorystore: from Google Cloud database options sort out by zhengkai.…

【自然语言处理】隐马尔科夫模型【Ⅱ】隐马尔科夫模型概述

有任何的书写错误、排版错误、概念错误等&#xff0c;希望大家包含指正。 由于字数限制&#xff0c;分成六篇博客。 【自然语言处理】隐马尔可夫模型【Ⅰ】马尔可夫模型 【自然语言处理】隐马尔科夫模型【Ⅱ】隐马尔科夫模型概述 【自然语言处理】隐马尔科夫模型【Ⅲ】估计问题…

14、TheFatRat木马生成工具-创建后门或payload

kail攻击主机&#xff1a; Kali 192.168.11.106靶机&#xff1a;windows server 2008 r2 192.168.11.134 x64 32位一、TheFatRat介绍 TheFatRat创建的后门或者payload&#xff0c;可以在Linux&#xff0c;Windows&#xff0c;Mac和Android上等多种平台上执行&#xff0c;可生…

九、JavaScript——数据类型_数值

一、数值 定义及规范&#xff1a; 数值 &#xff08;Number) -在JS中所有的整数和浮点数(小数) 都是Number JS中的数值并不是无限大的&#xff0c;当数值超过一定后会显示近似值&#xff0c;JavaScript表达的最大整数是&#xff1a;9007199254740992 Infinity 是…

设计模式总览——枯燥的知识又增加了

从今天开始呢&#xff0c;阿Q就带大家了解一下 java 的设计模式&#xff0c;并从中选出比较常用的几种设计模式进行总结。首先呢&#xff0c;先给大家说一下设计模式到底是什么。 设计模式 设计模式&#xff08;Design pattern&#xff09;代表了最佳的实践&#xff0c;通常被…

iOS备用机自动充电方案

搬到新加坡以后&#xff0c;我拥有了很多张手机卡&#xff1a;1张国内的电话卡&#xff0c;回国时使用1张新加坡电话卡&#xff0c;本地使用1张马来西亚电话卡&#xff0c;去马来西亚旅行时使用1张英国电话卡&#xff0c;去欧洲旅行时使用相对应的&#xff0c;我需要有备用机来…

DSP篇--C6701功能调试系列之 FLASH测试

目录 1、 FLASH介绍 2、 FLASH功能测试 调试的前期准备可以参考前面的博文&#xff1a;DSP篇--C6701功能调试系列之前期准备_nanke_yh的博客-CSDN博客 1、 FLASH介绍 FLASH作为EMIF的外设&#xff0c;对其操作就需要根据EMIF相关配置来确定FLASH的地址了。 一般地&#xff…

Django第二天学习记录

1.对于路由配置的正则化补充(re_path的正则匹配) 对于第一天学习的path转换器过于暴力&#xff0c;对于需要匹配的内容不能很精准的进行转换。为了实现精准的字符串匹配规则&#xff0c;因此引入了re_path&#xff08;reg,view,namexxx&#xff09;进行路由规则的精确匹配。 正…

关于云计算的 7 大误区

一直以来&#xff0c;有几个关于云计算的误区四处传播&#xff0c;散布恐惧和不必要的不信任。在使用新技术之前研究新技术并检查事实是很好的。让我们来看看关于云计算的 7 大误区&#xff0c;看看哪些是真的&#xff0c;哪些不是。 1.云迁移可能很麻烦 云迁移包括许多过程&am…

KMP算法讲解与实现

0、概述 KMP是用于字符串查找/匹配的算法&#xff1b; KMP算法的时间复杂度&#xff1a;O(n)O(n)O(n)&#xff1b; KMP算法的核心&#xff1a; 1&#xff09;如何理解 next 数组2&#xff09;如何利用 next 数组加速匹配过程&#xff0c;优化时的两个实质 KMP算法的实现 1…

推荐|资深架构师 10 年 10 条干货职场心得

出道这么些年&#xff0c;跳过好多公司&#xff0c;除了国企其他的基本上都去过&#xff0c;甲方、乙方、外包、外企、互联网公司、创业公司…总结下来有这么些体会&#xff0c;希望能对你和大家有些参考价值。1. 保持学习可能离开了学校之后&#xff0c;所有的学习几乎都出于工…