Spring中网络请求客户端WebClient的使用详解

Spring中网络请求客户端WebClient的使用详解_java_脚本之家

Spring5的WebClient使用详解-腾讯云开发者社区-腾讯云

在 Spring 5 之前,如果我们想要调用其他系统提供的 HTTP 服务,通常可以使用 Spring 提供的 RestTemplate 来访问,不过由于 RestTemplate 是 Spring 3 中引入的同步阻塞式 HTTP 客户端,因此存在一定性能瓶颈。根据 Spring 官方文档介绍,在将来的版本中它可能会被弃用。

​ 作为替代,Spring 官方已在 Spring 5 中引入了 WebClient 作为非阻塞式 Reactive HTTP 客户端。下面通过样例演示如何使用 WebClient。

一、基本介绍

1.什么是 WebClient

从 Spring 5 开始,Spring 中全面引入了 Reactive 响应式编程。而 WebClient 则是 Spring WebFlux 模块提供的一个非阻塞的基于响应式编程的进行 Http 请求的客户端工具。

由于 WebClient 的请求模式属于异步非阻塞,能够以少量固定的线程处理高并发的 HTTP 请求。因此,从 Spring 5 开始,HTTP 服务之间的通信我们就可以考虑使用 WebClient 来取代之前的 RestTemplate。

2.WebClient 的优势

(1)与 RestTemplate 相比,WebClient 有如下优势:

  • 非阻塞,Reactive 的,并支持更高的并发性和更少的硬件资源。
  • 提供利用 Java 8 lambdas 的函数 API。
  • 支持同步和异步方案。
  • 支持从服务器向上或向下流式传输。

(2)RestTemplate 不适合在非阻塞应用程序中使用,因此 Spring WebFlux 应用程序应始终使用 WebClient。在大多数高并发场景中,WebClient 也应该是 Spring MVC 中的首选,并且用于编写一系列远程,相互依赖的调用。

3.安装配置

编辑 pom.xml 文件,添加 Spring WebFlux 依赖,从而可以使用 WebClient。

1

2

3

4

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-webflux</artifactId>

</dependency>

二、创建 WebClient 实例

​ 从 WebClient 的源码中可以看出,WebClient 接口提供了三个不同的静态方法来创建 WebClient 实例:

1.利用 create() 创建

(1)下面利用 create() 方法创建一个 WebClient 对象,并利用该对象请求一个网络接口,最后将结果以字符串的形式打印出来。

注意:由于利用 create() 创建的 WebClient 对象没有设定 baseURL,所以这里的 uri() 方法相当于重写 baseURL。

1

2

3

4

5

6

7

8

9

WebClient webClient = WebClient.create();

  

Mono<String> mono = webClient

        .get() // GET 请求

        .uri("http://jsonplaceholder.typicode.com/posts/1")  // 请求路径

        .retrieve() // 获取响应体

        .bodyToMono(String.class); //响应数据类型转换

  

System.out.println(mono.block());

2.利用 create(String baseUrl) 创建

(1)下面利用 create(String baseUrl) 方法创建一个 WebClient 对象,并利用该对象请求一个网络接口,最后将结果以字符串的形式打印出来。

注意:由于利用 create(String baseUrl) 创建的 WebClient 对象时已经设定了 baseURL,所以 uri() 方法会将返回的结果和 baseUrl 进行拼接组成最终需要远程请求的资源 URL。

1

2

3

4

5

6

7

8

9

WebClient webClient = WebClient.create("http://jsonplaceholder.typicode.com");

  

