Http工具类封装

前言

为了支持多种HTTP客户端,可以使用接口和策略模式。我们可以创建一个HTTP客户端接口,并为Apache HttpClient和JDK 11 HttpClient分别实现这个接口。这样,可以根据需要切换不同的HTTP客户端实现。

接口定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package cn.idea360.assistant.dev.utils;

import java.io.IOException;

/**
* @author cuishiying
* @date 2023-06-14
*/
public interface HttpClientService {

/**
* 发送GET请求
* @param url 请求URL
* @return 响应内容
* @throws IOException 请求失败时抛出
*/
String sendGet(String url) throws IOException;

/**
* 发送POST请求
* @param url 请求URL
* @param json JSON格式的请求体
* @return 响应内容
* @throws IOException 请求失败时抛出
*/
String sendPost(String url, String json) throws IOException;

/**
* 发送PUT请求
* @param url 请求URL
* @param json JSON格式的请求体
* @return 响应内容
* @throws IOException 请求失败时抛出
*/
String sendPut(String url, String json) throws IOException;

/**
* 发送DELETE请求
* @param url 请求URL
* @return 响应内容
* @throws IOException 请求失败时抛出
*/
String sendDelete(String url) throws IOException;

}

ApacheHttpClientService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package cn.idea360.assistant.dev.utils;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
* @author cuishiying
* @date 2023-06-14
*/
public class ApacheHttpClientService implements HttpClientService {

private static final Logger logger = LoggerFactory.getLogger(ApacheHttpClientService.class);

private static final int MAX_TOTAL_CONNECTIONS = 300; // 最大连接数
private static final int MAX_PER_ROUTE = 100; // 每个路由的默认最大连接数
private static final int CONNECTION_TIMEOUT = 5000; // 建立连接的超时时间
private static final int REQUEST_TIMEOUT = 5000; // 从连接池获取连接的超时时间
private static final int SOCKET_TIMEOUT = 5000; // 数据传输的超时时间

private static final CloseableHttpClient httpClient;

static {
// 连接池管理器
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
connManager.setMaxTotal(MAX_TOTAL_CONNECTIONS);
connManager.setDefaultMaxPerRoute(MAX_PER_ROUTE);

// 请求配置
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(CONNECTION_TIMEOUT)
.setConnectionRequestTimeout(REQUEST_TIMEOUT)
.setSocketTimeout(SOCKET_TIMEOUT)
.build();

// 创建HttpClient
httpClient = HttpClients.custom()
.setConnectionManager(connManager) // 设置连接池管理器
.setDefaultRequestConfig(requestConfig) // 设置请求配置
.build();
}

public ApacheHttpClientService() {
}

@Override
public String sendGet(String url) throws IOException {
HttpGet request = new HttpGet(url);
return sendRequest(request);
}

@Override
public String sendPost(String url, String json) throws IOException {
HttpPost request = new HttpPost(url);
setEntity(request, json);
return sendRequest(request);
}

@Override
public String sendPut(String url, String json) throws IOException {
HttpPut request = new HttpPut(url);
setEntity(request, json);
return sendRequest(request);
}

@Override
public String sendDelete(String url) throws IOException {
HttpDelete request = new HttpDelete(url);
return sendRequest(request);
}

/**
* 发送HTTP请求
* @param request HttpUriRequest请求对象
* @return 响应内容
* @throws IOException 请求失败时抛出
*/
private static String sendRequest(HttpUriRequest request) throws IOException {
try (CloseableHttpResponse response = httpClient.execute(request)) {
int statusCode = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
String result = entity != null ? EntityUtils.toString(entity) : null;

if (statusCode >= HttpStatus.SC_OK && statusCode < HttpStatus.SC_MULTIPLE_CHOICES) {
return result;
} else {
handleErrorResponse(request, statusCode);
throw new IOException("请求失败, URL: " + request.getURI() + ", 状态码: " + statusCode);
}
} catch (IOException e) {
logger.error("请求失败, URL: {}, 原因: {}", request.getURI(), ExceptionUtils.getStackTrace(e));
throw e;
}
}

/**
* 设置请求体
*
* @param request HttpEntityEnclosingRequestBase请求对象
* @param json JSON格式的请求体
*/
private static void setEntity(HttpEntityEnclosingRequestBase request, String json) {
request.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
}

/**
* 处理错误响应
*
* @param request HttpUriRequest请求对象
* @param statusCode 响应状态码
*/
private static void handleErrorResponse(HttpUriRequest request, int statusCode) throws IOException {
logger.error("请求失败, URL: {}, 状态码: {}", request.getURI(), statusCode);
}

}

JdkHttpClientService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package cn.idea360.assistant.dev.utils;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

/**
* @author cuishiying
* @date 2023-06-14
*/
public class JdkHttpClientService implements HttpClientService {

private static final Logger logger = LoggerFactory.getLogger(JdkHttpClientService.class);

private static final int TIMEOUT = 5000; // 超时时间

private static final HttpClient httpClient;

static {
httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofMillis(TIMEOUT))
.build();
}

@Override
public String sendGet(String url) throws IOException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
return sendRequest(request);
}

@Override
public String sendPost(String url, String json) throws IOException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.POST(HttpRequest.BodyPublishers.ofString(json))
.header("Content-Type", "application/json")
.build();
return sendRequest(request);
}

@Override
public String sendPut(String url, String json) throws IOException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.PUT(HttpRequest.BodyPublishers.ofString(json))
.header("Content-Type", "application/json")
.build();
return sendRequest(request);
}

@Override
public String sendDelete(String url) throws IOException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.DELETE()
.build();
return sendRequest(request);
}

private String sendRequest(HttpRequest request) throws IOException {
try {
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
int statusCode = response.statusCode();
String result = response.body();

if (statusCode >= 200 && statusCode < 300) {
return result;
} else {
handleErrorResponse(request, statusCode);
throw new IOException("请求失败, URL: " + request.uri() + ", 状态码: " + statusCode);
}
} catch (InterruptedException e) {
logger.error("请求被中断, URL: {}, 原因: {}", request.uri(), e.getMessage());
Thread.currentThread().interrupt();
throw new IOException("请求被中断, URL: " + request.uri(), e);
} catch (IOException e) {
logger.error("请求失败, URL: {}, 原因: {}", request.uri(), ExceptionUtils.getStackTrace(e));
throw e;
}
}

private void handleErrorResponse(HttpRequest request, int statusCode) {
logger.error("请求失败, URL: {}, 状态码: {}", request.uri(), statusCode);
}
}

工厂类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package cn.idea360.assistant.dev.utils;

/**
* @author cuishiying
* @date 2023-06-14
*/
public class HttpClientFactory {
public static HttpClientService createHttpClientService(String clientType) {
if ("apache".equalsIgnoreCase(clientType)) {
return new ApacheHttpClientService();
} else if ("jdk".equalsIgnoreCase(clientType)) {
return new JdkHttpClientService();
} else {
throw new IllegalArgumentException("Unsupported client type: " + clientType);
}
}
}