Java调用Http/Https接口(7)--WebClient调用Http/Https接口

WebClient是Spring提供的非阻塞、响应式的Http客户端,提供同步及异步的API,将会代替RestTemplate及AsyncRestTemplate;本文主要介绍WebClient的基本使用,文中所使用到的软件版本:Java 1.8.0_191、SpringBoot 2.2.1.RELEASE。

1、服务端

参见Java调用Http接口(1)--编写服务端 

2、调用

使用WebClient需要用到Reactor Netty,依赖如下:

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>io.projectreactor.netty</groupId>
            <artifactId>reactor-netty</artifactId>
        </dependency>

2.1、GET请求

    public static void get() {
        String requestPath = "http://localhost:8080/demo/httptest/getUser?userId=1000&userName=李白";
        WebClient webClient = WebClient.create();
        Mono<String> mono = webClient.get().uri(requestPath).retrieve().bodyToMono(String.class);
        //同步方式
        System.out.println("get block返回结果:" + mono.block());
        
        //异步方式
        final CountDownLatch latch = new CountDownLatch(5);
        for (int i = 0; i < 5; i++) {
            requestPath = "http://localhost:8080/demo/httptest/getUser?userId=1000&userName=李白" + i;
            mono = webClient.get().uri(requestPath).retrieve().bodyToMono(String.class);
            mono.subscribe(new Consumer<String>() {
                @Override
                public void accept(String s) {
                    latch.countDown();
                    System.out.println("get subscribe返回结果:" + s);
                }
            });
        }
        
        try {
            latch.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

2.2、POST请求(发送键值对数据)

    public static void post() {
        String requestPath = "http://localhost:8080/demo/httptest/getUser";
        WebClient webClient = WebClient.create();
        MultiValueMap<String, String> map = new LinkedMultiValueMap <String, String>();
        map.add("userId", "1000");
        map.add("userName", "李白");
        Mono<String> mono = webClient.post().uri(requestPath).bodyValue(map).retrieve().bodyToMono(String.class);
        System.out.println("post返回结果:" + mono.block());
    }

2.3、POST请求(发送JSON数据)

    public static void post2() {
        String requestPath = "http://localhost:8080/demo/httptest/addUser";
        WebClient webClient = WebClient.create();
        String param = "{\"userId\": \"1001\",\"userName\":\"杜甫\"}";
        Mono<String> mono = webClient.post().uri(requestPath).contentType(MediaType.APPLICATION_JSON).bodyValue(param)
                .retrieve().bodyToMono(String.class);
        System.out.println("post json返回结果:" + mono.block());
    }

2.4、上传文件

    public static void upload() {
        String requestPath = "http://localhost:8080/demo/httptest/upload";
        WebClient webClient = WebClient.create();
        Mono<String> mono = webClient.post().uri(requestPath).contentType(MediaType.APPLICATION_OCTET_STREAM)
                .bodyValue(new FileSystemResource("d:/a.jpg")).retrieve().bodyToMono(String.class);
        System.out.println("upload返回结果:" + mono.block());
    }

2.5、上传文件及发送键值对数据

    public static void mulit() {
        String requestPath = "http://localhost:8080/demo/httptest/multi";
        WebClient webClient = WebClient.create();
        
        MultipartBodyBuilder builder = new MultipartBodyBuilder();
        builder.part("param1", "参数1");
        builder.part("param2", "参数2");
        builder.part("file", new FileSystemResource("d:/a.jpg"));
        MultiValueMap<String, HttpEntity<?>> parts = builder.build();
        Mono<String> mono = webClient.post().uri(requestPath)
                .bodyValue(parts).retrieve().bodyToMono(String.class);
        System.out.println("mulit返回结果:" + mono.block());
    }

2.6、完整例子

package com.abc.demo.http.client;

import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;

import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.MediaType;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.client.WebClient;

import reactor.core.publisher.Mono;

/**
 * 
 * 通过WebClient调用Http接口
 *
 */
public class WebClientCase {
    /**
     *  GET请求
     */
    public static void get() {
        String requestPath = "http://localhost:8080/demo/httptest/getUser?userId=1000&userName=李白";
        WebClient webClient = WebClient.create();
        Mono<String> mono = webClient.get().uri(requestPath).retrieve().bodyToMono(String.class);
        //同步方式
        System.out.println("get block返回结果:" + mono.block());
        
        //异步方式
        final CountDownLatch latch = new CountDownLatch(5);
        for (int i = 0; i < 5; i++) {
            requestPath = "http://localhost:8080/demo/httptest/getUser?userId=1000&userName=李白" + i;
            mono = webClient.get().uri(requestPath).retrieve().bodyToMono(String.class);
            mono.subscribe(new Consumer<String>() {
                @Override
                public void accept(String s) {
                    latch.countDown();
                    System.out.println("get subscribe返回结果:" + s);
                }
            });
        }
        
        try {
            latch.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     *  POST请求(发送键值对数据)
     */
    public static void post() {
        String requestPath = "http://localhost:8080/demo/httptest/getUser";
        WebClient webClient = WebClient.create();
        MultiValueMap<String, String> map = new LinkedMultiValueMap <String, String>();
        map.add("userId", "1000");
        map.add("userName", "李白");
        Mono<String> mono = webClient.post().uri(requestPath).bodyValue(map).retrieve().bodyToMono(String.class);
        System.out.println("post返回结果:" + mono.block());
    }
    
    /**
     *  POST请求(发送json数据)
     */
    public static void post2() {
        String requestPath = "http://localhost:8080/demo/httptest/addUser";
        WebClient webClient = WebClient.create();
        String param = "{\"userId\": \"1001\",\"userName\":\"杜甫\"}";
        Mono<String> mono = webClient.post().uri(requestPath).contentType(MediaType.APPLICATION_JSON).bodyValue(param)
                .retrieve().bodyToMono(String.class);
        System.out.println("post json返回结果:" + mono.block());
    }
    
    /**
     * 上传文件
     */
    public static void upload() {
        String requestPath = "http://localhost:8080/demo/httptest/upload";
        WebClient webClient = WebClient.create();
        Mono<String> mono = webClient.post().uri(requestPath).contentType(MediaType.APPLICATION_OCTET_STREAM)
                .bodyValue(new FileSystemResource("d:/a.jpg")).retrieve().bodyToMono(String.class);
        System.out.println("upload返回结果:" + mono.block());
    }
    
    /**
     * 上传文件及发送键值对数据
     */
    public static void mulit() {
        String requestPath = "http://localhost:8080/demo/httptest/multi";
        WebClient webClient = WebClient.create();
        
        MultipartBodyBuilder builder = new MultipartBodyBuilder();
        builder.part("param1", "参数1");
        builder.part("param2", "参数2");
        builder.part("file", new FileSystemResource("d:/a.jpg"));
        MultiValueMap<String, HttpEntity<?>> parts = builder.build();
        Mono<String> mono = webClient.post().uri(requestPath)
                .bodyValue(parts).retrieve().bodyToMono(String.class);
        System.out.println("mulit返回结果:" + mono.block());
    }
    
    public static void main(String[] args) {
        get();
        post();
        post2();
        upload();
        mulit();
    }
}
WebClientCase.java

3、调用Https接口

与调用Http接口不一样的部分主要在设置ssl部分,设置方法是生成合适的ClientHttpConnector;下面用GET请求来演示ssl的设置,其他调用方式类似。

package com.abc.demo.http.client;

import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SNIMatcher;
import javax.net.ssl.SNIServerName;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.TrustManagerFactory;

import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;

import com.abc.demo.common.util.FileUtil;

import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import reactor.netty.tcp.SslProvider.SslContextSpec;

/**
 * 通过WebClient调用Https接口
 */
public class WebClientHttpsCase {
    public static void main(String[] args) {
        try {
            /*
             * 请求有权威证书的地址
             */
            String requestPath = "https://www.12306.cn/index/";
            WebClient webClient = WebClient.create();
            Mono<String> mono = webClient.get().uri(requestPath).retrieve().bodyToMono(String.class);
            System.out.println("get1返回结果:" + mono.block());
            
            /*
             * 请求自定义证书的地址
             */
            //获取信任证书库
            KeyStore trustStore = getkeyStore("jks", "d:/temp/cacerts", "123456");
            
            //不需要客户端证书
            requestPath = "https://10.49.196.10:9010/myservice/";
            webClient = WebClient.builder().clientConnector(getClientHttpConnector(trustStore)).build();
            mono = webClient.get().uri(requestPath).retrieve().bodyToMono(String.class);
            System.out.println("get2返回结果:" + mono.block());
            
            //需要客户端证书,客户端证书可以是用 keytool 生成的 pks12 格式密钥库,也可以是用 OpenSSL 生成的 pkcs12 格式证书。
            requestPath = "https://10.49.196.10:9016/myservice/";
            KeyStore keyStore = getkeyStore("pkcs12", "d:/client.p12", "123456");
            webClient = WebClient.builder().clientConnector(getClientHttpConnector(keyStore, "123456", trustStore)).build();
            mono = webClient.get().uri(requestPath).retrieve().bodyToMono(String.class);
            System.out.println("get3返回结果:" + mono.block());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 获取Connector
     * @param keyStore
     * @param password
     * @return
     * @throws Exception
     */
    private static ClientHttpConnector getClientHttpConnector(KeyStore keyStore, String keyStorePassword, KeyStore trustStore) throws Exception {
        SslContextBuilder builder = SslContextBuilder.forClient();
        if (keyStore != null) {
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
            keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
            builder.keyManager(keyManagerFactory);
        }
        if (trustStore != null) {
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
            trustManagerFactory.init(trustStore);
            builder.trustManager(trustManagerFactory);
        } else {
            builder.trustManager(InsecureTrustManagerFactory.INSTANCE);
        }
        SslContext sslContext = builder.build();
        HttpClient httpClient = HttpClient.create().secure(new Consumer<SslContextSpec>() {
            @Override
            public void accept(SslContextSpec t) {
                t.sslContext(sslContext).handlerConfigurator(handler -> {
                    SSLEngine engine = handler.engine();
                    List<SNIMatcher> matchers = new LinkedList<SNIMatcher>();
                    SNIMatcher matcher = new SNIMatcher(0) {
                        @Override
                        public boolean matches(SNIServerName serverName) {
                            //if ("xxx".equals(serverName)) {
                            //    return true;
                            //} else {
                            //    return false;
                            //}
                            return true;
                        }
                    };
                    matchers.add(matcher);
                    SSLParameters params = new SSLParameters();
                    params.setSNIMatchers(matchers);
                    engine.setSSLParameters(params);
                });
            }
        });
        
        ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
        return connector;
    }
    
    private static ClientHttpConnector getClientHttpConnector(KeyStore trustStore) throws Exception {
        return getClientHttpConnector(null, null, trustStore);
    }
    
    /**
     * 获取证书
     * @param type
     * @param filePath
     * @param password
     * @return
     */
    private static KeyStore getkeyStore(String type, String filePath, String password) {
        KeyStore keySotre = null;
        FileInputStream in = null;
        try {
            keySotre = KeyStore.getInstance(type);
            in = new FileInputStream(new File(filePath));
            keySotre.load(in, password.toCharArray());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            FileUtil.close(in);
        }
        return keySotre;
    }
}
posted @ 2019-11-29 17:38  且行且码  阅读(6145)  评论(0编辑  收藏  举报