>在Spring5之前通常可以使用 Spring 提供的 RestTemplate 来调用接口,此时用它来调用是一个属于同步阻塞的方式获取返回结果,今天来使用以下异步非阻塞的方式远端调用接口
## 1. 与RestTemplate相比WebClient的优势
1. 非阻塞,支持更改的并发能力请求
2. 能够支持同步和异步的方式
3. 在Java8的代码中能够使用lambadas的箭头函数
4. RestTemplate 不适合在非阻塞应用程序中使用,因此 Spring WebFlux 应用程序应始终使用 WebClient。在大多数高并发场景中,WebClient 也应该是 Spring MVC 中的首选,并且用于编写一系列远程,相互依赖的调用。
## 2.直接上代码
1. 在pom文件中引入jar
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
```
## 3.创建 WebClient 实例
1. 利用 create() 创建,可以在create中协商IP或是域名,在下面的uri写完整的路径或是uri的路径
```java
/**
* 阻塞
* http://localhost:8001/webClient/getData
*/
@GetMapping("getData")
public void getData() {
WebClient webClient = WebClient.create();
Mono<String> stringMono = webClient
.get() //请求类型
.uri("https://api.dd16888.cn/baidu?access-key=93aa26aeb5f14819ae1c8d0865e2b69b&secret-key=1b497c79aee04758966f7127a8acd33d") //请求路径
.retrieve() //获取响应体
.bodyToMono(String.class);//返回类型;
log.info("获取数据:{}",stringMono.block());
}
```
2. 预先构建域名的访问地址
```java
/**
* 阻塞
* http://localhost:8001/webClient/getData1
*/
@GetMapping("getData1")
public void getData1() {
WebClient webClient = WebClient.create("https://api.dd16888.cn/baidu");
Mono<String> stringMono = webClient
.get() //请求类型
.uri("?access-key=93aa26aeb5f14819ae1c8d0865e2b69b&secret-key=1b497c79aee04758966f7127a8acd33d") //请求路径
.retrieve() //获取响应体
.bodyToMono(String.class);//返回类型;
log.info("获取数据:{}",stringMono.block());
}
```
3. 创建请求头,利用builder构建(推荐)我们可以通过返回的 WebClient.Builder 设置一些配置参数(例如:baseUrl、header、cookie 等),然后再调用 build 就可以返回 WebClient 对象了
```java
/**
* 利用 builder 创建(推荐)阻塞
* http://localhost:8001/webClient/getData2
*/
@GetMapping("getData2")
public JSONObject getData2() {
WebClient webClient = WebClient.builder()
.baseUrl("https://api.dd16888.cn")
.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<JSONObject> stringMono = webClient
.get() //请求类型
.uri("weibo?access-key=93aa26aeb5f14819ae1c8d0865e2b69b&secret-key=1b497c79aee04758966f7127a8acd33d") //请求路径
.retrieve() //获取响应体
.bodyToMono(JSONObject.class);//返回类型;
log.info("获取数据:{}",stringMono.block());
return stringMono.block();
}
```
## 4.关于GET的请求方式
### 4.1. 利用占位符路径,接收集合对象类型返回值
1. Java Bean实体类
```java
@Data
public class PostBean {
private Long userId;
private String title;
private String body;
}
```
2. 常规调用
```java
private final static String ak = "93aa26aeb5f14819ae1c8d0865e2b69b";
private final static String sk = "1b497c79aee04758966f7127a8acd33d";
/**
* 阻塞
* 占位符-返回集合对象
* http://localhost:8001/webClient/getData4
*/
@GetMapping("getData4")
public void getData4() {
WebClient webClient = WebClient.builder().baseUrl("https://api.dd16888.cn/baidu").build();
Flux<PostBean> postBeanFlux = webClient
.get() //请求类型
.uri("?access-key={ak}&secret-key={sk}", ak, sk) //请求路径
.retrieve() //获取响应体
.bodyToFlux(PostBean.class);//响应数据类型转换
List<PostBean> block = postBeanFlux.collectList().block();
log.info("根据占1位符获取数据:{}",block);
}
```
3. 附加另一种占位符的方式调用
```java
Map<String,Object> map = new HashMap<>();
{
map.put("ak", "93aa26aeb5f14819ae1c8d0865e2b69b");
map.put("sk", "1b497c79aee04758966f7127a8acd33d");
}
@GetMapping("getData3")
public void getData3() {
WebClient webClient = WebClient.create("https://api.dd16888.cn/baidu");
Mono<String> stringMono1 = webClient
.get() //请求类型
.uri("?access-key={ak}&secret-key={sk}",map) //请求路径
.retrieve() //获取响应体
.bodyToMono(String.class);//返回类型;
log.info("根据占2位符获取数据:{}",stringMono1.block());
}
```
### 4.2. subscribe 订阅(非阻塞式调用)
> 前面的样例我们都是人为地使用 block 方法来阻塞当前程序。其实 WebClient 是异步的,也就是说等待响应的同时不会阻塞正在执行的线程。只有在响应结果准备就绪时,才会发起通知。
```java
/**
* 异步非阻塞
* 占位符
* http://localhost:8001/webClient/getData5
*/
@GetMapping("getData5")
public void getData5() {
WebClient webClient = WebClient.create("https://api.dd16888.cn/baidu");
Mono<String> stringMono = webClient
.get() //请求类型
.uri("?access-key={ak}&secret-key={sk}",ak,sk) //请求路径
.retrieve() //获取响应体
.bodyToMono(String.class);//返回类型;
// 订阅(异步处理结果)
stringMono.subscribe(result -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("根据占1位符获取数据:{}",result);
});
log.info("结束");
}
```
### 4.3. 附:使用 exchange() 方法获取完整的响应内容
1. 前面我们都是使用 retrieve() 方法直接获取到了响应的内容,如果我们想获取到响应的头信息、Cookie 等,可以在通过 WebClient 请求时把调用 retrieve() 改为调用 exchange()。
2. 通过 exchange() 方法可以访问到代表响应结果的对象,通过该对象我们可以获取响应码、contentType、contentLength、响应消息体等。
3. 上代码(旧版本)
```java
/**
* 获取完整的请求内容(异步旧版)
* 占位符
* http://localhost:8001/webClient/getData7
*/
@GetMapping("getData7")
public void getData7() {
WebClient webClient = WebClient.create("https://api.dd16888.cn/baidu");
Mono<ClientResponse> stringMono = webClient
.get() //请求类型
.uri("?access-key={ak}&secret-key={sk}",ak,sk) //请求路径
.exchange();
// 同步的获取完整的响应对象
ClientResponse response = stringMono.block();
HttpStatus statusCode = response.statusCode(); // 获取响应码
int statusCodeValue = response.rawStatusCode(); // 获取响应码值
ClientResponse.Headers headers = response.headers(); // 获取响应头
// 获取响应体
Mono<String> resultMono = response.bodyToMono(String.class);
String body = resultMono.block();
// 输出结果
log.info("获取响应码:" + statusCode);
log.info("获取响应码值:" + statusCodeValue);
log.info("获取响应头:" + headers.asHttpHeaders());
log.info("获取响应体:" + body);
}
```
4. 新版本
```java
/**
* 获取完整的请求内容(异步新版)
* 占位符
* http://localhost:8001/webClient/getData6
*/
@GetMapping("getData6")
public void getData6() {
WebClient webClient = WebClient.create("https://api.dd16888.cn/baidu");
Mono<String> stringMono = webClient
.get() //请求类型
.uri("?access-key={ak}&secret-key={sk}",ak,sk) //请求路径
.exchangeToMono(response -> {
if (response.statusCode().is2xxSuccessful()) {
// 处理响应体
HttpStatus statusCode = response.statusCode(); // 获取响应码
int statusCodeValue = response.rawStatusCode(); // 获取响应码值
ClientResponse.Headers headers = response.headers(); // 获取响应头
// 输出结果
log.info("获取响应码:" + statusCode);
log.info("获取响应码值:" + statusCodeValue);
log.info("获取响应头:" + headers.asHttpHeaders());
Mono<String> stringMono1 = response.bodyToMono(String.class);
return response.bodyToMono(String.class);
// return Mono.just(response);
} else {
return Mono.error(new RuntimeException("Request failed with status code: " + response.statusCode()));
// return response.createException().flatMap(Mono::error);
}
});
stringMono.subscribe(data -> {
log.info("响应体:{}",data);
}, error -> {
// 处理异常
log.error("异常信息:",error);
});
}
```
## 5. POST请求
### 5.1. 发送一个 JSON 格式数据(使用 json 字符串)
1. 下面代码使用 post 方式发送一个 json 格式的字符串,并将结果打印出来(以字符串的形式),被调用方`不要用@RequestBody` 注解。
```java
/**
* post请求的json格式请求,被调用方不要用@RequestBody 注解
* http://localhost:8001/webClient/postData
*/
@GetMapping("postData")
public void postData() {
String jsonStr = "{\"access-key\": "+ak+",\"secret-key\": \""+sk+"\"}";
WebClient webClient = WebClient.builder()
.baseUrl("http://192.168.0.110:8003/")
.build();
Mono<String> stringMono = webClient
.post() //请求类型
.uri("createOrder") //请求路径
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(jsonStr))
.retrieve() // 获取响应体
.bodyToMono(String.class); //响应数据类型转换
log.info("POST请求的结果:{}",stringMono.block());
}
```
### 5.2. 发送一个 JSON 格式数据(使用 Java Bean)
1. 下面代码使用 post 方式发送一个 Bean 对象,并将结果打印出来(以字符串的形式)。结果同上面是一样的;被调用方`要用@RequestBody` 注解
```java
/**
* post请求的Java Bean格式请求,被调用方要用@RequestBody 注解
* http://localhost:8001/webClient/postData1
*/
@GetMapping("postData1")
public void postData1() {
// 要发送的数据对象
PostBean postBean = new PostBean();
postBean.setUserId(1L);
postBean.setTitle("abc");
postBean.setBody("航歌");
WebClient webClient = WebClient.builder()
.baseUrl("http://192.168.0.110:8003/")
.build();
Mono<String> stringMono = webClient
.post() //请求类型
.uri("createOrder") //请求路径
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(postBean)
.retrieve() // 获取响应体
.bodyToMono(String.class); //响应数据类型转换
log.info("POST请求的结果:{}",stringMono.block());
}
```
### 5.3. 使用 Form 表单的形式提交数据
1. 下面样例使用 POST 方式发送 multipart/form-data 格式的数据:
```java
/**
* post请求的 multipart/form-data格式请求
* http://localhost:8001/webClient/postData2
*/
@GetMapping("postData2")
public void postData2() {
// 要发送的数据对象
//提交参数设置
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("access-key", ak);
map.add("secret-key", sk);
WebClient webClient = WebClient.builder()
.baseUrl("https://api.dd16888.cn/") //请求路径
.build();
Mono<String> stringMono = webClient
.post() //请求类型
.uri("baidu") //请求路径
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromFormData(map))
.retrieve() // 获取响应体
.bodyToMono(String.class); //响应数据类型转换
log.info("POST请求的结果:{}",stringMono.block());
}
```
### 5.4. subscribe 订阅(非阻塞式调用)
1. 下面代码请求一个网络接口,并将响应体、响应头、响应码打印出来。其中响应体的类型设置为 String。
```java
/**
* post请求的 subscribe 订阅(非阻塞式调用),并且携带请求头的
* http://localhost:8001/webClient/postData3
*/
@GetMapping(value = "postData3",produces = { "application/json;charset=UTF-8" })
public String postData3() {
// 要发送的数据对象
//提交参数设置
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("access-key", ak);
map.add("secret-key", sk);
WebClient webClient = WebClient.builder()
.baseUrl("https://api.dd16888.cn/") //请求路径
.defaultHeader(HttpHeaders.USER_AGENT,"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)")
.build();
Mono<String> stringMono = webClient
.post() //请求类型
.uri("baidu") //请求路径
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromFormData(map))
.retrieve() // 获取响应体
.bodyToMono(String.class); //响应数据类型转换
// 订阅(异步处理结果)
stringMono.subscribe(result -> {
try {
Thread.sleep(5000);
log.info("POST请求的结果:{}",result);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
return "返回成功";
}
```
基于WebClient的网络调用的使用
在Spring5之前通常可以使用 Spring 提供的 RestTemplate 来调用接口,此时用它来调用是一个属于同步阻塞的方式获取返回结果,今天来使用以下异步非阻塞的方式远端调用接口
摘要由AtUtil通过智能技术生成
声明:本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。微信:ZDVIP51888;邮箱:8122356@qq.com。
本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明,转载时需注明出处: 内容转载自: 智编生态圈👉https://www.atutil.com/article/36
AtUtil
中间件
日常
本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明,转载时需注明出处: 内容转载自: 智编生态圈👉https://www.atutil.com/article/36