jdk自带的httpclient简单使用你在用吗?
封装一个Http连接器HttpConnector,代码如下:
package com.vtarj.pythagoras.tools.http;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.text.MessageFormat;
import java.time.Duration;
import java.util.Map;
import java.util.Optional;
import java.util.StringJoiner;
/**
* @Author Vtarj
* @Description Http连接器
* @Time 2022/3/18 16:06
**/
public class HttpConnector {
private HttpConnectorConfig httpConnectorConfig;
private volatile HttpClient client;
/**
* 构造HttpExplorer
* @param httpConnectorConfig HttpExplorer参数
*/
public HttpConnector(HttpConnectorConfig httpConnectorConfig) {
if (client == null){
synchronized (HttpConnector.class){
if(client == null){
this.httpConnectorConfig = httpConnectorConfig;
HttpClient.Builder builder = HttpClient.newBuilder()
.version(httpConnectorConfig.getVersion())
.connectTimeout(Duration.ofMillis(httpConnectorConfig.getConnectTimeout()))
.followRedirects(httpConnectorConfig.getRedirect());
Optional.ofNullable(httpConnectorConfig.getAuthenticator()).ifPresent(builder::authenticator);
Optional.ofNullable(httpConnectorConfig.getCookieManager()).ifPresent(builder::cookieHandler);
Optional.ofNullable(httpConnectorConfig.getProxySelector()).ifPresent(builder::proxy);
Optional.ofNullable(httpConnectorConfig.getExecutor()).ifPresent(builder::executor);
client = builder.build();
}
}
}
}
/**
* 构造Get请求
* @param url 请求地址
* @return 返回的响应信息
*/
private HttpRequest buildGetRequest(String url){
return HttpRequest.newBuilder()
.GET()
.headers(httpConnectorConfig.buildHeader())
.uri(URI.create(url))
.timeout(Duration.ofMillis(httpConnectorConfig.getDefaultReadTimeout()))
.build();
}
/**
* 构造Post请求,form表单提交
* @param url 请求地址
* @param form 提交的form表单
* @return 返回的响应信息
*/
private HttpRequest buildPostRequest(String url, Map<String ,Object> form){
return HttpRequest.newBuilder()
.POST(formToPublisher(form))
.headers(httpConnectorConfig.buildHeader("application/x-www-form-urlencoded"))
.uri(URI.create(url))
.timeout(Duration.ofMillis(httpConnectorConfig.getDefaultReadTimeout()))
.build();
}
/**
* 构造Put请求,form表单提交
* @param url 请求地址
* @param form 提交的form表单
* @return 返回的响应信息
*/
private HttpRequest buildPutRequest(String url, Map<String ,Object> form){
return HttpRequest.newBuilder()
.PUT(formToPublisher(form))
.headers(httpConnectorConfig.buildHeader("application/x-www-form-urlencoded"))
.uri(URI.create(url))
.timeout(Duration.ofMillis(httpConnectorConfig.getDefaultReadTimeout()))
.build();
}
/**
* 构造Delete请求
* @param url 请求地址
* @return 返回的响应信息
*/
private HttpRequest buildDeleteRequest(String url){
return HttpRequest.newBuilder()
.DELETE()
.headers(httpConnectorConfig.buildHeader())
.uri(URI.create(url))
.timeout(Duration.ofMillis(httpConnectorConfig.getDefaultReadTimeout()))
.build();
}
/**
* 发送Get请求
* @param url 请求地址
* @param resClass 响应类型,支持:byte[].class、String.class、InputStream.class
* @param <T> 响应类型
* @return 响应信息
* @throws IOException IO异常
* @throws InterruptedException 线程等待
*/
public <T> T doGet(String url,Class<T> resClass) throws IOException, InterruptedException {
HttpRequest httpRequest = buildGetRequest(url);
return response(httpRequest,resClass);
}
/**
* 发送Post请求
* @param url 请求地址
* @param form 提交的表单
* @param resClass 响应类型,支持:byte[].class、String.class、InputStream.class
* @param <T> 响应类型
* @return 响应信息
* @throws IOException IO异常
* @throws InterruptedException 线程等待
*/
public <T> T doPost(String url,Map<String ,Object> form,Class<T> resClass) throws IOException, InterruptedException {
HttpRequest httpRequest = buildPostRequest(url,form);
return response(httpRequest,resClass);
}
/**
* 发送Put请求
* @param url 请求地址
* @param form 提交的表单
* @param resClass 响应类型,支持:byte[].class、String.class、InputStream.class
* @param <T> 响应类型
* @return 响应信息
* @throws IOException IO异常
* @throws InterruptedException 线程等待
*/
public <T> T doPut(String url,Map<String ,Object> form,Class<T> resClass) throws IOException, InterruptedException {
HttpRequest httpRequest = buildPutRequest(url, form);
return response(httpRequest,resClass);
}
/**
* 发送Delete请求
* @param url 请求地址
* @param resClass 响应类型,支持:byte[].class、String.class、InputStream.class
* @param <T> 响应类型
* @return 响应信息
* @throws IOException IO异常
* @throws InterruptedException 线程等待
*/
public <T> T doDelete(String url,Class<T> resClass) throws IOException, InterruptedException {
HttpRequest httpRequest = buildDeleteRequest(url);
return response(httpRequest,resClass);
}
/**
* 通用发送请求并解析响应
* @param httpRequest 请求体
* @param resClass 响应类型
* @param <T> 响应类型
*/
private <T> T response(HttpRequest httpRequest,Class<T> resClass) throws IOException, InterruptedException {
T t;
if(byte[].class == resClass){
t = (T) client.send(httpRequest, HttpResponse.BodyHandlers.ofByteArray()).body();
} else if (String.class == resClass){
t = (T) client.send(httpRequest, HttpResponse.BodyHandlers.ofString(httpConnectorConfig.getResponseCode())).body();
} else if (InputStream.class == resClass){
t = (T) client.send(httpRequest,HttpResponse.BodyHandlers.ofInputStream()).body();
} else {
throw new UnsupportedOperationException(MessageFormat.format("暂不支持该类型返回:[{0}]",resClass));
}
return t;
}
/**
* 将Form表单转换为请求所需的publisher,供post、put等模式调用
* @param form Form表单内容
* @return 转换后的publisher
*/
private HttpRequest.BodyPublisher formToPublisher(Map<String ,Object> form){
StringJoiner sj = new StringJoiner("&");
form.forEach((k,v) -> sj.add(k + "=" + v.toString()));
return HttpRequest.BodyPublishers.ofString(sj.toString(), httpConnectorConfig.getRequestCode());
}
}
再封装一个HttpConnector的配置器HttpConnectorConfig,代码如下:
package com.vtarj.pythagoras.tools.http;
import lombok.Data;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.net.*;
import java.net.http.HttpClient;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* @Author Vtarj
* @Description HttpConnector配置器
* @Time 2022/3/24 12:36
**/
@Data
public class HttpConnectorConfig {
/** * Http版本,默认HTTP_2 */
private HttpClient.Version version = HttpClient.Version.HTTP_2;
/** * 转发策略 */
private HttpClient.Redirect redirect = HttpClient.Redirect.NORMAL;
/** * 线程池,默认5个连接 */
private Executor executor;
/** * 认证信息 */
private Authenticator authenticator;
/** * 代理信息 */
private ProxySelector proxySelector;
/** * Cookies信息 */
private CookieManager cookieManager;
/** * SSL连接信息 */
private SSLContext sslContext;
/** * SSL连接参数 */
private SSLParameters sslParameters;
/** * 连接超时时间,毫秒 */
private int connectTimeout = 10000;
/** * 默认读取数据超时时间,毫秒 */
private int defaultReadTimeout = 1200000;
/** * 默认Content-Type */
private static final String defaultContentType = "application/json";
/** * 默认内容编码 */
private Charset requestCode = StandardCharsets.UTF_8,responseCode = StandardCharsets.UTF_8;
/** * 自定义头信息 */
private Map<String,String> headerMap;
/** * 构造函数 */
public HttpConnectorConfig() {
TrustManager[] trustAllCertificates = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
};
sslParameters = new SSLParameters();
sslParameters.setEndpointIdentificationAlgorithm("");
try {
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null,trustAllCertificates,new SecureRandom());
} catch (NoSuchAlgorithmException | KeyManagementException e){
e.printStackTrace();
}
}
/**
* 构造头文件
* @return 头文件信息
*/
public String[] buildHeader(){
return buildHeader(defaultContentType);
}
/**
* 构造头文件参数,允许强制设定Content-Type
* @param contentType 强制的Content-Type
* @return 头文件信息
*/
public String[] buildHeader(String contentType){
if (headerMap == null){
headerMap = new HashMap<>();
headerMap.put("Content-Type", contentType);
} else {
Set<String> headerKeys = headerMap.keySet();
if(headerKeys.stream().noneMatch("Content-Type" :: equalsIgnoreCase)){
headerMap.put("Content-Type", contentType);
}
}
String[] result = new String[headerMap.size() * 2];
int index = 0;
for (Map.Entry<String,String> entry:
headerMap.entrySet()) {
result[index++] = entry.getKey();
result[index++] = entry.getValue();
}
return result;
}
/**
* 构建线程池,支持快捷构建默认线程池
*/
public void buildExecutor(){
if(executor == null){
this.executor = Executors.newFixedThreadPool(5);
}
}
/**
* 构建Cookie,支持快速构建默认Cookie
*/
public void buildCookieManager(){
if(this.cookieManager == null){
this.cookieManager = new CookieManager();
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
}
}
}
测试一下效果:
@Test
void testHttConector() throws IOException, InterruptedException{
HttpConnectorConfig config = new HttpConnectorConfig();
Map<String,String> headerMap = new HashMap<>();
headerMap.put("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.46");
config.setHeaderMap(headerMap);
config.setRequestCode(Charset.forName("GBK"));
config.setResponseCode(Charset.forName("GBK"));
config.buildCookieManager();
config.buildExecutor();
//config.setVersion(HttpClient.Version.HTTP_1_1);
config.setRedirect(HttpClient.Redirect.NEVER);
HttpConnector connector = new HttpConnector(config);
String result = connector.doGet("http://www.66ip.cn/index.html",String.class);
System.out.println(result);
}
这样一个简单的封装使用就完成了
在封装中发现,JDK自带的请求与HttpClient等工具使用效果还是有一定不足的,因为是原生工具,所以很多东西都没有封装,且在使用中需要很多的转换,比如对response的处理和对request的设定都有一定局限性,因此自带的httpclient工具感兴趣了研究可以,投产使用还是要花一些心思好好封装的,希望本文对您有用!