Mono<String> mono = webClient

        .get() // GET 请求

        .uri("/posts/1"// 请求路径

        .retrieve() // 获取响应体

        .bodyToMono(String.class); //响应数据类型转换

  

System.out.println(mono.block());

3.利用 builder 创建(推荐)

(1)下面使用 builder() 返回一个 WebClient.Builder,然后再调用 build 就可以返回 WebClient 对象。并利用该对象请求一个网络接口,最后将结果以字符串的形式打印出来。

注意:由于返回的不是 WebClient 类型而是 WebClient.Builder,我们可以通过返回的 WebClient.Builder 设置一些配置参数(例如:baseUrl、header、cookie 等),然后再调用 build 就可以返回 WebClient 对象了

1

2

3

4

5

6

7

8

9

10

11

12

13

WebClient webClient = WebClient.builder()

        .baseUrl("http://jsonplaceholder.typicode.com")

        .defaultHeader(HttpHeaders.USER_AGENT,"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)")

        .defaultCookie("ACCESS_TOKEN", "test_token")

        .build();

  

Mono<String> mono = webClient

        .get() // GET 请求

        .uri("/posts/1"// 请求路径

        .retrieve() // 获取响应体

        .bodyToMono(String.class); //响应数据类型转换

          

System.out.println(mono.block());

三、GET 请求

1.获取 String 结果数据

下面代码将响应结果映射为一个 String 字符串,并打印出来。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

@RestController

public class HelloController {

  

    // 创建 WebClient 对象

    private WebClient webClient = WebClient.builder()

            .baseUrl("http://jsonplaceholder.typicode.com")

            .build();

  

    @GetMapping("/test")

    public void test() {

        Mono<String> mono = webClient

                .get() // GET 请求

                .uri("/posts/1"// 请求路径

                .retrieve() // 获取响应体

                .bodyToMono(String.class); //响应数据类型转换

        System.out.println(mono.block());

        return;

    }

}

2.将结果转换为对象

(1)当响应的结果是 JSON 时,也可以直接指定为一个 Object,WebClient 将接收到响应后把 JSON 字符串转换为对应的对象。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

@RestController

public class HelloController {

  

    // 创建 WebClient 对象

    private WebClient webClient = WebClient.builder()

            .baseUrl("http://jsonplaceholder.typicode.com")

            .build();

  

    @GetMapping("/test")

    public void test() {

        Mono<PostBean> mono = webClient

                .get() // GET 请求

                .uri("/posts/1"// 请求路径

                .retrieve() // 获取响应体

                .bodyToMono(PostBean.class); //响应数据类型转换

        System.out.println(mono.block());

        return;

    }

}

(2)其中定义的实体 Bean 代码如下:

1

2

3

4

5

6

7

8

9

@Getter

@Setter

@ToString

public class PostBean {

    private int userId;

    private int id;

    private String title;

    private String body;

}

3.将结果转成集合

(1)假设接口返回的是一个 json 数组,内容如下:

(2)我们也可以将其转成对应的 Bean 集合:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

@RestController

public class HelloController {

  

    // 创建 WebClient 对象

    private WebClient webClient = WebClient.builder()

            .baseUrl("http://jsonplaceholder.typicode.com")

            .build();

  

    @GetMapping("/test")

    public void test() {

        Flux<PostBean> flux = webClient

                .get() // GET 请求

                .uri("/posts"// 请求路径

                .retrieve() // 获取响应体

                .bodyToFlux(PostBean.class); //响应数据类型转换

        List<PostBean> posts = flux.collectList().block();

        System.out.println("结果数:" + posts.size());

        return;

    }

}

4.参数传递的几种方式

下面 3 种方式的结果都是一样的。

(1)使用占位符的形式传递参数:

1

2

3

4

5

Mono<String> mono = webClient

        .get() // GET 请求

        .uri("/{1}/{2}", "posts", "1"// 请求路径

        .retrieve() // 获取响应体

        .bodyToMono(String.class); //响应数据类型转换

(2)另一种使用占位符的形式:

1

2

3

4

5

6

7

8

9

String type = "posts";

int id = 1;

  

Mono<String> mono = webClient

        .get() // GET 请求

        .uri("/{type}/{id}", type, id)  // 请求路径

        .retrieve() // 获取响应体

        .bodyToMono(String.class); //响应数据类型转换

        System.out.println(mono.block());

(3)我们也可以使用 map 装载参数:

1

2

3

4

5

6

7

8

9

Map<String,Object> map = new HashMap<>();

map.put("type", "posts");

map.put("id", 1);

  

Mono<String> mono = webClient

        .get() // GET 请求

        .uri("/{type}/{id}", map)  // 请求路径

        .retrieve() // 获取响应体

        .bodyToMono(String.class); //响应数据类型转换

5.subscribe 订阅(非阻塞式调用)

(1)前面的样例我们都是人为地使用 block 方法来阻塞当前程序。其实 WebClient 是异步的,也就是说等待响应的同时不会阻塞正在执行的线程。只有在响应结果准备就绪时,才会发起通知。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

@RestController

public class HelloController {

  

    // 创建 WebClient 对象

    private WebClient webClient = WebClient.builder()

            .baseUrl("http://jsonplaceholder.typicode.com")

            .build();

  

    @GetMapping("/test")

    public void test() {

        System.out.println("--- begin ---");

  

        Mono<String> mono = webClient

                .get() // GET 请求

                .uri("/posts/1"// 请求路径

                .retrieve() // 获取响应体

                .bodyToMono(String.class); //响应数据类型转换

  

        // 订阅(异步处理结果)

        mono.subscribe(result -> {

            System.out.println(result);

        });

  

        System.out.println("--- end ---");

        return;

    }

}

附:使用 exchange() 方法获取完整的响应内容

1.方法介绍

(1)前面我们都是使用 retrieve() 方法直接获取到了响应的内容,如果我们想获取到响应的头信息、Cookie 等,可以在通过 WebClient 请求时把调用 retrieve() 改为调用 exchange()。

(2)通过 exchange() 方法可以访问到代表响应结果的对象,通过该对象我们可以获取响应码、contentType、contentLength、响应消息体等。

2.使用样例

下面代码请求一个网络接口,并将响应体、响应头、响应码打印出来。其中响应体的类型设置为 String。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

@RestController

public class HelloController {

  

    // 创建 WebClient 对象

    private WebClient webClient = WebClient.builder()

            .baseUrl("http://jsonplaceholder.typicode.com")

            .build();

  

    @GetMapping("/test")

    public void test() {

        Mono<ClientResponse> mono = webClient

                .get() // GET 请求

                .uri("/posts/1"// 请求路径

                .exchange();

  

        // 获取完整的响应对象

        ClientResponse response = mono.block();

  

        HttpStatus statusCode = response.statusCode(); // 获取响应码

        int statusCodeValue = response.rawStatusCode(); // 获取响应码值

        Headers headers = response.headers(); // 获取响应头

  

        // 获取响应体

        Mono<String> resultMono = response.bodyToMono(String.class);

        String body = resultMono.block();

  

        // 输出结果

        System.out.println("statusCode:" + statusCode);

        System.out.println("statusCodeValue:" + statusCodeValue);

        System.out.println("headers:" + headers.asHttpHeaders());

        System.out.println("body:" + body);

        return;

    }

}

四、POST 请求

1.发送一个 JSON 格式数据(使用 json 字符串)

(1)下面代码使用 post 方式发送一个 json 格式的字符串,并将结果打印出来(以字符串的形式)。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

@RestController

public class HelloController {

  

    // 创建 WebClient 对象

    private WebClient webClient = WebClient.builder()

            .baseUrl("http://jsonplaceholder.typicode.com")

            .build();

  

    @GetMapping("/test")

    public void test() {

        // 需要提交的 json 字符串

        String jsonStr = "{\"userId\": 222,\"title\": \"abc\",\"body\": \"航歌\"}";

  

        // 发送请求

        Mono<String> mono = webClient

                .post() // POST 请求

                .uri("/posts"// 请求路径

                .contentType(MediaType.APPLICATION_JSON_UTF8)

                .body(BodyInserters.fromObject(jsonStr))

                .retrieve() // 获取响应体

                .bodyToMono(String.class); //响应数据类型转换

  

        // 输出结果

        System.out.println(mono.block());

        return;

    }

}

2.发送一个 JSON 格式数据(使用 Java Bean)

(1)下面代码使用 post 方式发送一个 Bean 对象,并将结果打印出来(以字符串的形式)。结果同上面是一样的:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

@RestController

public class HelloController {

  

    // 创建 WebClient 对象

    private WebClient webClient = WebClient.builder()

            .baseUrl("http://jsonplaceholder.typicode.com")

            .build();

  

    @GetMapping("/test")

    public void test() {

        // 要发送的数据对象

        PostBean postBean = new PostBean();

        postBean.setUserId(222);

        postBean.setTitle("abc");

        postBean.setBody("航歌");

  

        // 发送请求

        Mono<String> mono = webClient

                .post() // POST 请求

                .uri("/posts"// 请求路径

                .contentType(MediaType.APPLICATION_JSON_UTF8)

                .syncBody(postBean)

                .retrieve() // 获取响应体

                .bodyToMono(String.class); //响应数据类型转换

  

        // 输出结果

        System.out.println(mono.block());

        return;

    }

}

(2)上面发送的 Bean 对象实际上会转成如下格式的 JSON 数据提交:

3.使用 Form 表单的形式提交数据

(1)下面样例使用 POST 方式发送 multipart/form-data 格式的数据:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

@RestController

public class HelloController {

  

    // 创建 WebClient 对象

    private WebClient webClient = WebClient.builder()

            .baseUrl("http://jsonplaceholder.typicode.com")

            .build();

  

    @GetMapping("/test")

    public void test() {

        //提交参数设置

        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();

        map.add("title", "abc");

        map.add("body", "航歌");

  

        // 发送请求

        Mono<String> mono = webClient

                .post() // POST 请求

                .uri("/posts"// 请求路径

                .contentType(MediaType.APPLICATION_FORM_URLENCODED)

                .body(BodyInserters.fromFormData(map))

                .retrieve() // 获取响应体

                .bodyToMono(String.class); //响应数据类型转换

  

        // 输出结果

        System.out.println(mono.block());

        return;

    }

}

(2)上面代码最终会通过如下这种 form 表单方式提交数据:

4.将结果转成自定义对象

​ 上面样例我们都是将响应结果以 String 形式接收,其实 WebClient 还可以自动将响应结果转成自定的对象或则数组。具体可以参考前面写的文章:

5.设置 url 参数

(1)如果 url 地址上面需要传递一些参数,可以使用占位符的方式:

1

2

String url = "http://jsonplaceholder.typicode.com/{1}/{2}";

String url = "http://jsonplaceholder.typicode.com/{type}/{id}";

(2)具体的用法可以参考前面写的文章:

6.subscribe 订阅(非阻塞式调用)

(1)前面的样例我们都是人为地使用 block 方法来阻塞当前程序。其实 WebClient 是异步的,也就是说等待响应的同时不会阻塞正在执行的线程。只有在响应结果准备就绪时,才会发起通知。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

@RestController

public class HelloController {

  

    // 创建 WebClient 对象

    private WebClient webClient = WebClient.builder()

            .baseUrl("http://jsonplaceholder.typicode.com")

            .build();

  

    @GetMapping("/test")

    public void test() {

        System.out.println("--- begin ---");

  

        // 需要提交的 json 字符串

        String jsonStr = "{\"userId\": 222,\"title\": \"abc\",\"body\": \"航歌\"}";

  

        Mono<String> mono = webClient

                .post() // POST 请求

                .uri("/posts"// 请求路径

                .contentType(MediaType.APPLICATION_JSON_UTF8)

                .body(BodyInserters.fromObject(jsonStr))

                .retrieve() // 获取响应体

                .bodyToMono(String.class); //响应数据类型转换

  

        // 订阅(异步处理结果)

        mono.subscribe(result -> {

            System.out.println(result);

        });

  

        System.out.println("--- end ---");

        return;

    }

}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

口罩佩戴智能监测摄像机

智能监测摄像机在现代城市安全管理中扮演着关键角色&#xff0c;尤其是像口罩佩戴智能监测摄像机这样的设备&#xff0c;其应用正在日益扩展&#xff0c;对于公共卫生和安全至关重要。 这类摄像机利用先进的图像识别技术&#xff0c;能够实时监测人群中是否佩戴口罩。通过高精度…

python基础语法学习(工程向)-Stage3-数据可视化

json 是一种轻量的数据交互格式&#xff0c;可以按照json指定的格式去组织和封装数据&#xff0c;而本质上是一个带有特定格式的字符串。 功能 json是在各个编程语言中流通的数据格式&#xff0c;负责不同编程语言之间的数据传递和交互。 格式 json的格式要求较为严格&#…

[Cloud Networking] Layer3 (Continue)

文章目录 1. DHCP Protocol1.1 DHCP 三种分配方式1.2 DHCP Relay (中继) 2. 路由协议 (Routing Protocol)2.1 RIP (Routing Information Protocol)2.2 OSPF Protocol2.2.1 OSPF Area2.2.2 Route ID / DR / BDR2.2.3 LSA / OSPF 邻居表 / LSDB / OSPF路由表 2.3 BGP Protocol2.4…

交易中的群体行为特征和决策模型

本文基于人的行为和心理特征&#xff0c;归纳出交易中群体的行为决策模型&#xff0c;并基于这个模型&#xff0c;分析股价波浪运行背后的逻辑&#xff0c;以及投机情绪的周期变化规律&#xff0c;以此指导交易&#xff0c;分析潜在的风险和机会&#xff0c;寻找并等待高性价比…

Python大数据-电商商品详情数据分析【JD电商平台为例】

一、项目背景 网上购物已经成为大众生活的重要组成部分。人们在电商平台上浏览商品并购物&#xff0c;产生了海量的用户行为数据&#xff0c;用户对商品的详情数据对商家具有重要的意义。利用好这些碎片化、非结构化的数据&#xff0c;将有利于企业在电商平台上的持续发展&…

mysql分析常用锁

这里写自定义目录标题 1.未提交事物&#xff0c;阻塞DDL&#xff0c;继而阻塞所有同表的后续操作,查看未提交事务的进程2.存着正在进行的线程数据。3.根据processlist表中的id杀掉未释放的线程4.查看正在使用的表5.mysql为什么state会有waiting for handler commit6.什么情况导…

鸿蒙实现金刚区效果

前言&#xff1a; DevEco Studio版本&#xff1a;4.0.0.600 所谓“金刚区"是位于APP功能入口的导航区域&#xff0c;通常以“图标文字”的宫格导航的形式出现。之所以叫“金刚区”&#xff0c;是因为该区域会随着业务目标的改变&#xff0c;展示不同的功能图标&#xff…

快速压缩前端项目

背景 作为前端开发工程师难免会遇到需要把项目压缩成压缩文件来传送的情况&#xff0c;这时候需要压缩软件进行压缩文件处理 问题 项目中的依赖包文件非常庞大&#xff0c;严重影响压缩速度&#xff0c;即使想先删除再压缩&#xff0c;删除文件也不会很快完成 解决 首先要安…

Jmeter如何进行分布式测试

使用Jmeter进行性能测试时&#xff0c;有些同学问我如果并发数比较大(比如最近项目需要支持1000并发)&#xff0c;单台电脑的配置(CPU和内存)可能无法支持&#xff0c;怎么办就需要使用分布式压测 1.分布式原理&#xff1a; 1、Jmeter分布式测试时&#xff0c;选择其中一台作…

数据库复习——范式(Normal Form)

因为上课的时候一直在摸鱼没有听懂&#xff0c;所以复习的时候理解一下数据库中关于范式的相关知识点。涉及范式的定义&#xff0c;以及给定一个函数依赖集判断是那种范式的方法。 范式 迄今为止一共提出了 6 6 6 种范式&#xff0c;他们的关系是 5 N F ⊂ 4 N F ⊂ B C N F …

UE5 C++ 跑酷游戏练习 Part1

一.修改第三人称模板的 Charactor 1.随鼠标将四处看的功能的输入注释掉。 void ARunGANCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) {// Set up action bindingsif (UEnhancedInputComponent* EnhancedInputComponent CastChecked&…

UML详解

1.what is the UML UML 全称是 Unified Modeling Language&#xff08;统一建模语言&#xff09;&#xff0c;它以图形的方式来描述软件的概念 2.它存在的目的 UML 的目标是通过一定结构的表达&#xff0c;来解决现实世界到软件世界的沟通问题。 3.什么是模&#xff0c;…

Centos7安装自动化运维Ansible

自动化运维Devops-Ansible Ansible是新出现的自动化运维工具&#xff0c;基于Python 开发&#xff0c;集合了众多运维工具&#xff08;puppet 、cfengine、chef、func、fabric&#xff09;的优点&#xff0c;实现了批量系统配置 、批量程序部署、批量运行命令 等功能。Ansible…

【每日刷题】Day68

【每日刷题】Day68 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 451. 根据字符出现频率排序 - 力扣&#xff08;LeetCode&#xff09; 2. 最小的K个数_牛客题霸_牛客…

github连接报本地

一、创建GIthub账号 这里默认大家已经创建好了并且有加速器&#xff0c;能正常上网&#xff0c;然后才能进行下面的操作。 二、创建ssh公钥 网址&#xff1a;Sign in to GitHub GitHub Sign in to GitHub GitHub 进入下面的界面&#xff1a; 然后创建新的密钥 三、官方文…

Excel/WPS《超级处理器》功能介绍与安装下载

超级处理器是基于Excel或WPS开发的一款插件&#xff0c;拥有近300个功能&#xff0c;非常简单高效的处理表格数据&#xff0c;安装即可使用。 点击此处&#xff1a;超i处理器安装下载 Excel菜单&#xff0c;显示如下图所示&#xff1a; WPS菜单显示&#xff0c;如下图所示&am…

【BES2500x系列 -- RTX5操作系统】CMSIS-RTOS RTX -- 实时操作系统的核心,为嵌入式系统注入活力 --(一)

&#x1f48c; 所属专栏&#xff1a;【BES2500x系列】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f49…

nodejs爬取小红书图片

昨天的文章已经描述了可以抓取评论区内容&#xff0c; 抓取图片内容和抓取评论区的内容基本一致 我们可以看到接口信息中含有图片链接&#xff0c;我们要做的就是爬取图片链接然后下载 这边要用到的模块为const downloadrequire(download) 将爬到的图片链接存放到images数组…

【解决问题】QApplication: No such file or directory,C++ 使用Qt或项目未正确加载Cmake报错

运行环境&#xff1a; Clion编译&#xff0c;构建C工程项目报错QApplication: No such file or directory 问题描述 QApplication: No such file or directory 引用的#include <QApplication>飘红 解决方案 1、Qt没有安装正确&#xff0c;请使用对应版本的Qt。或编译…

各类存储器类型(RAM、ROM、FLASH、DRAM、SRAM)

1 计算机存储类型构成 在计算机中&#xff0c;各类存储器构成了计算机能高速高效运转程序的基石。 计算机的存储体系中&#xff0c;从速度慢到速度快对应着容量大到小&#xff0c;也就是说&#xff0c;速度越快容量越小&#xff1b;容量越大的&#xff0c;速度越慢。两者互相…