alexpdh's blog

httpclient 调用工具(同时支持 HTTP 和 HTTPS 协议)

简介

接口调用是作为后端开发人员在编码过程中的日常,而 HTTP 协议可能是现在 Internet 上使用最多、最重要的协议。虽然在 JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。随着互联网安全性提高,各个互联网公司将自己站点 HTTPS 化,而在调用 HTTPS 协议接口时需要 SSL 证书认证。一般做法是创建一个SSL连接,接受自签名证书的信任策略,使其通过验证。 下面是一个我日常调用接口时使用的 httpclient 调用工具

引入 jar 包

maven 引入 httpclient jar包:

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.6</version>
</dependency>

HttpClientUtil.java

httpclient 调用工具类,封装一系列静态入口方法,供其它对象调用

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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
package com.pdh.http;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
/**
* httpclient 调用工具
*
* @author pengdh
* @date 2017/10/28
*/
public class HttpClientUtil {
/**
* 缺省超时时间 单位:ms
*/
private static final int TIMEOUT = 60000;
/**
* 是否重定向标识
*/
private static final boolean IS_REDIRECTS = false;
private static String EMPTY_STR = "";
/**
* 字符集编码格式
*/
private static String UTF_8 = "UTF-8";
private HttpClientUtil() {
}
/**
* 发送 get 请求
*
* @param url 请求地址
* @return String
*/
public static String httpGetRequest(String url) {
HttpGet httpGet = new HttpGet(url);
return execute(httpGet);
}
/**
* 发送 get 请求
*
* @param url 请求地址
* @param headers 头信息
* @return String
*/
public static String httpGetRequestWithHeaders(String url, Map<String, Object> headers) {
HttpGet httpGet = new HttpGet(url);
for (Map.Entry<String, Object> param : headers.entrySet()) {
httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));
}
return execute(httpGet);
}
/**
* 发送 get 请求
*
* @param url 请求地址
* @param headers 头信息
* @param params 参数
* @return String
*/
public static String httpGetRequest(String url, Map<String, Object> headers,
Map<String, Object> params) {
HttpGet httpGet = new HttpGet(createParamUrl(url, params));
for (Map.Entry<String, Object> param : headers.entrySet()) {
httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));
}
return execute(httpGet);
}
/**
* 发送 get 请求
*
* @param url 请求地址
* @return String
*/
public static String httpGetRequestWithParams(String url, Map<String, Object> params) {
HttpGet httpGet = new HttpGet(createParamUrl(url, params));
return execute(httpGet);
}
/**
* 创建带参数的 URL
*
* @param url 无参URL
* @param params 参数
* @return String 带参数URL
*/
private static String createParamUrl(String url, Map<String, Object> params) {
Iterator<String> it = params.keySet().iterator();
StringBuilder sb = new StringBuilder();
boolean isIncludeQuestionMark = url.contains("?");
if (!isIncludeQuestionMark) {
sb.append("?");
}
while (it.hasNext()) {
String key = it.next();
String value = (String) params.get(key);
sb.append("&");
sb.append(key);
sb.append("=");
sb.append(value);
}
url += sb.toString();
return url;
}
/**
* 发送 post 请求
*
* @param url 请求地址
* @return String
*/
public static String httpPostRequest(String url) {
HttpPost httpPost = new HttpPost(url);
return execute(httpPost);
}
/**
* 发送 post 请求
*
* @param url 地址
* @param params 参数
* @return String
*/
public static String httpPostRequest(String url, Map<String, Object> params) {
HttpPost httpPost = new HttpPost(url);
ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
try {
httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return execute(httpPost);
}
/**
* 发送 post 请求
*
* @param url 地址
* @param headers 头信息
* @param params 参数
* @return String
*/
public static String httpPostRequest(String url, Map<String, Object> headers,
Map<String, Object> params) {
HttpPost httpPost = new HttpPost(url);
for (Map.Entry<String, Object> headerParam : headers.entrySet()) {
httpPost.addHeader(headerParam.getKey(), String.valueOf(headerParam.getValue()));
}
ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
try {
httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return execute(httpPost);
}
/**
* 发送 post 请求
*
* @param url 地址
* @param headers 头信息
* @param json json 格式参数
* @return String
*/
public static String httpPostRequestByJson(String url, Map<String, Object> headers,
String json) {
HttpPost httpPost = new HttpPost(url);
for (Map.Entry<String, Object> headerParam : headers.entrySet()) {
httpPost.addHeader(headerParam.getKey(), String.valueOf(headerParam.getValue()));
}
try {
httpPost.setEntity(new StringEntity(json, UTF_8));
} catch (UnsupportedCharsetException e) {
e.printStackTrace();
}
return execute(httpPost);
}
/**
* 把参数转换为名值对数组
*
* @param params 参数
* @return ArrayList<NameValuePair>
*/
private static ArrayList<NameValuePair> covertParams2NVPS(Map<String, Object> params) {
ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();
for (Map.Entry<String, Object> param : params.entrySet()) {
pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));
}
return pairs;
}
/**
* 执行 HTTP 请求 若重定向返回重定向地址
*
* @return String
*/
private static String execute(HttpRequestBase request) {
String result = EMPTY_STR;
request.setConfig(createConfig(TIMEOUT, IS_REDIRECTS));
CloseableHttpClient httpClient = getHttpClient();
try {
CloseableHttpResponse response = httpClient.execute(request);
if (isRedirected(response)) {
result = getRedirectedUrl(response);
} else {
result = getEntityData(response);
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* 创建HTTP请求配置
*
* @param timeout 超时时间
* @param redirectsEnabled 是否开启重定向
* @return RequestConfig
*/
private static RequestConfig createConfig(int timeout, boolean redirectsEnabled) {
return RequestConfig.custom()
// 读取数据超时时间(毫秒)
.setSocketTimeout(timeout)
// 建立连接超时时间(毫秒)
.setConnectTimeout(timeout)
// 从连接池获取连接的等待时间(毫秒)
.setConnectionRequestTimeout(timeout)
// 当响应状态码为302时,是否进行重定向
.setRedirectsEnabled(redirectsEnabled)
.build();
}
/**
* 通过连接池获取 httpclient
*/
private static CloseableHttpClient getHttpClient() {
return HttpClients.custom().setConnectionManager(
HttpConnectionManager.POOLING_CONNECTION_MANAGER).build();
}
/**
* 判断发送请求是否重定向跳转过
*
* @param response 请求响应
* @return boolean
*/
private static boolean isRedirected(CloseableHttpResponse response) {
int statusCode = response.getStatusLine().getStatusCode();
return statusCode == HttpStatus.SC_MOVED_PERMANENTLY
|| statusCode == HttpStatus.SC_MOVED_TEMPORARILY;
}
/**
* 获得重定向跳转地址
*
* @param response 请求响应
* @return String 重定向地址
*/
private static String getRedirectedUrl(CloseableHttpResponse response) {
String result = EMPTY_STR;
Header[] hs = response.getHeaders("Location");
if (hs.length > 0) {
result = hs[0].getValue();
}
return result;
}
/**
* 获得响应实体信息
*
* @param response 请求响应
* @return String 消息实体信息
*/
private static String getEntityData(CloseableHttpResponse response)
throws ParseException, IOException {
String result = EMPTY_STR;
HttpEntity entity = response.getEntity();
if (entity != null) {
result = EntityUtils.toString(entity);
response.close();
}
return result;
}
}

HttpConnectionManager.java

httpClient 连接管理器,支持创建普通连接和使用使用连接池

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
package com.migu.barrage.common.http;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
/**
* httpClient 连接管理器
*
* @author pengdh
* @date 2017/10/28
*/
public class HttpConnectionManager {
/**
* 普通连接管理器
*/
public static final HttpClientConnectionManager BASIC_CONNECTION_MANAGER;
/**
* 连接池管理器
*/
public static final HttpClientConnectionManager POOLING_CONNECTION_MANAGER;
static {
Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLSelfSigned.SSL_CONNECTION_SOCKET_FACTORY).build();
// 普通连接管理器
BASIC_CONNECTION_MANAGER = new BasicHttpClientConnectionManager(r);
// 连接池管理器
PoolingHttpClientConnectionManager pooling = new PoolingHttpClientConnectionManager(r);
// 设置最大连接数
pooling.setMaxTotal(1000);
// 设置每个路由基础上的最大连接数
pooling.setDefaultMaxPerRoute(300);
POOLING_CONNECTION_MANAGER = pooling;
}
private HttpConnectionManager() {
}
/**
* @param max httpClient 最大连接数
*/
public static void setMaxTotal(int max) {
((PoolingHttpClientConnectionManager) POOLING_CONNECTION_MANAGER).setMaxTotal(max);
}
/**
* @param max 每个路由基础上的最大连接数
*/
public static void setDefaultMaxPerRoute(int max) {
((PoolingHttpClientConnectionManager) POOLING_CONNECTION_MANAGER).setDefaultMaxPerRoute(max);
}
}

SSLSelfSigned.java

SSL自签证书

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
package com.pdh.http;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.ssl.SSLContexts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 如果 https 使用自签名证书(不是第三方机构颁发的证书),无法通过验证;
* 因此需要创建一个SSL连接,接受自签名证书的信任策略,使其通过验证。
*
* @author pengdh
* @date 2017/10/28
*/
public class SSLSelfSigned {
public static final SSLConnectionSocketFactory SSL_CONNECTION_SOCKET_FACTORY;
protected static final Logger logger = LoggerFactory.getLogger(SSLSelfSigned.class);
static {
SSLContext sslContext = null;
try {
sslContext = SSLContexts.custom().loadTrustMaterial(TrustSelfSignedStrategy.INSTANCE).build();
} catch (KeyManagementException e) {
logger.error("{}", e);
} catch (NoSuchAlgorithmException e) {
logger.error("{}", e);
} catch (KeyStoreException e) {
logger.error("{}", e);
}
SSL_CONNECTION_SOCKET_FACTORY = new SSLConnectionSocketFactory(sslContext,
NoopHostnameVerifier.INSTANCE);
}
private SSLSelfSigned() {
}
}
alexpdh wechat
欢迎扫一扫关注 程序猿pdh 公众号!