目錄
- 簡述
- 具體方法
- 獲取WebClient.Builder類的方法集合
- 其它工具方法
- ResponseCookie轉MultiValueMap
簡述
WebClient是WebFlux框架中重要的Http請求框架,同時也是Spring官方的Http請求工具,相當于SpringMVC框架中的RestTemplate,關于這個工具的詳情,大家可參考下方的官方檔案學習具體的使用方法
《WebClient》
在日常使用中,WebClient通常是使用WebClient.Builder來完成構建的,為了方便日常使用,筆者將日常使用到的場景封裝了一個工具類,其中包含了獲取WebClient.Builder的部分,和方便提取和轉換一些資訊(如Cookies等)的工具方法,
具體方法
獲取WebClient.Builder類的方法集合
在這個部分,分別通過幾種不同的方法獲取了包含SSH認證和代理等資訊的WebClient.Builder的方法
同時,下列方法中默認給Builder附上了一個超時時間配置,避免Socket例外導致請求被hung住,
/**
* 給了一個默認的WebClient,這個Client里面配置了默認請求超時時間
*
* @return 回傳一個帶超時時間的{@link WebClient.Builder}
*/
public static WebClient.Builder getDefaultWebClientBuilder() {
return getWebClientBuilder(DEFAULT_REQUEST_TIMEOUT);
}
/**
* [基礎創建方法]
* 給了一個默認的WebClient,這個Client里面配置了指定了請求超時時間
*
* @param requestTimeOut 請求超時時間
* @return 回傳一個帶超時時間的{@link WebClient.Builder}
*/
public static WebClient.Builder getWebClientBuilder(Duration requestTimeOut) {
if (requestTimeOut == null) {
requestTimeOut = DEFAULT_REQUEST_TIMEOUT;
}
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient
.create()
//重新定向開啟
.followRedirect(true)
.responseTimeout(requestTimeOut)));
}
/**
* 給到一個帶默認超時時間,并帶有不校驗任何SSL整數的WebClient
*
* @return 回傳一個帶默認超時時間和默認全域信任的SSL請求校驗器{@link WebClient.Builder}
*/
public static WebClient.Builder getWebClientBuilderWithSslTrust() {
return getWebClientBuilderWithSslTrust(DEFAULT_REQUEST_TIMEOUT);
}
/**
* 給到一個帶超時時間,并帶有不校驗任何SSL整數的WebClient
*
* @param requestTimeOut 超時時間
* @return 回傳一個帶超時時間和默認全域信任的SSL請求校驗器{@link WebClient.Builder}
*/
public static WebClient.Builder getWebClientBuilderWithSslTrust(Duration requestTimeOut) {
return getWebClientBuilderWithSslTrust(requestTimeOut, false);
}
/**
* [基礎創建方法]
* 給到一個帶超時時間,并帶有不校驗任何SSL整數的WebClient
*
* @param requestTimeOut 超時時間
* @param compressionEnabled 開啟壓縮?默認關閉
* @return 回傳一個帶超時時間和默認全域信任的SSL請求校驗器{@link WebClient.Builder}
*/
public static WebClient.Builder getWebClientBuilderWithSslTrust(Duration requestTimeOut, boolean compressionEnabled) {
if (requestTimeOut == null) {
requestTimeOut = DEFAULT_REQUEST_TIMEOUT;
}
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient
.create()
//重新定向開啟
.followRedirect(true)
//這里注入了一個拋棄一切SSL認證的sslContext
.secure(sslContextSpec -> sslContextSpec.sslContext(SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE)))
.responseTimeout(requestTimeOut)
.compress(compressionEnabled)
));
}
/**
* 給到一個帶超時時間,帶代理,并帶有不校驗任何SSL整數的WebClient
*
* @param requestTimeOut 超時時間
* @param proxyDO 代理物體
* @return 回傳一個帶超時時間和默認全域信任的SSL請求校驗器{@link WebClient.Builder}
*/
public static WebClient.Builder getWebClientBuilderWithSslTrustAndPolicy(Duration requestTimeOut, ProxyDO proxyDO) {
return getWebClientBuilderWithSslTrustAndPolicy(requestTimeOut, proxyDO, false);
}
/**
* [基礎創建方法]
* 給到一個帶超時時間,帶代理,并帶有不校驗任何SSL整數的WebClient
*
* @param requestTimeOut 超時時間
* @param proxyDO 代理物體
* @param compressionEnabled 開啟壓縮?默認關閉
* @return 回傳一個帶超時時間和默認全域信任的SSL請求校驗器{@link WebClient.Builder}
*/
public static WebClient.Builder getWebClientBuilderWithSslTrustAndPolicy(Duration requestTimeOut, ProxyDO proxyDO, boolean compressionEnabled) {
if (requestTimeOut == null) {
requestTimeOut = DEFAULT_REQUEST_TIMEOUT;
}
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient
.create()
//這里注入了一個拋棄一切SSL認證的sslContext
.secure(sslContextSpec -> sslContextSpec.sslContext(SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE)))
.responseTimeout(requestTimeOut)
.compress(compressionEnabled)
//重新定向開啟
.followRedirect(true)
.tcpConfiguration(tcpClient -> tcpClient.proxy(
p -> {
ProxyProvider.Builder pb = p.type(ProxyProvider.Proxy.HTTP)
.address(InetSocketAddress.createUnresolved(proxyDO.getServiceAddress(), Integer.parseInt(proxyDO.getPort())));
if (StringUtils.isNotEmpty(proxyDO.getUserName())) {
pb.username(proxyDO.getUserName())
.password(v -> proxyDO.getPassword());
}
Long proxyTimeOutMillis = proxyDO.getProxyTimeOutMillis();
if (proxyTimeOutMillis != null && proxyTimeOutMillis > 0) {
pb.connectTimeoutMillis(proxyTimeOutMillis);
} else {
pb.connectTimeoutMillis(DEFAULT_PROXY_TIMEOUT_MILLIS);
}
}
))
));
}
其它工具方法
ResponseCookie轉MultiValueMap<String, String> 方法
當我們做一些http請求時,可能需要進行cookie復用,即上一個請求回傳的cookie需要在下一個請求中帶上,
但是WebClient的Response設計中有個較為糾結的地方,即:WebClient回傳的ClientRespons中能夠拿到的是MultiValueMap<String, ResponseCookie>物體,而在RequestHeadersSpec#cookies(即在構建http請求時指定cookie的地方)方法中,需要給定的入參是一個MultiValueMap<String, String>>,
如上的場景催生了下面的這個方法,即完成了cookies轉換的操作
/**
* 將http相應中的Cookie轉換為用于http請求中的cookie
* 方法中僅進行簡單轉換,不會對Cookie有效期等進行判斷
*
* @param responseCookie 需要被轉換的cookie
* @return 回傳可以用于請求的Cookies
*/
public static MultiValueMap<String, String> transformResponseCookiesToRequestCookies(MultiValueMap<String, ResponseCookie> responseCookie) {
MultiValueMap<String, String> ret = new LinkedMultiValueMap<>();
if (responseCookie == null || responseCookie.size() == 0) {
return ret;
}
for (Map.Entry<String, List<ResponseCookie>> entity : responseCookie.entrySet()) {
String key = entity.getKey();
List<ResponseCookie> value = entity.getValue();
int size = value.size();
if (size == 0) {
continue;
}
List<String> cookies = new ArrayList<>(size);
for (ResponseCookie cookie : value) {
cookies.add(cookie.getValue());
}
ret.addAll(key, cookies);
}
return ret;
}
完整代碼
為了方便大家使用,我把完整的代碼貼在此處
package com.demo.common.utils;
import com.demo.model.ProxyDO;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.ResponseCookie;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import reactor.netty.tcp.ProxyProvider;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* WebClientUtils
*
* @author John Chen
* @since 2020/12/23
*/
public class WebClientUtils {
/**
* 默認3分鐘超時時間
*/
private final static Duration DEFAULT_REQUEST_TIMEOUT = Duration.ofMinutes(3L);
/**
* 默認代理超時時間
*/
private final static Long DEFAULT_PROXY_TIMEOUT_MILLIS = DEFAULT_REQUEST_TIMEOUT.toMillis();
//region 生成WebClient.Builder的方法
/**
* 給了一個默認的WebClient,這個Client里面配置了默認請求超時時間
*
* @return 回傳一個帶超時時間的{@link WebClient.Builder}
*/
public static WebClient.Builder getDefaultWebClientBuilder() {
return getWebClientBuilder(DEFAULT_REQUEST_TIMEOUT);
}
/**
* [基礎創建方法]
* 給了一個默認的WebClient,這個Client里面配置了指定了請求超時時間
*
* @param requestTimeOut 請求超時時間
* @return 回傳一個帶超時時間的{@link WebClient.Builder}
*/
public static WebClient.Builder getWebClientBuilder(Duration requestTimeOut) {
if (requestTimeOut == null) {
requestTimeOut = DEFAULT_REQUEST_TIMEOUT;
}
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient
.create()
//重新定向開啟
.followRedirect(true)
.responseTimeout(requestTimeOut)));
}
/**
* 給到一個帶默認超時時間,并帶有不校驗任何SSL整數的WebClient
*
* @return 回傳一個帶默認超時時間和默認全域信任的SSL請求校驗器{@link WebClient.Builder}
*/
public static WebClient.Builder getWebClientBuilderWithSslTrust() {
return getWebClientBuilderWithSslTrust(DEFAULT_REQUEST_TIMEOUT);
}
/**
* 給到一個帶超時時間,并帶有不校驗任何SSL整數的WebClient
*
* @param requestTimeOut 超時時間
* @return 回傳一個帶超時時間和默認全域信任的SSL請求校驗器{@link WebClient.Builder}
*/
public static WebClient.Builder getWebClientBuilderWithSslTrust(Duration requestTimeOut) {
return getWebClientBuilderWithSslTrust(requestTimeOut, false);
}
/**
* [基礎創建方法]
* 給到一個帶超時時間,并帶有不校驗任何SSL整數的WebClient
*
* @param requestTimeOut 超時時間
* @param compressionEnabled 開啟壓縮?默認關閉
* @return 回傳一個帶超時時間和默認全域信任的SSL請求校驗器{@link WebClient.Builder}
*/
public static WebClient.Builder getWebClientBuilderWithSslTrust(Duration requestTimeOut, boolean compressionEnabled) {
if (requestTimeOut == null) {
requestTimeOut = DEFAULT_REQUEST_TIMEOUT;
}
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient
.create()
//重新定向開啟
.followRedirect(true)
//這里注入了一個拋棄一切SSL認證的sslContext
.secure(sslContextSpec -> sslContextSpec.sslContext(SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE)))
.responseTimeout(requestTimeOut)
.compress(compressionEnabled)
));
}
/**
* 給到一個帶超時時間,帶代理,并帶有不校驗任何SSL整數的WebClient
*
* @param requestTimeOut 超時時間
* @param proxyDO 代理物體
* @return 回傳一個帶超時時間和默認全域信任的SSL請求校驗器{@link WebClient.Builder}
*/
public static WebClient.Builder getWebClientBuilderWithSslTrustAndPolicy(Duration requestTimeOut, ProxyDO proxyDO) {
return getWebClientBuilderWithSslTrustAndPolicy(requestTimeOut, proxyDO, false);
}
/**
* [基礎創建方法]
* 給到一個帶超時時間,帶代理,并帶有不校驗任何SSL整數的WebClient
*
* @param requestTimeOut 超時時間
* @param proxyDO 代理物體
* @param compressionEnabled 開啟壓縮?默認關閉
* @return 回傳一個帶超時時間和默認全域信任的SSL請求校驗器{@link WebClient.Builder}
*/
public static WebClient.Builder getWebClientBuilderWithSslTrustAndPolicy(Duration requestTimeOut, ProxyDO proxyDO, boolean compressionEnabled) {
if (requestTimeOut == null) {
requestTimeOut = DEFAULT_REQUEST_TIMEOUT;
}
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient
.create()
//這里注入了一個拋棄一切SSL認證的sslContext
.secure(sslContextSpec -> sslContextSpec.sslContext(SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE)))
.responseTimeout(requestTimeOut)
.compress(compressionEnabled)
//重新定向開啟
.followRedirect(true)
.tcpConfiguration(tcpClient -> tcpClient.proxy(
p -> {
ProxyProvider.Builder pb = p.type(ProxyProvider.Proxy.HTTP)
.address(InetSocketAddress.createUnresolved(proxyDO.getServiceAddress(), Integer.parseInt(proxyDO.getPort())));
if (StringUtils.isNotEmpty(proxyDO.getUserName())) {
pb.username(proxyDO.getUserName())
.password(v -> proxyDO.getPassword());
}
Long proxyTimeOutMillis = proxyDO.getProxyTimeOutMillis();
if (proxyTimeOutMillis != null && proxyTimeOutMillis > 0) {
pb.connectTimeoutMillis(proxyTimeOutMillis);
} else {
pb.connectTimeoutMillis(DEFAULT_PROXY_TIMEOUT_MILLIS);
}
}
))
));
}
//endregion
/**
* 將http相應中的Cookie轉換為用于http請求中的cookie
* 方法中僅進行簡單轉換,不會對Cookie有效期等進行判斷
*
* @param responseCookie 需要被轉換的cookie
* @return 回傳可以用于請求的Cookies
*/
public static MultiValueMap<String, String> transformResponseCookiesToRequestCookies(MultiValueMap<String, ResponseCookie> responseCookie) {
MultiValueMap<String, String> ret = new LinkedMultiValueMap<>();
if (responseCookie == null || responseCookie.size() == 0) {
return ret;
}
for (Map.Entry<String, List<ResponseCookie>> entity : responseCookie.entrySet()) {
String key = entity.getKey();
List<ResponseCookie> value = entity.getValue();
int size = value.size();
if (size == 0) {
continue;
}
List<String> cookies = new ArrayList<>(size);
for (ResponseCookie cookie : value) {
cookies.add(cookie.getValue());
}
ret.addAll(key, cookies);
}
return ret;
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/261052.html
標籤:java
上一篇:設計模式在JDK原始碼中的應用
