WebClient 同步、异步调用实现对比

文章目录

  • 一、概述
  • 二、pom依赖
  • 三、代码结构
  • 四、源码传送
    • 1、异步代码
    • 2、同步代码
    • 3、完整代码

一、概述

WebClient是Spring WebFlux模块提供的一个非阻塞的基于响应式编程的进行Http请求的客户端工具,从Spring5.0开始WebClient作为RestTemplete的替代品,有更好的响应式能力,支持异步调用,可以在Spring项目中实现网络请求。

二、pom依赖

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webflux</artifactId>
			<version>5.2.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>io.projectreactor.netty</groupId>
			<artifactId>reactor-netty</artifactId>
			<version>0.9.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-slf4j-impl</artifactId>
			<version>2.12.1</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.13.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.10</version>
		</dependency>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.5</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.12</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
		</dependency>

三、代码结构

在这里插入图片描述

图片请手工放入 src\test\resources\123.jpg
在这里插入图片描述
单元测试
在这里插入图片描述

四、源码传送

1、异步代码

import java.awt.Desktop;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

import org.apache.commons.lang3.RandomUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;

import com.fly.http.bean.ImageShowDialog;
import com.fly.http.bean.JsonBeanUtils;
import com.fly.http.bean.SearchReq;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;

/**
 * http请求WebClient异步调用实现
 */
