WebClient是Spring提供的非阻塞、响应式的Http客户端,提供同步及异步的API,将会代替RestTemplate及AsyncRestTemplate。文中所使用到的软件版本:Java 1.8.0_191、SpringBoot 2.2.1.RELEASE。
1、服务端
参见Java调用Http接口(1)--编写服务端
2、调用
使用WebClient需要用到Reactor Netty,依赖如下:
org.springframework spring-webflux io.projectreactor.netty reactor-netty
2.1、GET请求
public static void get() { String requestPath = "http://localhost:8080/demo/httptest/getUser?userId=1000&userName=李白"; WebClient webClient = WebClient.create(); Monomono = 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 () { @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(); MultiValueMapmap = new LinkedMultiValueMap (); map.add("userId", "1000"); map.add("userName", "李白"); Mono 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\":\"杜甫\"}"; Monomono = 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(); Monomono = 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> parts = builder.build(); Mono mono = webClient.post().uri(requestPath) .bodyValue(parts).retrieve().bodyToMono(String.class); System.out.println("mulit返回结果:" + mono.block()); }
2.6、完整例子
package com.inspur.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(); Monomono = 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 () { @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 map = new LinkedMultiValueMap (); map.add("userId", "1000"); map.add("userName", "李白"); Mono 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 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 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 > parts = builder.build(); Mono 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(); } }
3、调用Https接口
与调用Http接口不一样的部分主要在设置ssl部分,设置方法是生成合适的ClientHttpConnector;下面用GET请求来演示ssl的设置,其他调用方式类似。
package com.inspur.demo.http.client; import java.io.File; import java.io.FileInputStream; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.Collections; import java.util.function.Consumer; import org.springframework.http.client.reactive.ClientHttpConnector; import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.web.reactive.function.client.WebClient; import com.inspur.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 { /** * GET请求 */ public static void get() { try { String requestPath = "https://x.x.x.x:9010/x/"; WebClient webClient = WebClient.builder().clientConnector(getClientHttpConnector()).build(); Monomono = webClient.get().uri(requestPath).retrieve().bodyToMono(String.class); System.out.println("get(不需要证书)返回结果:" + mono.block()); String requestPath2 = "https://x.x.x.x:9016/x/"; WebClient webClient2 = WebClient.builder().clientConnector(getClientHttpConnector(getKeyStore("d:/client.p12", "123"), "123")).build(); Mono mono2 = webClient2.get().uri(requestPath2).retrieve().bodyToMono(String.class); System.out.println("get(需要证书)返回结果:" + mono2.block()); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { get(); } /** * 获取Connector * @param keyStore * @param password * @return * @throws Exception */ private static ClientHttpConnector getClientHttpConnector(KeyStore keyStore, String password) throws Exception { SslContextBuilder builder = SslContextBuilder.forClient(); if (keyStore != null) { X509Certificate[] chain = Collections.list(keyStore.aliases()).stream().map(alias -> { try { return (X509Certificate) keyStore.getCertificate(alias); } catch (KeyStoreException e) { e.printStackTrace(); } return null; }).toArray(X509Certificate[]::new); builder.keyManager((PrivateKey) keyStore.getKey(keyStore.aliases().nextElement(), password.toCharArray()), password, chain); } builder.trustManager(InsecureTrustManagerFactory.INSTANCE); SslContext sslContext = builder.build(); HttpClient httpClient = HttpClient.create().secure(new Consumer () { @Override public void accept(SslContextSpec t) { t.sslContext(sslContext); } }); ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient); return connector; } private static ClientHttpConnector getClientHttpConnector() throws Exception { return getClientHttpConnector(null, null); } /** * 获取证书 * @param filePath * @param password * @return */ private static KeyStore getKeyStore(String filePath, String password) { KeyStore keySotre = null; FileInputStream in = null; try { keySotre = KeyStore.getInstance("PKCS12"); in = new FileInputStream(new File(filePath)); keySotre.load(in, password.toCharArray()); } catch (Exception e) { e.printStackTrace(); } finally { FileUtil.close(in); } return keySotre; } }