RestTemplate 以及 WebClient 调用第三方接口使用总结

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

在这里插入图片描述

posted @ 2023-01-31 17:09  凡223  阅读(280)  评论(0编辑  收藏  举报