@Slf4j
public class WebClientAsyncTest
{
    private WebClient webClient = WebClient.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)).build();
    
    private void openImage(Resource resource)
    {
        try
        {
            new ImageShowDialog(ImageIO.read(resource.getInputStream()));
        }
        catch (IOException e)
        {
            log.error(e.getMessage(), e);
        }
    }
    
    @BeforeClass
    public static void init()
    {
        new File("download").mkdirs();
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws IOException
     * @throws InterruptedException
     */
    @Test
    public void testDownFile()
        throws IOException, InterruptedException
    {
        Mono<Resource> mono = webClient.get()
            .uri("https://00fly.online/upload/urls.txt")
            .accept(MediaType.IMAGE_JPEG)
            .exchange()
            .doOnSuccess(clientResponse -> log.info("----- headers: {}", clientResponse.headers()))
            .doOnSuccess(clientResponse -> log.info("----- statusCode: {}", clientResponse.statusCode()))
            .flatMap(clientResponse -> clientResponse.bodyToMono(Resource.class));
        
        // 保存到本地
        mono.subscribe(resource -> {
            try
            {
                FileCopyUtils.copy(resource.getInputStream(), new FileOutputStream("download/urls.txt"));
            }
            catch (IOException e)
            {
                log.error(e.getMessage(), e);
            }
        });
        TimeUnit.SECONDS.sleep(2);
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws IOException
     * @throws InterruptedException
     */
    @Test
    public void testDownImg001()
        throws IOException, InterruptedException
    {
        Mono<Resource> mono = webClient.get()
            .uri("https://00fly.online/upload/2019/02/201902262129360274AKuFZcUfip.jpg")
            .accept(MediaType.IMAGE_JPEG)
            .retrieve() // 获取响应体
            .bodyToMono(Resource.class);
        mono.subscribe(resource -> openImage(resource));
        TimeUnit.SECONDS.sleep(10);
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws IOException
     * @throws InterruptedException
     */
    @Test
    public void testDownImg002()
        throws IOException, InterruptedException
    {
        Mono<Resource> mono = webClient.get()
            .uri("https://00fly.online/upload/2019/02/201902262129360274AKuFZcUfip.jpg")
            .accept(MediaType.IMAGE_JPEG)
            .retrieve() // 获取响应体
            .bodyToMono(Resource.class);
        
        // 保存到本地
        mono.subscribe(resource -> {
            try
            {
                File dest = new File(String.format("download/img_%s.jpg", System.currentTimeMillis()));
                FileCopyUtils.copy(resource.getInputStream(), new FileOutputStream(dest));
                if (Desktop.isDesktopSupported())
                {
                    Desktop.getDesktop().open(dest.getParentFile());
                }
            }
            catch (IOException e)
            {
                log.error(e.getMessage(), e);
            }
        });
        TimeUnit.SECONDS.sleep(2);
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testExchange001()
        throws InterruptedException
    {
        // get
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        Mono<ClientResponse> monoGet = webClient.get()
            .uri(uriBuilder -> uriBuilder.scheme("https")
                .host("httpbin.org")
                .path("/get")
                .queryParams(params) // 等价 queryParam("q1", "java").queryParam("q2", "python")
                .build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange();
        
        monoGet.subscribe(clientResponse -> {
            log.info("----- headers: {}", clientResponse.headers());
            log.info("----- statusCode: {}", clientResponse.statusCode());
            clientResponse.bodyToMono(String.class).subscribe(body -> log.info("----- reponse: {}", body));
        });
        TimeUnit.SECONDS.sleep(2);
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testExchange002()
        throws InterruptedException
    {
        // get
        Mono<ClientResponse> monoGet = webClient.get()
            .uri(uriBuilder -> uriBuilder.scheme("https").host("httpbin.org").path("/get").queryParam("q1", "java").queryParam("q2", "python").build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange();
        monoGet.subscribe(clientResponse -> {
            log.info("----- headers: {}", clientResponse.headers());
            log.info("----- statusCode: {}", clientResponse.statusCode());
            clientResponse.bodyToMono(String.class).subscribe(body -> log.info("----- reponse: {}", body));
        });
        
        // formData post
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        Mono<ClientResponse> monoPost = webClient.post()
            .uri("https://httpbin.org/post")
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromFormData(params)) // 设置formData,等价 BodyInserters.fromFormData("q1", "java").with("q2", "python")
            .exchange();
        monoPost.subscribe(clientResponse -> {
            log.info("----- headers: {}", clientResponse.headers());
            log.info("----- statusCode: {}", clientResponse.statusCode());
            clientResponse.bodyToMono(String.class).subscribe(body -> log.info("----- reponse: {}", body));
        });
        TimeUnit.SECONDS.sleep(2);
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testFormDataPost()
        throws InterruptedException
    {
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        Mono<String> mono = webClient.post()
            .uri("https://httpbin.org/post")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromFormData(params)) // 设置formData,等价 BodyInserters.fromFormData("q1", "java").with("q2", "python")
            .retrieve() // 获取响应体
            .bodyToMono(String.class); // 响应数据类型转换
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testGet001()
        throws InterruptedException
    {
        Mono<String> mono = webClient.get()
            .uri("https://httpbin.org/{method}", "get") // {任意命名}
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .retrieve()
            .bodyToMono(String.class);
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testGet002()
        throws InterruptedException
    {
        Mono<ClientResponse> mono = webClient.get().uri("https://httpbin.org/get").acceptCharset(StandardCharsets.UTF_8).accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML).exchange();
        mono.subscribe(reponse -> {
            log.info("----- headers: {}", reponse.headers());
            log.info("----- statusCode: {}", reponse.statusCode());
            reponse.bodyToMono(String.class).subscribe(body -> log.info("----- reponse: {}", body));
        });
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testGet003()
        throws InterruptedException
    {
        Mono<String> mono = webClient.get()
            .uri("https://httpbin.org/get")
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange()
            .doOnSuccess(clientResponse -> log.info("----- headers: {}", clientResponse.headers()))
            .doOnSuccess(clientResponse -> log.info("----- statusCode: {}", clientResponse.statusCode()))
            .flatMap(clientResponse -> clientResponse.bodyToMono(String.class));
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用, https://httpbin.org/get?q=java
     * 
     * @throws InterruptedException
     */
    @Test
    public void testGet004()
        throws InterruptedException
    {
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q", "java");
        String uri = UriComponentsBuilder.fromUriString("https://httpbin.org/get").queryParams(params).toUriString();
        
        // 注意比较
        // uri = UriComponentsBuilder.fromUriString("https://httpbin.org/get").queryParam("q", "java", "python").toUriString();
        // uri = UriComponentsBuilder.fromUriString("https://httpbin.org/get").queryParam("q1", "java").queryParam("q2", "python").toUriString();
        
        Mono<String> mono = webClient.get()
            .uri(uri)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange()
            .doOnSuccess(clientResponse -> log.info("----- headers: {}", clientResponse.headers()))
            .doOnSuccess(clientResponse -> log.info("----- statusCode: {}", clientResponse.statusCode()))
            .flatMap(clientResponse -> clientResponse.bodyToMono(String.class));
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testGet005()
        throws InterruptedException
    {
        Mono<String> mono = webClient.get()
            .uri(uriBuilder -> uriBuilder.scheme("https").host("httpbin.org").path("/get").queryParam("q1", "java").queryParam("q2", "python").build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .retrieve()
            .bodyToMono(String.class);
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testJsonBody001()
        throws InterruptedException
    {
        Mono<String> mono = webClient.post()
            .uri(uriBuilder -> uriBuilder.scheme("https").host("httpbin.org").path("/post").build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(Collections.singletonMap("q", "java"))
            .retrieve()
            .bodyToMono(String.class);
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws IOException
     * @throws InterruptedException
     */
    @Test
    public void testJsonBody002()
        throws IOException, InterruptedException
    {
        Mono<String> mono;
        int num = RandomUtils.nextInt(1, 4);
        switch (num)
        {
            case 1: // 方式1,javaBean
                SearchReq req = new SearchReq();
                req.setPageNo(1);
                req.setPageSize(10);
                req.setKeyword("1");
                mono = webClient.post()
                    .uri("https://httpbin.org/post")
                    .acceptCharset(StandardCharsets.UTF_8)
                    .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
                    .bodyValue(req) // 设置JsonBody
                    .retrieve()
                    .bodyToMono(String.class);
                mono.subscribe(body -> log.info("reponse: {}", body));
                break;
            
            case 2: // 方式2,HashMap
                Map<String, String> params = new HashMap<>();
                params.put("pageNo", "2");
                params.put("pageSize", "20");
                params.put("keyword", "2");
                mono = webClient.post()
                    .uri("https://httpbin.org/post")
                    .acceptCharset(StandardCharsets.UTF_8)
                    .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
                    .bodyValue(params) // 设置JsonBody
                    .retrieve()
                    .bodyToMono(String.class);
                mono.subscribe(body -> log.info("reponse: {}", body));
                break;
            
            case 3: // 方式3,json字符串
                Map<String, String> params2 = new HashMap<>();
                params2.put("pageNo", "2");
                params2.put("pageSize", "20");
                params2.put("keyword", "2");
                mono = webClient.post()
                    .uri("https://httpbin.org/post")
                    .acceptCharset(StandardCharsets.UTF_8)
                    .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
                    .body(BodyInserters.fromValue(JsonBeanUtils.beanToJson(params2, false))) // 设置formData
                    .retrieve()
                    .bodyToMono(String.class);
                mono.subscribe(body -> log.info("reponse: {}", body));
                break;
        }
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testUpload001()
        throws InterruptedException
    {
        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        params.add("file", new ClassPathResource("123.jpg"));
        Mono<String> mono = webClient.post()
            .uri("https://httpbin.org/post")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromMultipartData(params)) // 设置formData,等价 BodyInserters.fromFormData("q1", "java").with("q2", "python")
            .retrieve() // 获取响应体
            .bodyToMono(String.class); // 响应数据类型转换
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testUpload002()
        throws InterruptedException
    {
        Mono<String> mono = webClient.post()
            .uri("https://httpbin.org/post")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromMultipartData("q1", "java").with("q2", "python").with("file", new ClassPathResource("123.jpg")))
            .retrieve() // 获取响应体
            .bodyToMono(String.class); // 响应数据类型转换
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
}

2、同步代码


import java.awt.Desktop;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

import org.apache.commons.lang3.RandomUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;

import com.fly.http.bean.ImageShowDialog;
import com.fly.http.bean.JsonBeanUtils;
import com.fly.http.bean.SearchReq;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;

/**
 * http请求WebClient同步调用实现
 */
@Slf4j
public class WebClientSyncTest
{
    private WebClient webClient = WebClient.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)).build();
    
    private void openImage(Resource resource)
    {
        try
        {
            new ImageShowDialog(ImageIO.read(resource.getInputStream()));
        }
        catch (IOException e)
        {
            log.error(e.getMessage(), e);
        }
    }
    
    @BeforeClass
    public static void init()
    {
        new File("download").mkdirs();
    }
    
    /**
     * WebClient同步调用
     * 
     * @throws IOException
     */
    @Test
    public void testDownFile()
        throws IOException
    {
        Mono<ClientResponse> mono = webClient.get().uri("https://00fly.online/upload/urls.txt").accept(MediaType.IMAGE_JPEG).exchange();
        ClientResponse response = mono.block();
        log.info("----- headers: {}", response.headers());
        log.info("----- statusCode: {}", response.statusCode());
        
        // 保存到本地
        Resource resource = response.bodyToMono(Resource.class).block();
        FileCopyUtils.copy(resource.getInputStream(), new FileOutputStream("download/urls.txt"));
    }
    
    /**
     * WebClient同步调用
     * 
     * @throws IOException
     * @throws InterruptedException
     */
    @Test
    public void testDownImg001()
        throws IOException, InterruptedException
    {
        Mono<Resource> mono = webClient.get()
            .uri("https://00fly.online/upload/2019/02/201902262129360274AKuFZcUfip.jpg")
            .accept(MediaType.IMAGE_JPEG)
            .retrieve() // 获取响应体
            .bodyToMono(Resource.class);
        openImage(mono.block());
        TimeUnit.SECONDS.sleep(10);
    }
    
    /**
     * WebClient同步调用
     * 
     * @throws IOException
     */
    @Test
    public void testDownImg002()
        throws IOException
    {
        Mono<Resource> mono = webClient.get()
            .uri("https://00fly.online/upload/2019/02/201902262129360274AKuFZcUfip.jpg")
            .accept(MediaType.IMAGE_JPEG)
            .retrieve() // 获取响应体
            .bodyToMono(Resource.class);
        
        // 保存到本地
        Resource resource = mono.block();
        File dest = new File(String.format("download/img_%s.jpg", System.currentTimeMillis()));
        FileCopyUtils.copy(resource.getInputStream(), new FileOutputStream(dest));
        if (Desktop.isDesktopSupported())
        {
            Desktop.getDesktop().open(dest.getParentFile());
        }
    }
    
    /**
     * WebClient同步调用
     */
    @Test
    public void testExchange001()
    {
        // get
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        Mono<ClientResponse> monoGet = webClient.get()
            .uri(uriBuilder -> uriBuilder.scheme("https")
                .host("httpbin.org")
                .path("/get")
                .queryParams(params) // 等价 queryParam("q1", "java").queryParam("q2", "python")
                .build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange();
        
        ClientResponse clientResponse = monoGet.block(); // 获取完整的响应对象
        log.info("----- headers: {}", clientResponse.headers());
        log.info("----- statusCode: {}", clientResponse.statusCode());
        log.info("----- reponse: {}", clientResponse.bodyToMono(String.class).block());
    }
    
    /**
     * WebClient同步调用
     */
    @Test
    public void testExchange002()
    {
        // get
        Mono<ClientResponse> monoGet = webClient.get()
            .uri(uriBuilder -> uriBuilder.scheme("https").host("httpbin.org").path("/get").queryParam("q1", "java").queryParam("q2", "python").build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange();
        ClientResponse clientResponse = monoGet.block(); // 获取完整的响应对象
        log.info("----- headers: {}", clientResponse.headers());
        log.info("----- statusCode: {}", clientResponse.statusCode());
        log.info("----- reponse: {}", clientResponse.bodyToMono(String.class).block());
        
        // formData post
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        Mono<ClientResponse> monoPost = webClient.post()
            .uri("https://httpbin.org/post")
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromFormData(params)) // 设置formData,等价 BodyInserters.fromFormData("q1", "java").with("q2", "python")
            .exchange();
        ClientResponse clientResponse2 = monoPost.block(); // 获取完整的响应对象
        log.info("----- headers: {}", clientResponse2.headers());
        log.info("----- statusCode: {}", clientResponse2.statusCode());
        log.info("----- reponse: {}", clientResponse2.bodyToMono(String.class).block());
    }
    
    /**
     * WebClient同步调用
     */
    @Test
    public void testFormDataPost()
    {
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        Mono<String> mono = webClient.post()
            .uri("https://httpbin.org/post")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromFormData(params)) // 设置formData,等价 BodyInserters.fromFormData("q1", "java").with("q2", "python")
            .retrieve() // 获取响应体
            .bodyToMono(String.class); // 响应数据类型转换
        log.info("reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用
     */
    @Test
    public void testGet001()
    {
        Mono<String> mono = webClient.get()
            .uri("https://httpbin.org/{method}", "get") // {任意命名}
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .retrieve()
            .bodyToMono(String.class);
        log.info("reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用
     */
    @Test
    public void testGet002()
    {
        Mono<String> mono = webClient.get()
            .uri("https://httpbin.org/get")
            .acceptCharset(StandardCharsets.UTF_8)
            .exchange()
            .doOnSuccess(clientResponse -> log.info("----- headers: {}", clientResponse.headers()))
            .doOnSuccess(clientResponse -> log.info("----- statusCode: {}", clientResponse.statusCode()))
            .flatMap(clientResponse -> clientResponse.bodyToMono(String.class));
        log.info("reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用
     * 
     */
    @Test
    public void testGet003()
    {
        Mono<String> mono = webClient.get()
            .uri("https://httpbin.org/get")
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange()
            .doOnSuccess(clientResponse -> log.info("----- headers: {}", clientResponse.headers()))
            .doOnSuccess(clientResponse -> log.info("----- statusCode: {}", clientResponse.statusCode()))
            .flatMap(clientResponse -> clientResponse.bodyToMono(String.class));
        log.info("reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用, https://httpbin.org/get?q=java
     * 
     */
    @Test
    public void testGet004()
    {
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q", "java");
        String uri = UriComponentsBuilder.fromUriString("https://httpbin.org/get").queryParams(params).toUriString();
        
        // 注意比较
        // uri = UriComponentsBuilder.fromUriString("https://httpbin.org/get").queryParam("q", "java", "python").toUriString();
        // uri = UriComponentsBuilder.fromUriString("https://httpbin.org/get").queryParam("q1", "java").queryParam("q2", "python").toUriString();
        
        Mono<String> mono = webClient.get()
            .uri(uri)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange()
            .doOnSuccess(clientResponse -> log.info("----- headers: {}", clientResponse.headers()))
            .doOnSuccess(clientResponse -> log.info("----- statusCode: {}", clientResponse.statusCode()))
            .flatMap(clientResponse -> clientResponse.bodyToMono(String.class));
        log.info("reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用
     * 
     */
    @Test
    public void testGet005()
    {
        Mono<String> mono = webClient.get()
            .uri(uriBuilder -> uriBuilder.scheme("https").host("httpbin.org").path("/get").queryParam("q1", "java").queryParam("q2", "python").build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .retrieve()
            .bodyToMono(String.class);
        log.info("reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用
     * 
     */
    @Test
    public void testJsonBody001()
    {
        Mono<String> mono = webClient.post()
            .uri(uriBuilder -> uriBuilder.scheme("https").host("httpbin.org").path("/post").build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(Collections.singletonMap("q", "java"))
            .retrieve()
            .bodyToMono(String.class);
        log.info("reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用
     * 
     * @throws IOException
     */
    @Test
    public void testJsonBody002()
        throws IOException
    {
        Mono<String> mono;
        int num = RandomUtils.nextInt(1, 4);
        switch (num)
        {
            case 1: // 方式1,javaBean
                SearchReq req = new SearchReq();
                req.setPageNo(1);
                req.setPageSize(10);
                req.setKeyword("1");
                mono = webClient.post()
                    .uri("https://httpbin.org/post")
                    .acceptCharset(StandardCharsets.UTF_8)
                    .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
                    .bodyValue(req) // 设置JsonBody
                    .retrieve()
                    .bodyToMono(String.class);
                log.info("reponse: {}", mono.block());
                break;
            
            case 2: // 方式2,HashMap
                Map<String, String> params = new HashMap<>();
                params.put("pageNo", "2");
                params.put("pageSize", "20");
                params.put("keyword", "2");
                mono = webClient.post()
                    .uri("https://httpbin.org/post")
                    .acceptCharset(StandardCharsets.UTF_8)
                    .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
                    .bodyValue(params) // 设置JsonBody
                    .retrieve()
                    .bodyToMono(String.class);
                log.info("reponse: {}", mono.block());
                break;
            
            case 3: // 方式3,json字符串
                Map<String, String> params2 = new HashMap<>();
                params2.put("pageNo", "2");
                params2.put("pageSize", "20");
                params2.put("keyword", "2");
                mono = webClient.post()
                    .uri("https://httpbin.org/post")
                    .acceptCharset(StandardCharsets.UTF_8)
                    .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
                    .body(BodyInserters.fromValue(JsonBeanUtils.beanToJson(params2, false))) // 设置formData
                    .retrieve()
                    .bodyToMono(String.class);
                log.info("reponse: {}", mono.block());
                break;
        }
    }
    
    /**
     * WebClient同步调用
     * 
     */
    @Test
    public void testUpload001()
    {
        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        params.add("file", new ClassPathResource("123.jpg"));
        Mono<String> mono = webClient.post()
            .uri("https://httpbin.org/post")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromMultipartData(params)) // 设置formData,等价 BodyInserters.fromFormData("q1", "java").with("q2", "python")
            .retrieve() // 获取响应体
            .bodyToMono(String.class); // 响应数据类型转换
        log.info("----- reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用
     * 
     */
    @Test
    public void testUpload002()
    {
        Mono<String> mono = webClient.post()
            .uri("https://httpbin.org/post")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromMultipartData("q1", "java").with("q2", "python").with("file", new ClassPathResource("123.jpg")))
            .retrieve() // 获取响应体
            .bodyToMono(String.class); // 响应数据类型转换
        log.info("----- reponse: {}", mono.block());
    }
}

3、完整代码

如何使用下面的备份文件恢复成原始的项目代码,请移步查阅:神奇代码恢复工具

//goto docker\docker-compose.yml
version: '3.7'
services:
  hello:
    image: registry.cn-shanghai.aliyuncs.com/00fly/web-client:0.0.1
    container_name: web-client
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 200M
        reservations:
          cpus: '0.05'
          memory: 100M
    environment:
      JAVA_OPTS: -server -Xms100m -Xmx100m -Djava.security.egd=file:/dev/./urandom
    restart: on-failure
    logging:
      driver: json-file
      options:
        max-size: 5m
        max-file: '1'


//goto docker\restart.sh
#!/bin/bash
docker-compose down && docker system prune -f && docker-compose up -d && docker stats
//goto docker\stop.sh
#!/bin/bash
docker-compose down
//goto Dockerfile
FROM openjdk:8-jre-alpine

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone

COPY target/web-client-*.jar  /app.jar

ENTRYPOINT ["java","-jar","/app.jar"]
//goto pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.fly</groupId>
	<artifactId>web-client</artifactId>
	<version>0.0.1</version>
	<name>web-client</name>
	<packaging>jar</packaging>
	<properties>
		<docker.hub>registry.cn-shanghai.aliyuncs.com</docker.hub>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
		<skipTests>true</skipTests>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webflux</artifactId>
			<version>5.2.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>io.projectreactor.netty</groupId>
			<artifactId>reactor-netty</artifactId>
			<version>0.9.4.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-slf4j-impl</artifactId>
			<version>2.12.1</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.13.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.10</version>
		</dependency>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.5</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.12</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>${project.artifactId}-${project.version}</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.10.1</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-shade-plugin</artifactId>
				<version>3.4.0</version>
				<configuration>
					<createDependencyReducedPom>false</createDependencyReducedPom>
				</configuration>
				<executions>
					<execution>
						<phase>package</phase>
						<goals>
							<goal>shade</goal>
						</goals>
						<configuration>
							<minimizeJar>false</minimizeJar>
							<filters>
								<filter>
									<artifact>*:*</artifact>
								</filter>
							</filters>
							<transformers>
								<!--MANIFEST文件中写入Main-Class是可执行包的必要条件。ManifestResourceTransformer可以轻松实现。 -->
								<transformer
									implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
									<mainClass>com.fly.http.RunMain</mainClass>
								</transformer>
							</transformers>
						</configuration>
					</execution>
				</executions>
			</plugin>

			<!-- 添加docker-maven插件 -->
			<plugin>
				<groupId>io.fabric8</groupId>
				<artifactId>docker-maven-plugin</artifactId>
				<version>0.40.0</version>
				<executions>
					<execution>
						<phase>package</phase>
						<goals>
							<goal>build</goal>
							<goal>push</goal>
							<goal>remove</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<!-- 连接到带docker环境的linux服务器编译image -->
					<!-- <dockerHost>http://192.168.182.10:2375</dockerHost> -->

					<!-- Docker 推送镜像仓库地址 -->
					<pushRegistry>${docker.hub}</pushRegistry>
					<images>
						<image>
							<name>
								${docker.hub}/00fly/${project.artifactId}:${project.version}</name>
							<build>
								<dockerFileDir>${project.basedir}</dockerFileDir>
							</build>
						</image>
					</images>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>
//goto src\main\java\com\fly\http\FluxWebClient.java
package com.fly.http;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;

import lombok.extern.slf4j.Slf4j;

/**
 * WebClient是RestTemplete的替代品,有更好的响应式能力,支持异步调用<br>
 * 
 * https://blog.csdn.net/zzhongcy/article/details/105412842
 * 
 */
@Slf4j
public class FluxWebClient
{
    private List<String> urls = new ArrayList<>();
    
    // 缓冲区默认256k,设为-1以解决报错Exceeded limit on max bytes to buffer : 262144
    private WebClient webClient = WebClient.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)).build();
    
    public void visitAll()
    {
        // block转换为同步调用
        if (urls.isEmpty())
        {
            log.info("★★★★★★★★ urls isEmpty, now get urls from api ★★★★★★★★");
            String resp = webClient.get().uri("https://00fly.online/upload/urls.txt").acceptCharset(StandardCharsets.UTF_8).accept(MediaType.TEXT_HTML).retrieve().bodyToMono(String.class).block();
            urls = Arrays.asList(StringUtils.split(resp, "\r\n"));
        }
        
        // 异步访问
        AtomicInteger count = new AtomicInteger(0);
        urls.stream()
            .filter(url -> RandomUtils.nextBoolean())
            .forEach(url -> webClient.get()
                .uri(url)
                .acceptCharset(StandardCharsets.UTF_8)
                .accept(MediaType.TEXT_HTML)
                .retrieve()
                .bodyToMono(String.class)
                .subscribe(r -> log.info("process complted: {}. {}", count.incrementAndGet(), url), e -> log.error(e.getMessage())));
        log.info("total:{} ==> ############## 异步请求已提交 ##############", urls.size());
    }
}
//goto src\main\java\com\fly\http\RunMain.java
package com.fly.http;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class RunMain
{
    private static FluxWebClient webClient = new FluxWebClient();
    
    /**
     * 程序运行入口
     * 
     */
    public static void main(String[] args)
    {
        scheduledThreadPoolExecutorStart();
    }
    
    private static void scheduledThreadPoolExecutorStart()
    {
        new ScheduledThreadPoolExecutor(2).scheduleAtFixedRate(() -> {
            webClient.visitAll();
        }, 0L, 30, TimeUnit.SECONDS);
        log.info("======== ScheduledThreadPoolExecutor started!");
    }
    
    /**
     * Timer线程安全, 但单线程执行, 抛出异常时, task会终止
     */
    @Deprecated
    protected static void timeStart()
    {
        new Timer().scheduleAtFixedRate(new TimerTask()
        {
            @Override
            public void run()
            {
                webClient.visitAll();
            }
        }, 0L, 30 * 1000L);
        log.info("======== Timer started!");
    }
}
//goto src\main\resources\log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="off" monitorInterval="0">
	<appenders>
		<console name="Console" target="system_out">
			<patternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %5p %pid --- [%t] %-30.30c{1.} : %m%n" />
		</console>
	</appenders>
	<loggers>
		<root level="INFO">
			<appender-ref ref="Console" />
		</root>
	</loggers>
</configuration>
//goto src\test\java\com\fly\http\ApiTest.java
package com.fly.http;

import java.awt.Desktop;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.util.ResourceUtils;
import org.springframework.web.reactive.function.client.WebClient;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ApiTest
{
    // 缓冲区默认256k,设为-1以解决报错Exceeded limit on max bytes to buffer : 262144
    private WebClient webClient = WebClient.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)).build();
    
    /**
     * 写入文本文件
     * 
     * @param urls
     * @see [类、类#方法、类#成员]
     */
    private void process(List<String> urls)
    {
        try
        {
            if (ResourceUtils.isFileURL(ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX)))
            {
                String path = ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX).getPath() + "urls.txt";
                File dest = new File(path);
                FileUtils.writeLines(dest, StandardCharsets.UTF_8.name(), urls);
                Desktop.getDesktop().open(dest);
            }
        }
        catch (IOException e)
        {
            log.error(e.getMessage(), e);
        }
    }
    
    @Test
    public void test001()
        throws IOException
    {
        String jsonBody = webClient.get()
            .uri("https://00fly.online/upload/data.json")
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON)
            .retrieve()
            .bodyToMono(String.class)
            .block()
            .replace("{", "{\n")
            .replace("}", "}\n")
            .replace(",", ",\n");
        try (InputStream is = new ByteArrayInputStream(jsonBody.getBytes(StandardCharsets.UTF_8)))
        {
            List<String> urls = IOUtils.readLines(is, StandardCharsets.UTF_8).stream().filter(line -> StringUtils.contains(line, "\"url\":")).map(n -> StringUtils.substringBetween(n, ":\"", "\",")).collect(Collectors.toList());
            log.info("★★★★★★★★ urls: {} ★★★★★★★★", urls.size());
            process(urls);
        }
    }
    
    @Test
    public void test002()
        throws IOException
    {
        Resource resource = new ClassPathResource("data.json");
        String jsonBody = IOUtils.toString(resource.getInputStream(), StandardCharsets.UTF_8).replace("{", "{\n").replace("}", "}\n").replace(",", ",\n");
        try (InputStream is = new ByteArrayInputStream(jsonBody.getBytes(StandardCharsets.UTF_8)))
        {
            List<String> urls = IOUtils.readLines(is, StandardCharsets.UTF_8).stream().filter(line -> StringUtils.contains(line, "\"url\":")).map(n -> StringUtils.substringBetween(n, ":\"", "\",")).collect(Collectors.toList());
            log.info("★★★★★★★★ urls: {} ★★★★★★★★", urls.size());
            process(urls);
        }
    }
    
    @Test
    public void test003()
    {
        String resp = webClient.get().uri("https://00fly.online/upload/urls.txt").acceptCharset(StandardCharsets.UTF_8).accept(MediaType.TEXT_HTML).retrieve().bodyToMono(String.class).block();
        List<String> urls = Arrays.asList(StringUtils.split(resp, "\r\n"));
        AtomicInteger count = new AtomicInteger(0);
        urls.stream().forEach(url -> log.info("{}. {}", count.incrementAndGet(), url));
    }
}
//goto src\test\java\com\fly\http\bean\ImageShowDialog.java
package com.fly.http.bean;

import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JLabel;

import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

/**
 * 
 * 弹出窗口
 * 
 * @author 00fly
 * @version [版本号, 2023年3月3日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class ImageShowDialog extends JDialog
{
    private static final long serialVersionUID = -7240357454480002551L;
    
    public static void main(String[] args)
        throws IOException
    {
        Resource resources = new ClassPathResource("123.jpg");
        BufferedImage image = ImageIO.read(resources.getInputStream());
        new ImageShowDialog(image);
    }
    
    public ImageShowDialog(BufferedImage image)
    {
        super();
        setTitle("图片查看工具");
        setSize(image.getWidth(), image.getHeight() + 30);
        Dimension screenSize = getToolkit().getScreenSize();
        Dimension dialogSize = getSize();
        dialogSize.height = Math.min(screenSize.height, dialogSize.height);
        dialogSize.width = Math.min(screenSize.width, dialogSize.width);
        setLocation((screenSize.width - dialogSize.width) / 2, (screenSize.height - dialogSize.height) / 2);
        add(new JLabel(new ImageIcon(image)));
        setVisible(true);
        setResizable(false);
        setAlwaysOnTop(true);
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    }
}
//goto src\test\java\com\fly\http\bean\JsonBeanUtils.java
package com.fly.http.bean;

import java.io.IOException;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * JsonBean转换工具
 * 
 * @author 00fly
 *
 */
public class JsonBeanUtils
{
    private static ObjectMapper objectMapper = new ObjectMapper();
    
    /**
     * bean转json字符串
     * 
     * @param bean
     * @return
     * @throws IOException
     */
    public static String beanToJson(Object bean)
        throws IOException
    {
        String jsonText = objectMapper.writeValueAsString(bean);
        return objectMapper.readTree(jsonText).toPrettyString();
    }
    
    /**
     * bean转json字符串
     * 
     * @param bean
     * @param pretty 是否格式美化
     * @return
     * @throws IOException
     */
    public static String beanToJson(Object bean, boolean pretty)
        throws IOException
    {
        String jsonText = objectMapper.writeValueAsString(bean);
        if (pretty)
        {
            return objectMapper.readTree(jsonText).toPrettyString();
        }
        return objectMapper.readTree(jsonText).toString();
    }
    
    /**
     * json字符串转bean
     * 
     * @param jsonText
     * @return
     * @throws IOException
     */
    public static <T> T jsonToBean(String jsonText, Class<T> clazz)
        throws IOException
    {
        return objectMapper.readValue(jsonText, clazz);
    }
    
    /**
     * json字符串转bean
     * 
     * @param jsonText
     * @return
     * @throws IOException
     */
    public static <T> T jsonToBean(String jsonText, JavaType javaType)
        throws IOException
    {
        return objectMapper.readValue(jsonText, javaType);
    }
    
    /**
     * json字符串转bean
     * 
     * @param jsonText
     * @return
     * @throws IOException
     */
    public static <T> T jsonToBean(String jsonText, TypeReference<T> typeRef)
        throws IOException
    {
        return objectMapper.readValue(jsonText, typeRef);
    }
}
//goto src\test\java\com\fly\http\bean\SearchReq.java
package com.fly.http.bean;

import lombok.Data;

@Data
public class SearchReq
{
    Integer pageNo = 1;
    
    Integer pageSize = 10;
    
    String keyword;
}
//goto src\test\java\com\fly\http\WebClientAsyncTest.java
package com.fly.http;

import java.awt.Desktop;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

import org.apache.commons.lang3.RandomUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;

import com.fly.http.bean.ImageShowDialog;
import com.fly.http.bean.JsonBeanUtils;
import com.fly.http.bean.SearchReq;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;

/**
 * http请求WebClient异步调用实现
 */
@Slf4j
public class WebClientAsyncTest
{
    private WebClient webClient = WebClient.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)).build();
    
    private void openImage(Resource resource)
    {
        try
        {
            new ImageShowDialog(ImageIO.read(resource.getInputStream()));
        }
        catch (IOException e)
        {
            log.error(e.getMessage(), e);
        }
    }
    
    @BeforeClass
    public static void init()
    {
        new File("download").mkdirs();
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws IOException
     * @throws InterruptedException
     */
    @Test
    public void testDownFile()
        throws IOException, InterruptedException
    {
        Mono<Resource> mono = webClient.get()
            .uri("https://00fly.online/upload/urls.txt")
            .accept(MediaType.IMAGE_JPEG)
            .exchange()
            .doOnSuccess(clientResponse -> log.info("----- headers: {}", clientResponse.headers()))
            .doOnSuccess(clientResponse -> log.info("----- statusCode: {}", clientResponse.statusCode()))
            .flatMap(clientResponse -> clientResponse.bodyToMono(Resource.class));
        
        // 保存到本地
        mono.subscribe(resource -> {
            try
            {
                FileCopyUtils.copy(resource.getInputStream(), new FileOutputStream("download/urls.txt"));
            }
            catch (IOException e)
            {
                log.error(e.getMessage(), e);
            }
        });
        TimeUnit.SECONDS.sleep(2);
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws IOException
     * @throws InterruptedException
     */
    @Test
    public void testDownImg001()
        throws IOException, InterruptedException
    {
        Mono<Resource> mono = webClient.get()
            .uri("https://00fly.online/upload/2019/02/201902262129360274AKuFZcUfip.jpg")
            .accept(MediaType.IMAGE_JPEG)
            .retrieve() // 获取响应体
            .bodyToMono(Resource.class);
        mono.subscribe(resource -> openImage(resource));
        TimeUnit.SECONDS.sleep(10);
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws IOException
     * @throws InterruptedException
     */
    @Test
    public void testDownImg002()
        throws IOException, InterruptedException
    {
        Mono<Resource> mono = webClient.get()
            .uri("https://00fly.online/upload/2019/02/201902262129360274AKuFZcUfip.jpg")
            .accept(MediaType.IMAGE_JPEG)
            .retrieve() // 获取响应体
            .bodyToMono(Resource.class);
        
        // 保存到本地
        mono.subscribe(resource -> {
            try
            {
                File dest = new File(String.format("download/img_%s.jpg", System.currentTimeMillis()));
                FileCopyUtils.copy(resource.getInputStream(), new FileOutputStream(dest));
                if (Desktop.isDesktopSupported())
                {
                    Desktop.getDesktop().open(dest.getParentFile());
                }
            }
            catch (IOException e)
            {
                log.error(e.getMessage(), e);
            }
        });
        TimeUnit.SECONDS.sleep(2);
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testExchange001()
        throws InterruptedException
    {
        // get
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        Mono<ClientResponse> monoGet = webClient.get()
            .uri(uriBuilder -> uriBuilder.scheme("https")
                .host("httpbin.org")
                .path("/get")
                .queryParams(params) // 等价 queryParam("q1", "java").queryParam("q2", "python")
                .build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange();
        
        monoGet.subscribe(clientResponse -> {
            log.info("----- headers: {}", clientResponse.headers());
            log.info("----- statusCode: {}", clientResponse.statusCode());
            clientResponse.bodyToMono(String.class).subscribe(body -> log.info("----- reponse: {}", body));
        });
        TimeUnit.SECONDS.sleep(2);
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testExchange002()
        throws InterruptedException
    {
        // get
        Mono<ClientResponse> monoGet = webClient.get()
            .uri(uriBuilder -> uriBuilder.scheme("https").host("httpbin.org").path("/get").queryParam("q1", "java").queryParam("q2", "python").build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange();
        monoGet.subscribe(clientResponse -> {
            log.info("----- headers: {}", clientResponse.headers());
            log.info("----- statusCode: {}", clientResponse.statusCode());
            clientResponse.bodyToMono(String.class).subscribe(body -> log.info("----- reponse: {}", body));
        });
        
        // formData post
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        Mono<ClientResponse> monoPost = webClient.post()
            .uri("https://httpbin.org/post")
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromFormData(params)) // 设置formData,等价 BodyInserters.fromFormData("q1", "java").with("q2", "python")
            .exchange();
        monoPost.subscribe(clientResponse -> {
            log.info("----- headers: {}", clientResponse.headers());
            log.info("----- statusCode: {}", clientResponse.statusCode());
            clientResponse.bodyToMono(String.class).subscribe(body -> log.info("----- reponse: {}", body));
        });
        TimeUnit.SECONDS.sleep(2);
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testFormDataPost()
        throws InterruptedException
    {
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        Mono<String> mono = webClient.post()
            .uri("https://httpbin.org/post")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromFormData(params)) // 设置formData,等价 BodyInserters.fromFormData("q1", "java").with("q2", "python")
            .retrieve() // 获取响应体
            .bodyToMono(String.class); // 响应数据类型转换
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testGet001()
        throws InterruptedException
    {
        Mono<String> mono = webClient.get()
            .uri("https://httpbin.org/{method}", "get") // {任意命名}
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .retrieve()
            .bodyToMono(String.class);
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testGet002()
        throws InterruptedException
    {
        Mono<ClientResponse> mono = webClient.get().uri("https://httpbin.org/get").acceptCharset(StandardCharsets.UTF_8).accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML).exchange();
        mono.subscribe(reponse -> {
            log.info("----- headers: {}", reponse.headers());
            log.info("----- statusCode: {}", reponse.statusCode());
            reponse.bodyToMono(String.class).subscribe(body -> log.info("----- reponse: {}", body));
        });
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testGet003()
        throws InterruptedException
    {
        Mono<String> mono = webClient.get()
            .uri("https://httpbin.org/get")
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange()
            .doOnSuccess(clientResponse -> log.info("----- headers: {}", clientResponse.headers()))
            .doOnSuccess(clientResponse -> log.info("----- statusCode: {}", clientResponse.statusCode()))
            .flatMap(clientResponse -> clientResponse.bodyToMono(String.class));
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用, https://httpbin.org/get?q=java
     * 
     * @throws InterruptedException
     */
    @Test
    public void testGet004()
        throws InterruptedException
    {
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q", "java");
        String uri = UriComponentsBuilder.fromUriString("https://httpbin.org/get").queryParams(params).toUriString();
        
        // 注意比较
        // uri = UriComponentsBuilder.fromUriString("https://httpbin.org/get").queryParam("q", "java", "python").toUriString();
        // uri = UriComponentsBuilder.fromUriString("https://httpbin.org/get").queryParam("q1", "java").queryParam("q2", "python").toUriString();
        
        Mono<String> mono = webClient.get()
            .uri(uri)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange()
            .doOnSuccess(clientResponse -> log.info("----- headers: {}", clientResponse.headers()))
            .doOnSuccess(clientResponse -> log.info("----- statusCode: {}", clientResponse.statusCode()))
            .flatMap(clientResponse -> clientResponse.bodyToMono(String.class));
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testGet005()
        throws InterruptedException
    {
        Mono<String> mono = webClient.get()
            .uri(uriBuilder -> uriBuilder.scheme("https").host("httpbin.org").path("/get").queryParam("q1", "java").queryParam("q2", "python").build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .retrieve()
            .bodyToMono(String.class);
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testJsonBody001()
        throws InterruptedException
    {
        Mono<String> mono = webClient.post()
            .uri(uriBuilder -> uriBuilder.scheme("https").host("httpbin.org").path("/post").build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(Collections.singletonMap("q", "java"))
            .retrieve()
            .bodyToMono(String.class);
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws IOException
     * @throws InterruptedException
     */
    @Test
    public void testJsonBody002()
        throws IOException, InterruptedException
    {
        Mono<String> mono;
        int num = RandomUtils.nextInt(1, 4);
        switch (num)
        {
            case 1: // 方式1,javaBean
                SearchReq req = new SearchReq();
                req.setPageNo(1);
                req.setPageSize(10);
                req.setKeyword("1");
                mono = webClient.post()
                    .uri("https://httpbin.org/post")
                    .acceptCharset(StandardCharsets.UTF_8)
                    .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
                    .bodyValue(req) // 设置JsonBody
                    .retrieve()
                    .bodyToMono(String.class);
                mono.subscribe(body -> log.info("reponse: {}", body));
                break;
            
            case 2: // 方式2,HashMap
                Map<String, String> params = new HashMap<>();
                params.put("pageNo", "2");
                params.put("pageSize", "20");
                params.put("keyword", "2");
                mono = webClient.post()
                    .uri("https://httpbin.org/post")
                    .acceptCharset(StandardCharsets.UTF_8)
                    .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
                    .bodyValue(params) // 设置JsonBody
                    .retrieve()
                    .bodyToMono(String.class);
                mono.subscribe(body -> log.info("reponse: {}", body));
                break;
            
            case 3: // 方式3,json字符串
                Map<String, String> params2 = new HashMap<>();
                params2.put("pageNo", "2");
                params2.put("pageSize", "20");
                params2.put("keyword", "2");
                mono = webClient.post()
                    .uri("https://httpbin.org/post")
                    .acceptCharset(StandardCharsets.UTF_8)
                    .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
                    .body(BodyInserters.fromValue(JsonBeanUtils.beanToJson(params2, false))) // 设置formData
                    .retrieve()
                    .bodyToMono(String.class);
                mono.subscribe(body -> log.info("reponse: {}", body));
                break;
        }
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testUpload001()
        throws InterruptedException
    {
        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        params.add("file", new ClassPathResource("123.jpg"));
        Mono<String> mono = webClient.post()
            .uri("https://httpbin.org/post")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromMultipartData(params)) // 设置formData,等价 BodyInserters.fromFormData("q1", "java").with("q2", "python")
            .retrieve() // 获取响应体
            .bodyToMono(String.class); // 响应数据类型转换
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
    
    /**
     * WebClient异步调用
     * 
     * @throws InterruptedException
     */
    @Test
    public void testUpload002()
        throws InterruptedException
    {
        Mono<String> mono = webClient.post()
            .uri("https://httpbin.org/post")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromMultipartData("q1", "java").with("q2", "python").with("file", new ClassPathResource("123.jpg")))
            .retrieve() // 获取响应体
            .bodyToMono(String.class); // 响应数据类型转换
        mono.subscribe(body -> log.info("----- reponse: {}", body));
        TimeUnit.SECONDS.sleep(2); // 重要,等待异步调用完成
    }
}
//goto src\test\java\com\fly\http\WebClientSyncTest.java
package com.fly.http;

import java.awt.Desktop;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

import org.apache.commons.lang3.RandomUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;

import com.fly.http.bean.ImageShowDialog;
import com.fly.http.bean.JsonBeanUtils;
import com.fly.http.bean.SearchReq;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;

/**
 * http请求WebClient同步调用实现
 */
@Slf4j
public class WebClientSyncTest
{
    private WebClient webClient = WebClient.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)).build();
    
    private void openImage(Resource resource)
    {
        try
        {
            new ImageShowDialog(ImageIO.read(resource.getInputStream()));
        }
        catch (IOException e)
        {
            log.error(e.getMessage(), e);
        }
    }
    
    @BeforeClass
    public static void init()
    {
        new File("download").mkdirs();
    }
    
    /**
     * WebClient同步调用
     * 
     * @throws IOException
     */
    @Test
    public void testDownFile()
        throws IOException
    {
        Mono<ClientResponse> mono = webClient.get().uri("https://00fly.online/upload/urls.txt").accept(MediaType.IMAGE_JPEG).exchange();
        ClientResponse response = mono.block();
        log.info("----- headers: {}", response.headers());
        log.info("----- statusCode: {}", response.statusCode());
        
        // 保存到本地
        Resource resource = response.bodyToMono(Resource.class).block();
        FileCopyUtils.copy(resource.getInputStream(), new FileOutputStream("download/urls.txt"));
    }
    
    /**
     * WebClient同步调用
     * 
     * @throws IOException
     * @throws InterruptedException
     */
    @Test
    public void testDownImg001()
        throws IOException, InterruptedException
    {
        Mono<Resource> mono = webClient.get()
            .uri("https://00fly.online/upload/2019/02/201902262129360274AKuFZcUfip.jpg")
            .accept(MediaType.IMAGE_JPEG)
            .retrieve() // 获取响应体
            .bodyToMono(Resource.class);
        openImage(mono.block());
        TimeUnit.SECONDS.sleep(10);
    }
    
    /**
     * WebClient同步调用
     * 
     * @throws IOException
     */
    @Test
    public void testDownImg002()
        throws IOException
    {
        Mono<Resource> mono = webClient.get()
            .uri("https://00fly.online/upload/2019/02/201902262129360274AKuFZcUfip.jpg")
            .accept(MediaType.IMAGE_JPEG)
            .retrieve() // 获取响应体
            .bodyToMono(Resource.class);
        
        // 保存到本地
        Resource resource = mono.block();
        File dest = new File(String.format("download/img_%s.jpg", System.currentTimeMillis()));
        FileCopyUtils.copy(resource.getInputStream(), new FileOutputStream(dest));
        if (Desktop.isDesktopSupported())
        {
            Desktop.getDesktop().open(dest.getParentFile());
        }
    }
    
    /**
     * WebClient同步调用
     */
    @Test
    public void testExchange001()
    {
        // get
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        Mono<ClientResponse> monoGet = webClient.get()
            .uri(uriBuilder -> uriBuilder.scheme("https")
                .host("httpbin.org")
                .path("/get")
                .queryParams(params) // 等价 queryParam("q1", "java").queryParam("q2", "python")
                .build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange();
        
        ClientResponse clientResponse = monoGet.block(); // 获取完整的响应对象
        log.info("----- headers: {}", clientResponse.headers());
        log.info("----- statusCode: {}", clientResponse.statusCode());
        log.info("----- reponse: {}", clientResponse.bodyToMono(String.class).block());
    }
    
    /**
     * WebClient同步调用
     */
    @Test
    public void testExchange002()
    {
        // get
        Mono<ClientResponse> monoGet = webClient.get()
            .uri(uriBuilder -> uriBuilder.scheme("https").host("httpbin.org").path("/get").queryParam("q1", "java").queryParam("q2", "python").build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange();
        ClientResponse clientResponse = monoGet.block(); // 获取完整的响应对象
        log.info("----- headers: {}", clientResponse.headers());
        log.info("----- statusCode: {}", clientResponse.statusCode());
        log.info("----- reponse: {}", clientResponse.bodyToMono(String.class).block());
        
        // formData post
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        Mono<ClientResponse> monoPost = webClient.post()
            .uri("https://httpbin.org/post")
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromFormData(params)) // 设置formData,等价 BodyInserters.fromFormData("q1", "java").with("q2", "python")
            .exchange();
        ClientResponse clientResponse2 = monoPost.block(); // 获取完整的响应对象
        log.info("----- headers: {}", clientResponse2.headers());
        log.info("----- statusCode: {}", clientResponse2.statusCode());
        log.info("----- reponse: {}", clientResponse2.bodyToMono(String.class).block());
    }
    
    /**
     * WebClient同步调用
     */
    @Test
    public void testFormDataPost()
    {
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        Mono<String> mono = webClient.post()
            .uri("https://httpbin.org/post")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromFormData(params)) // 设置formData,等价 BodyInserters.fromFormData("q1", "java").with("q2", "python")
            .retrieve() // 获取响应体
            .bodyToMono(String.class); // 响应数据类型转换
        log.info("reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用
     */
    @Test
    public void testGet001()
    {
        Mono<String> mono = webClient.get()
            .uri("https://httpbin.org/{method}", "get") // {任意命名}
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .retrieve()
            .bodyToMono(String.class);
        log.info("reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用
     */
    @Test
    public void testGet002()
    {
        Mono<String> mono = webClient.get()
            .uri("https://httpbin.org/get")
            .acceptCharset(StandardCharsets.UTF_8)
            .exchange()
            .doOnSuccess(clientResponse -> log.info("----- headers: {}", clientResponse.headers()))
            .doOnSuccess(clientResponse -> log.info("----- statusCode: {}", clientResponse.statusCode()))
            .flatMap(clientResponse -> clientResponse.bodyToMono(String.class));
        log.info("reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用
     * 
     */
    @Test
    public void testGet003()
    {
        Mono<String> mono = webClient.get()
            .uri("https://httpbin.org/get")
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange()
            .doOnSuccess(clientResponse -> log.info("----- headers: {}", clientResponse.headers()))
            .doOnSuccess(clientResponse -> log.info("----- statusCode: {}", clientResponse.statusCode()))
            .flatMap(clientResponse -> clientResponse.bodyToMono(String.class));
        log.info("reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用, https://httpbin.org/get?q=java
     * 
     */
    @Test
    public void testGet004()
    {
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("q", "java");
        String uri = UriComponentsBuilder.fromUriString("https://httpbin.org/get").queryParams(params).toUriString();
        
        // 注意比较
        // uri = UriComponentsBuilder.fromUriString("https://httpbin.org/get").queryParam("q", "java", "python").toUriString();
        // uri = UriComponentsBuilder.fromUriString("https://httpbin.org/get").queryParam("q1", "java").queryParam("q2", "python").toUriString();
        
        Mono<String> mono = webClient.get()
            .uri(uri)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .exchange()
            .doOnSuccess(clientResponse -> log.info("----- headers: {}", clientResponse.headers()))
            .doOnSuccess(clientResponse -> log.info("----- statusCode: {}", clientResponse.statusCode()))
            .flatMap(clientResponse -> clientResponse.bodyToMono(String.class));
        log.info("reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用
     * 
     */
    @Test
    public void testGet005()
    {
        Mono<String> mono = webClient.get()
            .uri(uriBuilder -> uriBuilder.scheme("https").host("httpbin.org").path("/get").queryParam("q1", "java").queryParam("q2", "python").build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .retrieve()
            .bodyToMono(String.class);
        log.info("reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用
     * 
     */
    @Test
    public void testJsonBody001()
    {
        Mono<String> mono = webClient.post()
            .uri(uriBuilder -> uriBuilder.scheme("https").host("httpbin.org").path("/post").build())
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(Collections.singletonMap("q", "java"))
            .retrieve()
            .bodyToMono(String.class);
        log.info("reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用
     * 
     * @throws IOException
     */
    @Test
    public void testJsonBody002()
        throws IOException
    {
        Mono<String> mono;
        int num = RandomUtils.nextInt(1, 4);
        switch (num)
        {
            case 1: // 方式1,javaBean
                SearchReq req = new SearchReq();
                req.setPageNo(1);
                req.setPageSize(10);
                req.setKeyword("1");
                mono = webClient.post()
                    .uri("https://httpbin.org/post")
                    .acceptCharset(StandardCharsets.UTF_8)
                    .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
                    .bodyValue(req) // 设置JsonBody
                    .retrieve()
                    .bodyToMono(String.class);
                log.info("reponse: {}", mono.block());
                break;
            
            case 2: // 方式2,HashMap
                Map<String, String> params = new HashMap<>();
                params.put("pageNo", "2");
                params.put("pageSize", "20");
                params.put("keyword", "2");
                mono = webClient.post()
                    .uri("https://httpbin.org/post")
                    .acceptCharset(StandardCharsets.UTF_8)
                    .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
                    .bodyValue(params) // 设置JsonBody
                    .retrieve()
                    .bodyToMono(String.class);
                log.info("reponse: {}", mono.block());
                break;
            
            case 3: // 方式3,json字符串
                Map<String, String> params2 = new HashMap<>();
                params2.put("pageNo", "2");
                params2.put("pageSize", "20");
                params2.put("keyword", "2");
                mono = webClient.post()
                    .uri("https://httpbin.org/post")
                    .acceptCharset(StandardCharsets.UTF_8)
                    .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
                    .body(BodyInserters.fromValue(JsonBeanUtils.beanToJson(params2, false))) // 设置formData
                    .retrieve()
                    .bodyToMono(String.class);
                log.info("reponse: {}", mono.block());
                break;
        }
    }
    
    /**
     * WebClient同步调用
     * 
     */
    @Test
    public void testUpload001()
    {
        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
        params.add("q1", "java");
        params.add("q2", "python");
        params.add("file", new ClassPathResource("123.jpg"));
        Mono<String> mono = webClient.post()
            .uri("https://httpbin.org/post")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromMultipartData(params)) // 设置formData,等价 BodyInserters.fromFormData("q1", "java").with("q2", "python")
            .retrieve() // 获取响应体
            .bodyToMono(String.class); // 响应数据类型转换
        log.info("----- reponse: {}", mono.block());
    }
    
    /**
     * WebClient同步调用
     * 
     */
    @Test
    public void testUpload002()
    {
        Mono<String> mono = webClient.post()
            .uri("https://httpbin.org/post")
            .contentType(MediaType.APPLICATION_FORM_URLENCODED)
            .acceptCharset(StandardCharsets.UTF_8)
            .accept(MediaType.APPLICATION_JSON, MediaType.TEXT_HTML)
            .body(BodyInserters.fromMultipartData("q1", "java").with("q2", "python").with("file", new ClassPathResource("123.jpg")))
            .retrieve() // 获取响应体
            .bodyToMono(String.class); // 响应数据类型转换
        log.info("----- reponse: {}", mono.block());
    }
}


有任何问题和建议,都可以向我提问讨论,大家一起进步,谢谢!

-over-

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Programming Abstractions in C阅读笔记:p331-p337

《Programming Abstractions in C》学习第79天&#xff0c;p331-p337&#xff0c;总计7页。 一、技术总结 /** File: stack.h* -------------* This interface defines an abstraction for stacks. In any* single application that uses this interface, the values in* the…

银行5G短消息应用架构设计

&#xff08;一&#xff09;RCS简介 1.1 RCS的提出与标准制定 RCS(Rich Communication Services & Suite&#xff0c;富媒体通信)是GSMA(Groupe Speciale Mobile Association&#xff0c;全球移动通信系统协会)在2008年提出的一种通讯方式&#xff0c;RCS融合了语音、消息…

Django(一)- 环境搭建和快速入门

一、搭建环境 1、创建Python虚拟环境 (base) C:\Users\35351>conda create -n django_study python3.9 2、安装Django (django_study) C:\Users\35351>pip install Django >> 查看安装版本 (django_study) C:\Users\35351>python -m django --version 3、安…

计算机毕业设计无从下手?学长带你从零开始,三天搞定!

嘿&#xff0c;各位朋友们&#xff0c;我是小新&#xff01;&#x1f44b; 研究生的日子就像过山车一样&#xff0c;一转眼就快到终点站了。目前也是在面临着毕业论文的压力&#xff0c;好在前期付出的时间和努力比较多&#xff0c;现阶段只剩下一些小问题了&#xff0c;相对来…

n维数字图像欧氏距离变换算法

算法简介 该算法主要用于在三维图像中计算有效体素之间的最短欧几里得距离。 算法出处&#xff1a;NEW ALGORITHMS FOR EUCLIDEAN DISTANCE TRANSFORMATION OF AN n-DIMENSIONAL DIGITIZED PICTURE WITH APPLICATIONS 算法在体渲染加速中的应用&#xff1a;Accelerated Volum…

[Halcon学习笔记]在Qt上实现Halcon窗口的字体设置颜色设置等功能

1、 Halcon字体大小设置在Qt上的实现 在之前介绍过Halcon窗口显示文字字体的尺寸和样式&#xff0c;具体详细介绍可回看 &#xff08;一&#xff09;Halcon窗口界面上显示文字的字体尺寸、样式修改 当时介绍的设定方法 //Win下QString Font_win "-Arial-10-*-1-*-*-1-&q…

忘记密码找回流程请求拦截器-前端

目录 设置找回密码请求拦截器 1.相关参数 2.约定 代码实现 1. 实现思路 2. 实现代码 校园统一身份认证系统&#xff1a; 基于网络安全&#xff0c;找回密码、重新设置密码的流程和正常登录流程中密钥等请求头不一致。 设置找回密码请求拦截器 1.相关参数 clientId 应…

明日周刊-第3期

第3期&#xff0c;分享自己最近的感悟和实用工具。 文章目录 1. 一周热点2. 资源分享3. 言论4. 歌曲推荐 1. 一周热点 国内生产总值持续增长&#xff1a;统计局最新数据显示&#xff0c;2023年全年国内生产总值&#xff08;GDP&#xff09;超过126万亿元&#xff0c;比上年增长…

提升质量透明度,动力电池企业的数据驱动生产实践 | 数据要素 × 工业制造

系列导读 如《“数据要素”三年行动计划&#xff08;2024—2026年&#xff09;》指出&#xff0c;工业制造是“数据要素”的关键领域之一。如何发挥海量数据资源、丰富应用场景等多重优势&#xff0c;以数据流引领技术流、资金流、人才流、物资流&#xff0c;对于制造企业而言…

4 Spring IOC/DI配置管理第三方bean

文章目录 1&#xff0c;IOC/DI配置管理第三方bean1.1 案例:数据源对象管理1.1.1 环境准备1.1.2 思路分析1.1.3 实现Druid管理步骤1:导入druid的依赖步骤2:配置第三方bean步骤3:从IOC容器中获取对应的bean对象步骤4:运行程序 1.1.4 实现C3P0管理步骤1:导入C3P0的依赖步骤2:配置第…

DBO优化GRNN回归预测(matlab代码)

DBO-GRNN回归预测matlab代码 蜣螂优化算法(Dung Beetle Optimizer, DBO)是一种新型的群智能优化算法&#xff0c;在2022年底提出&#xff0c;主要是受蜣螂的的滚球、跳舞、觅食、偷窃和繁殖行为的启发。 数据为Excel股票预测数据。 数据集划分为训练集、验证集、测试集,比例…

博途建立S7-1200PLC与HMS AB7013Profinet通讯

1、新建一个博图项目1200PLC .CPU 1214C ACDC/RIY 6ES7 214-1BG31-0x80 2、安装GSD文件 Install general station description fle (GsD) GSDMLV2.3-HMS-ABC PROFINET GSD 3、连接PLC 4、在线访问 5、增加访问子网络 </

FPGA 以太网传输ov5640视频

1 实验任务 使用 DFZU4EV MPSoC 开发板及双目 OV5640 摄像头其中一个摄像头实现图像采集&#xff0c;并通过开发板上的以太网接口发送给上位机实时显示。 2 系统框架 时钟模块用于为 I2C 驱动模块、以太网顶层模块和开始传输控制模块提供驱动时钟&#xff1b;I2C 驱动模块…

用最小堆实现通用的高效定时器组件

用最小堆实现通用的高效定时器组件 文章目录 用最小堆实现通用的高效定时器组件开篇解决方案类图源码实现测试总结 开篇 在程序开发过程中&#xff0c;定时器会经常被使用到。而在Linux应用开发中&#xff0c;系统定时器资源有限&#xff0c;进程可创建的定时器数量会受到系统限…

Priority Queue实现栈和队列

在排序算法中&#xff0c;堆排序利用了完全二叉树形式的堆结构&#xff0c;结合了插入排序与合并排序的优点&#xff0c;能够以 O ( n log ⁡ n ) O(n\log{n}) O(nlogn)的时间完成排序。优先级队列是可基于堆结构进行实现的一种数据结构&#xff0c;在计算机系统中可以用来记录…

【现代C++】可变参数模板

现代C中的可变参数模板是C11引入的一个功能&#xff0c;允许模板接受可变数量的参数&#xff0c;使得模板编程更加灵活和强大。 1. 基本用法 可变参数模板允许您创建接受任意数量参数的函数或类模板。 template<typename... Args> void func(Args... args) {// 处理参…

C++ 基本运算

何谓运算符和操作数 基本运算 1、双目运算 2、单目运算 3、赋值表达式 表达形式&#xff1a; <变量><表达式>; 表达式是指各种运算符把常量、变量&#xff0c;函数等运算对象连接起来的具有实际意义并符合C语法规则的式子。赋值是指表达式的值赋给一个变量。 …

基于SSM的NEUQ宿舍管理系统的设计与实现

基于SSM的NEUQ宿舍管理系统的设计与实现 获取源码——》公主号&#xff1a;计算机专业毕设大全 获取源码——》公主号&#xff1a;计算机专业毕设大全

LabVIEW电动汽车直流充电桩监控系统

LabVIEW电动汽车直流充电桩监控系统 随着电动汽车的普及&#xff0c;充电桩的安全运行成为重要议题。通过集成传感器监测、单片机技术与LabVIEW开发平台&#xff0c;设计了一套电动汽车直流充电桩监控系统&#xff0c;能实时监测充电桩的温度、电压和电流&#xff0c;并进行数…

PMP适合哪些人?考试PMP有什么职业要求吗?

威班PMP 3A路过拿证学员 。PMP认证没听说过有啥职业的要求&#xff0c;也没听说过限制在哪些行业可用&#xff0c;根据我考后经验所了解&#xff0c;它并不只作用在某一个领域&#xff0c;知识点也是项目管理相关的工作都能用到&#xff0c;毕竟这些都是通用的专业实践。如果非…