0


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
;
    
}
}
标签: java

本文转载自: https://blog.csdn.net/weixin_45433031/article/details/139792020
版权归原作者 keep one's resolveY 所有, 如有侵权,请联系我们删除。

“Spring中网络请求客户端WebClient的使用详解”的评论:

还没有评论