title: RestTemplate 以及 WebClient 调用第三方接口使用总结
date: 2023-01-31 16:51:29
tags:
- 开发技术及框架
categories: - 开发技术及框架
cover: https://cover.png
feature: false
1. RestTemplate
1.1 引入依赖
RestTemplate 在 spring-boot-starter-web 包下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
1.2 配置类
注入 RestTemplate,并进行连接时间等配置
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(15000);
factory.setReadTimeout(5000);
return factory;
}
}
1.3 工具类
RestTemplate 使用起来很简单,这里将 GET 方法和 POST 方法封装成了工具类,DELETE 和 PUT 方法使用方法相同,具体如下:
@Component
public class RestTemplateUtil {
private static RestTemplate restTemplate;
@Resource
private void setRestTemplate(RestTemplate restTemplate) {
RestTemplateUtil.staticSetRestTemplate(restTemplate);
}
/**
* 静态注入方法, 解决多线程下可能出现的并发问题
*
* @param restTemplate RestTemplate
* @author Fan
* @since 2023/1/13 17:24
*/
private static synchronized void staticSetRestTemplate(RestTemplate restTemplate) {
RestTemplateUtil.restTemplate = restTemplate;
}
/**
* 以 Get 方式请求第三方接口, getForEntity, 默认返回类型 String
*
* @param url 请求地址
* @return {@link String}
* @author Fan
* @since 2023/1/13 16:18
*/
public static String doGetWithEntity(String url) {
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
return responseEntity.getBody();
}
/**
* 以 Get 方式请求第三方接口, getForEntity, 指定返回类型
*
* @param url 请求地址
* @param clazz 返回类型
* @return {@link T}
* @author Fan
* @since 2023/1/13 17:08
*/
public static <T> T doGetWithEntity(String url, Class<T> clazz) {
ResponseEntity<T> responseEntity = restTemplate.getForEntity(url, clazz);
return responseEntity.getBody();
}
/**
* 以 Get 方式请求第三方接口, getForObject, 返回值返回的是响应体, 省了再去 getBody(), 默认返回类型 String
*
* @param url 请求地址
* @return {@link String}
* @author Fan
* @since 2023/1/13 16:19
*/
public static String doGetWithObject(String url) {
return restTemplate.getForObject(url, String.class);
}
/**
* 以 Get 方式请求第三方接口, getForObject, 返回值返回的是响应体, 省了再去 getBody(), 指定返回类型
*
* @param url 请求地址
* @param clazz 返回类型
* @return {@link T}
* @author Fan
* @since 2023/1/13 17:16
*/
public static <T> T doGetWithObject(String url, Class<T> clazz) {
return restTemplate.getForObject(url, clazz);
}
/**
* 以 Post 方式请求第三方接口, postForEntity, 默认返回类型 String
*
* @param url 请求地址
* @param object 请求参数
* @return {@link String}
* @author Fan
* @since 2023/1/13 16:28
*/
public static String doPostWithEntity(String url, Object object) {
ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, object, String.class);
return responseEntity.getBody();
}
/**
* 以 Post 方式请求第三方接口, postForEntity, 指定返回类型
*
* @param url 请求地址
* @param object 请求参数
* @param clazz 返回类型
* @return {@link T}
* @author Fan
* @since 2023/1/13 17:18
*/
public static <T> T doPostWithEntity(String url, Object object, Class<T> clazz) {
ResponseEntity<T> responseEntity = restTemplate.postForEntity(url, object, clazz);
return responseEntity.getBody();
}
/**
* 以 Post 方式请求第三方接口, postForObject, 返回值返回的是响应体, 省了再去 getBody(), 默认返回类型 String
*
* @param url 请求地址
* @param object 请求参数
* @return {@link String}
* @author Fan
* @since 2023/1/13 16:29
*/
public static String doPostWithObject(String url, Object object) {
return restTemplate.postForObject(url, object, String.class);
}
/**
* 以 Post 方式请求第三方接口, postForObject, 返回值返回的是响应体, 省了再去 getBody(), 指定返回类型
*
* @param url 请求地址
* @param object 请求参数
* @param clazz 返回类型
* @return {@link T}
* @author Fan
* @since 2023/1/13 17:19
*/
public static <T> T doPostWithObject(String url, Object object, Class<T> clazz) {
return restTemplate.postForObject(url, object, clazz);
}
}
1.4 使用
测试 Controller
@RestController
public class RestTemplateTestController {
@GetMapping("/test")
public String test() {
return RestTemplateUtil.doGetWithEntity("https://v1.hitokoto.cn");
}
}
启动项目,这里更改了端口为 8888,访问 http://localhost:8080/test
这里调用的是一言 API,调用成功
2. WebClient
Spring3.0 引入了 RestTemplate,但是在后来的官方源码中介绍,RestTemplate 有可能在未来的版本中被弃用。替代 RestTemplate,在 Spring5 中引入了 WebClient 作为非阻塞式 Reactive Http 客户端
2.1 引入依赖
WebClient 在 spring-boot-starter-webflux 包中
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2.2 使用
WebClient 不需要注入,直接使用即可
@RestController
public class WebClientSample {
/**
* WebClient.create() 方法
*
* @return {@link String}
* @author Fan
* @since 2023/1/31 14:01
*/
@GetMapping("/create")
public String create() {
// 一个 Mono 对象包含 0 个或 1 个元素
Mono<String> stringMono = WebClient
// 创建 WebClient 实例
.create()
// 方法调用, 请求类型
.method(HttpMethod.GET)
// 请求 uri
.uri("https://v1.hitokoto.cn")
// 获取响应结果
.retrieve()
// 将结果转换为指定类型
.bodyToMono(String.class);
// 返回最终调用结果,block 方法是阻塞的
return stringMono.block();
}
/**
* WebClient.create(String baseUrl): 指定 baseUrl, 使用该客户端发送请求都是基于 baseUrl
*
* @author Fan
* @since 2023/1/31 16:25
*/
@GetMapping("/createWithBaseUrl")
public void createWithBaseUrl() {
// 一个 Flux 对象包含 1 个或多个元素
Flux<String> stringFlux = WebClient.create("https://v1.hitokoto.cn")
.get()
// .uri("")
.retrieve()
.bodyToFlux(String.class);
// 非阻塞式获取响应结果
stringFlux.subscribe(this::handleResponse);
}
/**
* 异步响应回调
*
* @param response 响应
* @author Fan
* @since 2023/1/31 14:25
*/
private void handleResponse(String response) {
LogUtil.info(response);
}
/**
* WebClient.builder(): 返回一个 WebClient.Builder, 该对象可以做链式调用, 传递更多的参数 <br/>
* <ul>
* <li>uriBuilderFactory: 自定义 UriBuilderFactory 灵活配置使用 Uri</li>
* <li>defaultHeader: 为 HTTP 请求设置 Headers 请求头</li>
* <li>defaultCookie: 为 HTTP 请求设置 Cookies</li>
* <li>defaultRequest: 自定义 HttpRequest</li>
* <li>filter: 为 HTTP 请求增加客户端过滤器</li>
* <li>exchangeStrategies: HTTP 读写信息自定义</li>
* <li>clientConnector: HTTP 客户端连接器设置</li>
* </ul>
*
* @return {@link String}
* @author Fan
* @since 2023/1/31 14:02
*/
@GetMapping("/build")
public String build() {
return WebClient.builder()
.baseUrl("https://v1.hitokoto.cn")
.defaultHeader("build", "build")
.build()
.get()
// .uri("")
.retrieve()
.bodyToMono(String.class)
.block();
}
/**
* exchange() 方法获取 HTTP 响应完整内容
*
* @author Fan
* @since 2023/1/31 14:40
*/
@GetMapping("/exchange")
public String exchange() {
return WebClient.create()
.get()
.uri("https://v1.hitokoto.cn")
.exchangeToMono(clientResponse -> {
// 响应头
ClientResponse.Headers headers = clientResponse.headers();
// 响应状态
HttpStatus httpStatus = clientResponse.statusCode();
// 响应状态码
int rawStatusCode = clientResponse.rawStatusCode();
// 响应体
return clientResponse.bodyToMono(String.class);
}).block();
}
/**
* 数字占位符传参
*
* @author Fan
* @since 2023/1/31 14:50
*/
@GetMapping("/numParam")
public String numParam() {
return WebClient.create()
.method(HttpMethod.POST)
.uri("http://localhost:8080/user/{1}/{2}", ListUtil.list(false, 1, 2).toArray())
.retrieve()
.bodyToMono(String.class)
.block();
}
/**
* 参数名传参
*
* @author Fan
* @since 2023/1/31 14:53
*/
@GetMapping("/nameParam")
public String nameParam() {
return WebClient.create()
.method(HttpMethod.POST)
.uri("http://localhost:8080/user/{id}/{name}", "id", "name")
.retrieve()
.bodyToMono(String.class)
.block();
}
/**
* Map 传参
*
* @author Fan
* @since 2023/1/31 14:54
*/
@GetMapping("/mapParam")
public String mapParam() {
return WebClient.create()
.method(HttpMethod.POST)
.uri("http://localhost:8080/user/{id}/{name}",
MapUtil.ofEntries(false, MapUtil.entry("id", "id"), MapUtil.entry("name", "name")))
.retrieve()
.bodyToMono(String.class)
.block();
}
}
启动项目,这里更改了端口为 8888,访问 http://localhost:8888/build