HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
HTTP和浏览器有点像,但却不是浏览器。很多人觉得既然HttpClient是一个HTTP客户端编程工具,很多人把他当做浏览器来理解,但是其实HttpClient不是浏览器,它是一个HTTP通信库,因此它只提供一个通用浏览器应用程序所期望的功能子集,最根本的区别是HttpClient中没有用户界面,浏览器需要一个渲染引擎来显示页面,并解释用户输入,例如鼠标点击显示页面上的某处,有一个布局引擎,计算如何显示HTML页面,包括级联样式表和图像。javascript解释器运行嵌入HTML页面或从HTML页面引用的javascript代码。来自用户界面的事件被传递到javascript解释器进行处理。除此之外,还有用于插件的接口,可以处理Applet,嵌入式媒体对象(如pdf文件,Quicktime电影和Flash动画)或ActiveX控件(可以执行任何操作)。HttpClient只能以编程的方式通过其API用于传输和接受HTTP消息。
HttpClient的主要功能:
实现了所有 HTTP 的方法(GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS 等)
支持 HTTPS 协议
支持代理服务器(Nginx等)等
支持自动(跳转)转向
环境说明:JDK1.8、SpringBoot
注:本人引入此依赖的目的是,在后续示例中,会用到“将对象转化为json字符串的功能”,也可以引其他有此功能的依赖。
注:SpringBoot的基本依赖配置,这里就不再多说了。
详细使用示例
声明:此示例中,以JAVA发送HttpClient(在test里面单元测试发送的);也是以JAVA接收的(在controller里面接收的)。
声明:下面的代码,本人亲测有效。
GET无参:
HttpClient发送示例:
/**
* GET---无参测试
*
* @date 2018年7月13日 下午4:18:50
*/
@Test
public void doGetTestOne() {
// 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的)
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// 创建Get请求
HttpGet httpGet = new HttpGet("http://localhost:12345/doGetControllerOne");
// 响应模型
CloseableHttpResponse response = null;
try {
// 由客户端执行(发送)Get请求
response = httpClient.execute(httpGet);
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("响应内容长度为:" + responseEntity.getContentLength());
System.out.println("响应内容为:" + EntityUtils.toString(responseEntity));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
GET有参(方式一:直接拼接URL):
HttpClient发送示例:
/**
* GET---有参测试 (方式一:手动在url后面加上参数)
*
* @date 2018年7月13日 下午4:19:23
*/
@Test
public void doGetTestWayOne() {
// 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的)
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// 参数
StringBuffer params = new StringBuffer();
try {
// 字符数据最好encoding以下;这样一来,某些特殊字符才能传过去(如:某人的名字就是“&”,不encoding的话,传不过去)
params.append("name=" + URLEncoder.encode("&", "utf-8"));
params.append("&");
params.append("age=24");
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
// 创建Get请求
HttpGet httpGet = new HttpGet("http://localhost:12345/doGetControllerTwo" + "?" + params);
// 响应模型
CloseableHttpResponse response = null;
try {
// 配置信息
RequestConfig requestConfig = RequestConfig.custom()
// 设置连接超时时间(单位毫秒)
.setConnectTimeout(5000)
// 设置请求超时时间(单位毫秒)
.setConnectionRequestTimeout(5000)
// socket读写超时时间(单位毫秒)
.setSocketTimeout(5000)
// 设置是否允许重定向(默认为true)
.setRedirectsEnabled(true).build();
// 将上面的配置信息 运用到这个Get请求里
httpGet.setConfig(requestConfig);
// 由客户端执行(发送)Get请求
response = httpClient.execute(httpGet);
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("响应内容长度为:" + responseEntity.getContentLength());
System.out.println("响应内容为:" + EntityUtils.toString(responseEntity));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
对应接收示例:
GET有参(方式二:使用URI获得HttpGet):
HttpClient发送示例:
/**
* GET---有参测试 (方式二:将参数放入键值对类中,再放入URI中,从而通过URI得到HttpGet实例)
*
* @date 2018年7月13日 下午4:19:23
*/
@Test
public void doGetTestWayTwo() {
// 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的)
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// 参数
URI uri = null;
try {
// 将参数放入键值对类NameValuePair中,再放入集合中
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("name", "&"));
params.add(new BasicNameValuePair("age", "18"));
// 设置uri信息,并将参数集合放入uri;
// 注:这里也支持一个键值对一个键值对地往里面放setParameter(String key, String value)
uri = new URIBuilder().setScheme("http").setHost("localhost")
.setPort(12345).setPath("/doGetControllerTwo")
.setParameters(params).build();
} catch (URISyntaxException e1) {
e1.printStackTrace();
}
// 创建Get请求
HttpGet httpGet = new HttpGet(uri);
// 响应模型
CloseableHttpResponse response = null;
try {
// 配置信息
RequestConfig requestConfig = RequestConfig.custom()
// 设置连接超时时间(单位毫秒)
.setConnectTimeout(5000)
// 设置请求超时时间(单位毫秒)
.setConnectionRequestTimeout(5000)
// socket读写超时时间(单位毫秒)
.setSocketTimeout(5000)
// 设置是否允许重定向(默认为true)
.setRedirectsEnabled(true).build();
// 将上面的配置信息 运用到这个Get请求里
httpGet.setConfig(requestConfig);
// 由客户端执行(发送)Get请求
response = httpClient.execute(httpGet);
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("响应内容长度为:" + responseEntity.getContentLength());
System.out.println("响应内容为:" + EntityUtils.toString(responseEntity));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
对应接收示例:
POST无参:
HttpClient发送示例:
/**
* POST—无参测试
*
* @date 2018年7月13日 下午4:18:50
*/
@Test
public void doPostTestOne() {
// 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的)
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// 创建Post请求
HttpPost httpPost = new HttpPost("http://localhost:12345/doPostControllerOne");
// 响应模型
CloseableHttpResponse response = null;
try {
// 由客户端执行(发送)Post请求
response = httpClient.execute(httpPost);
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("响应内容长度为:" + responseEntity.getContentLength());
System.out.println("响应内容为:" + EntityUtils.toString(responseEntity));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
对应接收示例:
POST有参(普通参数):
注:POST传递普通参数时,方式与GET一样即可,这里以直接在url后缀上参数的方式示例。
HttpClient发送示例:
/**
* POST---有参测试(普通参数)
*
* @date 2018年7月13日 下午4:18:50
*/
@Test
public void doPostTestFour() {
// 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的)
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// 参数
StringBuffer params = new StringBuffer();
try {
// 字符数据最好encoding以下;这样一来,某些特殊字符才能传过去(如:某人的名字就是“&”,不encoding的话,传不过去)
params.append("name=" + URLEncoder.encode("&", "utf-8"));
params.append("&");
params.append("age=24");
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
// 创建Post请求
HttpPost httpPost = new HttpPost("http://localhost:12345/doPostControllerFour" + "?" + params);
// 设置ContentType(注:如果只是传普通参数的话,ContentType不一定非要用application/json)
httpPost.setHeader("Content-Type", "application/json;charset=utf8");
// 响应模型
CloseableHttpResponse response = null;
try {
// 由客户端执行(发送)Post请求
response = httpClient.execute(httpPost);
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("响应内容长度为:" + responseEntity.getContentLength());
System.out.println("响应内容为:" + EntityUtils.toString(responseEntity));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
对应接收示例:
POST有参(对象参数):
先给出User类
HttpClient发送示例:
/**
* POST---有参测试(对象参数)
*
* @date 2018年7月13日 下午4:18:50
*/
@Test
public void doPostTestTwo() {
// 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的)
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// 创建Post请求
HttpPost httpPost = new HttpPost("http://localhost:12345/doPostControllerTwo");
User user = new User();
user.setName("潘晓婷");
user.setAge(18);
user.setGender("女");
user.setMotto("姿势要优雅~");
// 我这里利用阿里的fastjson,将Object转换为json字符串;
// (需要导入com.alibaba.fastjson.JSON包)
String jsonString = JSON.toJSONString(user);
StringEntity entity = new StringEntity(jsonString, "UTF-8");
// post请求是将参数放在请求体里面传过去的;这里将entity放入post请求体中
httpPost.setEntity(entity);
httpPost.setHeader("Content-Type", "application/json;charset=utf8");
// 响应模型
CloseableHttpResponse response = null;
try {
// 由客户端执行(发送)Post请求
response = httpClient.execute(httpPost);
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("响应内容长度为:" + responseEntity.getContentLength());
System.out.println("响应内容为:" + EntityUtils.toString(responseEntity));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
对应接收示例:
POST有参(普通参数 + 对象参数):
注:POST传递普通参数时,方式与GET一样即可,这里以通过URI获得HttpPost的方式为例。
先给出User类:
HttpClient发送示例:
/**
* POST---有参测试(普通参数 + 对象参数)
*
* @date 2018年7月13日 下午4:18:50
*/
@Test
public void doPostTestThree() {
// 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的)
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// 创建Post请求
// 参数
URI uri = null;
try {
// 将参数放入键值对类NameValuePair中,再放入集合中
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("flag", "4"));
params.add(new BasicNameValuePair("meaning", "这是什么鬼?"));
// 设置uri信息,并将参数集合放入uri;
// 注:这里也支持一个键值对一个键值对地往里面放setParameter(String key, String value)
uri = new URIBuilder().setScheme("http").setHost("localhost").setPort(12345)
.setPath("/doPostControllerThree").setParameters(params).build();
} catch (URISyntaxException e1) {
e1.printStackTrace();
}
HttpPost httpPost = new HttpPost(uri);
// HttpPost httpPost = new
// HttpPost("http://localhost:12345/doPostControllerThree1");
// 创建user参数
User user = new User();
user.setName("潘晓婷");
user.setAge(18);
user.setGender("女");
user.setMotto("姿势要优雅~");
// 将user对象转换为json字符串,并放入entity中
StringEntity entity = new StringEntity(JSON.toJSONString(user), "UTF-8");
// post请求是将参数放在请求体里面传过去的;这里将entity放入post请求体中
httpPost.setEntity(entity);
httpPost.setHeader("Content-Type", "application/json;charset=utf8");
// 响应模型
CloseableHttpResponse response = null;
try {
// 由客户端执行(发送)Post请求
response = httpClient.execute(httpPost);
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("响应内容长度为:" + responseEntity.getContentLength());
System.out.println("响应内容为:" + EntityUtils.toString(responseEntity));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
对应接收示例:
提示:如果想要知道完整的具体的代码及测试细节,可去下面给的项目代码托管链接,将项目clone下来
进行观察。如果需要运行测试,可以先启动该SpringBoot项目,然后再运行相关test方法,进行
测试。
解决响应乱码问题(示例):
进行HTTPS请求并进行(或不进行)证书校验(示例):
使用示例:
相关方法详情(非完美封装):
/**
* 根据是否是https请求,获取HttpClient客户端
*
* TODO 本人这里没有进行完美封装。对于 校不校验校验证书的选择,本人这里是写死
* 在代码里面的,你们在使用时,可以灵活二次封装。
*
* 提示: 此工具类的封装、相关客户端、服务端证书的生成,可参考我的这篇博客:
* <linked>https://blog.csdn.net/justry_deng/article/details/91569132</linked>
*
*
* @param isHttps 是否是HTTPS请求
*
* @return HttpClient实例
* @date 2019/9/18 17:57
*/
private CloseableHttpClient getHttpClient(boolean isHttps) {
CloseableHttpClient httpClient;
if (isHttps) {
SSLConnectionSocketFactory sslSocketFactory;
try {
/// 如果不作证书校验的话
sslSocketFactory = getSocketFactory(false, null, null);
/// 如果需要证书检验的话
// 证书
//InputStream ca = this.getClass().getClassLoader().getResourceAsStream("client/ds.crt");
// 证书的别名,即:key。 注:cAalias只需要保证唯一即可,不过推荐使用生成keystore时使用的别名。
// String cAalias = System.currentTimeMillis() + "" + new SecureRandom().nextInt(1000);
//sslSocketFactory = getSocketFactory(true, ca, cAalias);
} catch (Exception e) {
throw new RuntimeException(e);
}
httpClient = HttpClientBuilder.create().setSSLSocketFactory(sslSocketFactory).build();
return httpClient;
}
httpClient = HttpClientBuilder.create().build();
return httpClient;
}
/**
* HTTPS辅助方法, 为HTTPS请求 创建SSLSocketFactory实例、TrustManager实例
*
* @param needVerifyCa
* 是否需要检验CA证书(即:是否需要检验服务器的身份)
* @param caInputStream
* CA证书。(若不需要检验证书,那么此处传null即可)
* @param cAalias
* 别名。(若不需要检验证书,那么此处传null即可)
* 注意:别名应该是唯一的, 别名不要和其他的别名一样,否者会覆盖之前的相同别名的证书信息。别名即key-value中的key。
*
* @return SSLConnectionSocketFactory实例
* @throws NoSuchAlgorithmException
* 异常信息
* @throws CertificateException
* 异常信息
* @throws KeyStoreException
* 异常信息
* @throws IOException
* 异常信息
* @throws KeyManagementException
* 异常信息
* @date 2019/6/11 19:52
*/
private static SSLConnectionSocketFactory getSocketFactory(boolean needVerifyCa, InputStream caInputStream, String cAalias)
throws CertificateException, NoSuchAlgorithmException, KeyStoreException,
IOException, KeyManagementException {
X509TrustManager x509TrustManager;
// https请求,需要校验证书
if (needVerifyCa) {
KeyStore keyStore = getKeyStore(caInputStream, cAalias);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
x509TrustManager = (X509TrustManager) trustManagers[0];
// 这里传TLS或SSL其实都可以的
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{x509TrustManager}, new SecureRandom());
return new SSLConnectionSocketFactory(sslContext);
}
// https请求,不作证书校验
x509TrustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) {
}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) {
// 不验证
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{x509TrustManager}, new SecureRandom());
return new SSLConnectionSocketFactory(sslContext);
}
/**
* 获取(密钥及证书)仓库
* 注:该仓库用于存放 密钥以及证书
*
* @param caInputStream
* CA证书(此证书应由要访问的服务端提供)
* @param cAalias
* 别名
* 注意:别名应该是唯一的, 别名不要和其他的别名一样,否者会覆盖之前的相同别名的证书信息。别名即key-value中的key。
* @return 密钥、证书 仓库
* @throws KeyStoreException 异常信息
* @throws CertificateException 异常信息
* @throws IOException 异常信息
* @throws NoSuchAlgorithmException 异常信息
* @date 2019/6/11 18:48
*/
private static KeyStore getKeyStore(InputStream caInputStream, String cAalias)
throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException {
// 证书工厂
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
// 秘钥仓库
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
keyStore.setCertificateEntry(cAalias, certificateFactory.generateCertificate(caInputStream));
return keyStore;
}
application/x-www-form-urlencoded表单请求(示例):
发送文件(示例):
准备工作:
如果想要灵活方便的传输文件的话,除了引入org.apache.httpcomponents基本的httpclient依赖外再额外引入org.apache.httpcomponents的httpmime依赖。
P.S.:即便不引入httpmime依赖,也是能传输文件的,不过功能不够强大。
在pom.xml中额外引入:
org.apache.httpcomponents httpmime 4.5.5发送端是这样的:
/**
*
- 发送文件
- multipart/form-data传递文件(及相关信息)
- 注:如果想要灵活方便的传输文件的话,
- 除了引入org.apache.httpcomponents基本的httpclient依赖外
- 再额外引入org.apache.httpcomponents的httpmime依赖。
- 追注:即便不引入httpmime依赖,也是能传输文件的,不过功能不够强大。
*/
@Test
public void test4() {
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(“http://localhost:12345/file”);
CloseableHttpResponse response = null;
try {
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
// 第一个文件
String filesKey = “files”;
File file1 = new File(“C:\Users\JustryDeng\Desktop\back.jpg”);
multipartEntityBuilder.addBinaryBody(filesKey, file1);
// 第二个文件(多个文件的话,使用同一个key就行,后端用数组或集合进行接收即可)
File file2 = new File(“C:\Users\JustryDeng\Desktop\头像.jpg”);
// 防止服务端收到的文件名乱码。 我们这里可以先将文件名URLEncode,然后服务端拿到文件名时在URLDecode。就能避免乱码问题。
// 文件名其实是放在请求头的Content-Disposition里面进行传输的,如其值为form-data; name=“files”; filename=“头像.jpg”
multipartEntityBuilder.addBinaryBody(filesKey, file2, ContentType.DEFAULT_BINARY, URLEncoder.encode(file2.getName(), “utf-8”));
// 其它参数(注:自定义contentType,设置UTF-8是为了防止服务端拿到的参数出现乱码)
ContentType contentType = ContentType.create(“text/plain”, Charset.forName(“UTF-8”));
multipartEntityBuilder.addTextBody(“name”, “邓沙利文”, contentType);
multipartEntityBuilder.addTextBody(“age”, “25”, contentType);
HttpEntity httpEntity = multipartEntityBuilder.build();
httpPost.setEntity(httpEntity);
response = httpClient.execute(httpPost);
HttpEntity responseEntity = response.getEntity();
System.out.println("HTTPS响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("HTTPS响应内容长度为:" + responseEntity.getContentLength());
// 主动设置编码,来防止响应乱码
String responseStr = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
System.out.println("HTTPS响应内容为:" + responseStr);
}
} catch (ParseException | IOException e) {
e.printStackTrace();
} finally {
try {
// 释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
接收端是这样的:
发送流(示例):
发送端是这样的:
/**
*
* 发送流
*
*/
@Test
public void test5() {
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost("http://localhost:12345/is?name=邓沙利文");
CloseableHttpResponse response = null;
try {
InputStream is = new ByteArrayInputStream("流啊流~".getBytes());
InputStreamEntity ise = new InputStreamEntity(is);
httpPost.setEntity(ise);
response = httpClient.execute(httpPost);
HttpEntity responseEntity = response.getEntity();
System.out.println("HTTPS响应状态为:" + response.getStatusLine());
if (responseEntity != null) {
System.out.println("HTTPS响应内容长度为:" + responseEntity.getContentLength());
// 主动设置编码,来防止响应乱码
String responseStr = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
System.out.println("HTTPS响应内容为:" + responseStr);
}
} catch (ParseException | IOException e) {
e.printStackTrace();
} finally {
try {
// 释放资源
if (httpClient != null) {
httpClient.close();
}
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
接收端是这样的:
Maven坐标:
<!-- https://mvnrepository.com/artifact/com.arronlong/httpclientutil -->
<dependency>
<groupId>com.arronlong</groupId>
<artifactId>httpclientutil</artifactId>
<version>1.0.4</version>
</dependency>
demo
public static void main(String[] args) throws HttpProcessException, FileNotFoundException {
String url = "https://github.com/Arronlong/httpclientutil";
//最简单的使用:
String html = HttpClientUtil.get(HttpConfig.custom().url(url));
System.out.println(html);
//---------------------------------
// 【详细说明】
//--------------------------------
//插件式配置Header(各种header信息、自定义header)
Header[] headers = HttpHeader.custom()
.userAgent("javacl")
.other("customer", "自定义")
.build();
//插件式配置生成HttpClient时所需参数(超时、连接池、ssl、重试)
HCB hcb = HCB.custom()
.timeout(1000) //超时
.pool(100, 10) //启用连接池,每个路由最大创建10个链接,总连接数限制为100个
.sslpv(SSLProtocolVersion.TLSv1_2) //设置ssl版本号,默认SSLv3,也可以调用sslpv("TLSv1.2")
.ssl() //https,支持自定义ssl证书路径和密码,ssl(String keyStorePath, String keyStorepass)
.retry(5) //重试5次
;
HttpClient client = hcb.build();
Map<String, Object> map = new HashMap<String, Object>();
map.put("key1", "value1");
map.put("key2", "value2");
//插件式配置请求参数(网址、请求参数、编码、client)
HttpConfig config = HttpConfig.custom()
.headers(headers) //设置headers,不需要时则无需设置
.url(url) //设置请求的url
.map(map) //设置请求参数,没有则无需设置
.encoding("utf-8") //设置请求和返回编码,默认就是Charset.defaultCharset()
.client(client) //如果只是简单使用,无需设置,会自动获取默认的一个client对象
//.inenc("utf-8") //设置请求编码,如果请求返回一直,不需要再单独设置
//.inenc("utf-8") //设置返回编码,如果请求返回一直,不需要再单独设置
//.json("json字符串") //json方式请求的话,就不用设置map方法,当然二者可以共用。
//.context(HttpCookies.custom().getContext()) //设置cookie,用于完成携带cookie的操作
//.out(new FileOutputStream("保存地址")) //下载的话,设置这个方法,否则不要设置
//.files(new String[]{"d:/1.txt","d:/2.txt"}) //上传的话,传递文件路径,一般还需map配置,设置服务器保存路径
;
//使用方式:
String result1 = HttpClientUtil.get(config); //get请求
String result2 = HttpClientUtil.post(config); //post请求
System.out.println(result1);
System.out.println(result2);
//HttpClientUtil.down(config); //下载,需要调用config.out(fileOutputStream对象)
//HttpClientUtil.upload(config); //上传,需要调用config.files(文件路径数组)
//如果指向看是否访问正常
//String result3 = HttpClientUtil.head(config); // 返回Http协议号+状态码
//int statusCode = HttpClientUtil.status(config);//返回状态码
//[新增方法]sendAndGetResp,可以返回原生的HttpResponse对象,
//同时返回常用的几类对象:result、header、StatusLine、StatusCode
HttpResult respResult = HttpClientUtil.sendAndGetResp(config);
System.out.println("返回结果:\n"+respResult.getResult());
System.out.println("返回resp-header:"+respResult.getRespHeaders());//可以遍历
System.out.println("返回具体resp-header:"+respResult.getHeaders("Date"));
System.out.println("返回StatusLine对象:"+respResult.getStatusLine());
System.out.println("返回StatusCode:"+respResult.getStatusCode());
System.out.println("返回HttpResponse对象)(可自行处理):"+respResult.getResp());
}
log4j.properties
log4j.rootLogger=INFO,stdout,logfile
### \u628a\u65e5\u5fd7\u4fe1\u606f\u8f93\u51fa\u5230\u63a7\u5236\u53f0 ###
log4j.appender.INFO=org.apache.log4j.ConsoleAppender
log4j.appender.INFO.layout=org.apache.log4j.PatternLayout
log4j.appender.INFO.layout.ConversionPattern=[%-5p] %L method:%l - %m%n
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
#log4j.appender.stdout.layout.ConversionPattern=[%-5p] %L method:%l - %m%n
### \u628a\u65e5\u5fd7\u4fe1\u606f\u8f93\u51fa\u5230\u6587\u4ef6 jbit.log ###
#log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.logfile.File=/logs/httpclient/httputil.log
log4j.appender.logfile.DatePattern='.'yyyy-MM-dd
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
#log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n
log4j.appender.logfile.layout.ConversionPattern=[%-5p] %L-%d{yyyy-MM-dd HH:mm:ss,SSS} method:%l - %m%n
HttpClientUtil.
package com.arronlong.httpclientutil;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import com.arronlong.httpclientutil.builder.HCB;
import com.arronlong.httpclientutil.common.HttpConfig;
import com.arronlong.httpclientutil.common.HttpMethods;
import com.arronlong.httpclientutil.common.HttpResult;
import com.arronlong.httpclientutil.common.Utils;
import com.arronlong.httpclientutil.exception.HttpProcessException;
/**
* 使用HttpClient模拟发送(http/https)请求
*
* @author arron
* @version 1.0
*/
public class HttpClientUtil{
//默认采用的http协议的HttpClient对象
private static HttpClient client4HTTP;
//默认采用的https协议的HttpClient对象
private static HttpClient client4HTTPS;
static{
try {
client4HTTP = HCB.custom().build();
client4HTTPS = HCB.custom().ssl().build();
} catch (HttpProcessException e) {
Utils.errorException("创建https协议的HttpClient对象出错:{}", e);
}
}
/**
* 判定是否开启连接池、及url是http还是https <br>
* 如果已开启连接池,则自动调用build方法,从连接池中获取client对象<br>
* 否则,直接返回相应的默认client对象<br>
*
* @param config 请求参数配置
* @throws HttpProcessException http处理异常
*/
private static void create(HttpConfig config) throws HttpProcessException {
if(config.client()==null){//如果为空,设为默认client对象
if(config.url().toLowerCase().startsWith("https://")){
config.client(client4HTTPS);
}else{
config.client(client4HTTP);
}
}
}
//-----------华----丽----分----割----线--------------
//-----------华----丽----分----割----线--------------
//-----------华----丽----分----割----线--------------
/**
* 以Get方式,请求资源或服务
*
* @param client client对象
* @param url 资源地址
* @param headers 请求头信息
* @param context http上下文,用于cookie操作
* @param encoding 编码
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String get(HttpClient client, String url, Header[] headers, HttpContext context, String encoding) throws HttpProcessException {
return get(HttpConfig.custom().client(client).url(url).headers(headers).context(context).encoding(encoding));
}
/**
* 以Get方式,请求资源或服务
*
* @param config 请求参数配置
* @return 返回结果
* @throws HttpProcessException http处理异常
*/
public static String get(HttpConfig config) throws HttpProcessException {
return send(config.method(HttpMethods.GET));
}
/**
* 以Post方式,请求资源或服务
*
* @param client client对象
* @param url 资源地址
* @param headers 请求头信息
* @param parasMap 请求参数
* @param context http上下文,用于cookie操作
* @param encoding 编码
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String post(HttpClient client, String url, Header[] headers, Map<String,Object>parasMap, HttpContext context, String encoding) throws HttpProcessException {
return post(HttpConfig.custom().client(client).url(url).headers(headers).map(parasMap).context(context).encoding(encoding));
}
/**
* 以Post方式,请求资源或服务
*
* @param config 请求参数配置
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String post(HttpConfig config) throws HttpProcessException {
return send(config.method(HttpMethods.POST));
}
/**
* 以Put方式,请求资源或服务
*
* @param client client对象
* @param url 资源地址
* @param parasMap 请求参数
* @param headers 请求头信息
* @param context http上下文,用于cookie操作
* @param encoding 编码
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String put(HttpClient client, String url, Map<String,Object>parasMap,Header[] headers, HttpContext context,String encoding) throws HttpProcessException {
return put(HttpConfig.custom().client(client).url(url).headers(headers).map(parasMap).context(context).encoding(encoding));
}
/**
* 以Put方式,请求资源或服务
*
* @param config 请求参数配置
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String put(HttpConfig config) throws HttpProcessException {
return send(config.method(HttpMethods.PUT));
}
/**
* 以Delete方式,请求资源或服务
*
* @param client client对象
* @param url 资源地址
* @param headers 请求头信息
* @param context http上下文,用于cookie操作
* @param encoding 编码
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String delete(HttpClient client, String url, Header[] headers, HttpContext context,String encoding) throws HttpProcessException {
return delete(HttpConfig.custom().client(client).url(url).headers(headers).context(context).encoding(encoding));
}
/**
* 以Delete方式,请求资源或服务
*
* @param config 请求参数配置
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String delete(HttpConfig config) throws HttpProcessException {
return send(config.method(HttpMethods.DELETE));
}
/**
* 以Patch方式,请求资源或服务
*
* @param client client对象
* @param url 资源地址
* @param parasMap 请求参数
* @param headers 请求头信息
* @param context http上下文,用于cookie操作
* @param encoding 编码
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String patch(HttpClient client, String url, Map<String,Object>parasMap, Header[] headers, HttpContext context,String encoding) throws HttpProcessException {
return patch(HttpConfig.custom().client(client).url(url).headers(headers).map(parasMap).context(context).encoding(encoding));
}
/**
* 以Patch方式,请求资源或服务
*
* @param config 请求参数配置
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String patch(HttpConfig config) throws HttpProcessException {
return send(config.method(HttpMethods.PATCH));
}
/**
* 以Head方式,请求资源或服务
*
* @param client client对象
* @param url 资源地址
* @param headers 请求头信息
* @param context http上下文,用于cookie操作
* @param encoding 编码
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String head(HttpClient client, String url, Header[] headers, HttpContext context,String encoding) throws HttpProcessException {
return head(HttpConfig.custom().client(client).url(url).headers(headers).context(context).encoding(encoding));
}
/**
* 以Head方式,请求资源或服务
*
* @param config 请求参数配置
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String head(HttpConfig config) throws HttpProcessException {
return send(config.method(HttpMethods.HEAD));
}
/**
* 以Options方式,请求资源或服务
*
* @param client client对象
* @param url 资源地址
* @param headers 请求头信息
* @param context http上下文,用于cookie操作
* @param encoding 编码
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String options(HttpClient client, String url, Header[] headers, HttpContext context,String encoding) throws HttpProcessException {
return options(HttpConfig.custom().client(client).url(url).headers(headers).context(context).encoding(encoding));
}
/**
* 以Options方式,请求资源或服务
*
* @param config 请求参数配置
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String options(HttpConfig config) throws HttpProcessException {
return send(config.method(HttpMethods.OPTIONS));
}
/**
* 以Trace方式,请求资源或服务
*
* @param client client对象
* @param url 资源地址
* @param headers 请求头信息
* @param context http上下文,用于cookie操作
* @param encoding 编码
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String trace(HttpClient client, String url, Header[] headers, HttpContext context, String encoding) throws HttpProcessException {
return trace(HttpConfig.custom().client(client).url(url).headers(headers).context(context).encoding(encoding));
}
/**
* 以Trace方式,请求资源或服务
*
* @param config 请求参数配置
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String trace(HttpConfig config) throws HttpProcessException {
return send(config.method(HttpMethods.TRACE));
}
/**
* 下载文件
*
* @param client client对象
* @param url 资源地址
* @param headers 请求头信息
* @param context http上下文,用于cookie操作
* @param out 输出流
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static OutputStream down(HttpClient client, String url, Header[] headers, HttpContext context, OutputStream out) throws HttpProcessException {
return down(HttpConfig.custom().client(client).url(url).headers(headers).context(context).out(out));
}
/**
* 下载文件
*
* @param config 请求参数配置
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static OutputStream down(HttpConfig config) throws HttpProcessException {
if(config.method() == null) {
config.method(HttpMethods.GET);
}
return fmt2Stream(execute(config), config.out());
}
/**
* 上传文件
*
* @param client client对象
* @param url 资源地址
* @param headers 请求头信息
* @param context http上下文,用于cookie操作
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String upload(HttpClient client, String url, Header[] headers, HttpContext context) throws HttpProcessException {
return upload(HttpConfig.custom().client(client).url(url).headers(headers).context(context));
}
/**
* 上传文件
*
* @param config 请求参数配置
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String upload(HttpConfig config) throws HttpProcessException {
if(config.method() != HttpMethods.POST && config.method() != HttpMethods.PUT){
config.method(HttpMethods.POST);
}
return send(config);
}
/**
* 查看资源链接情况,返回状态码
*
* @param client client对象
* @param url 资源地址
* @param headers 请求头信息
* @param context http上下文,用于cookie操作
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static int status(HttpClient client, String url, Header[] headers, HttpContext context, HttpMethods method) throws HttpProcessException {
return status(HttpConfig.custom().client(client).url(url).headers(headers).context(context).method(method));
}
/**
* 查看资源链接情况,返回状态码
*
* @param config 请求参数配置
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static int status(HttpConfig config) throws HttpProcessException {
return fmt2Int(execute(config));
}
//-----------华----丽----分----割----线--------------
//-----------华----丽----分----割----线--------------
//-----------华----丽----分----割----线--------------
/**
* 请求资源或服务
*
* @param config 请求参数配置
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
public static String send(HttpConfig config) throws HttpProcessException {
return fmt2String(execute(config), config.outenc());
}
/**
* 请求资源或服务,返回HttpResult对象
*
* @param config 请求参数配置
* @return 返回HttpResult处理结果
* @throws HttpProcessException http处理异常
*/
public static HttpResult sendAndGetResp(HttpConfig config) throws HttpProcessException {
Header[] reqHeaders = config.headers();
//执行结果
HttpResponse resp = execute(config);
HttpResult result = new HttpResult(resp);
result.setResult(fmt2String(resp, config.outenc()));
result.setReqHeaders(reqHeaders);
return result;
}
/**
* 请求资源或服务
*
* @param config 请求参数配置
* @return 返回HttpResponse对象
* @throws HttpProcessException http处理异常
*/
private static HttpResponse execute(HttpConfig config) throws HttpProcessException {
create(config);//获取链接
HttpResponse resp = null;
try {
//创建请求对象
HttpRequestBase request = getRequest(config.url(), config.method());
//设置超时
request.setConfig(config.requestConfig());
//设置header信息
request.setHeaders(config.headers());
//判断是否支持设置entity(仅HttpPost、HttpPut、HttpPatch支持)
if(HttpEntityEnclosingRequestBase.class.isAssignableFrom(request.getClass())){
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
if(request.getClass()==HttpGet.class) {
//检测url中是否存在参数
//注:只有get请求,才自动截取url中的参数,post等其他方式,不再截取
config.url(Utils.checkHasParas(config.url(), nvps, config.inenc()));
}
//装填参数
HttpEntity entity = Utils.map2HttpEntity(nvps, config.map(), config.inenc());
//设置参数到请求对象中
((HttpEntityEnclosingRequestBase)request).setEntity(entity);
Utils.info("请求地址:"+config.url());
if(nvps.size()>0){
Utils.info("请求参数:"+nvps.toString());
}
if(config.json()!=null){
Utils.info("请求参数:"+config.json());
}
}else{
int idx = config.url().indexOf("?");
Utils.info("请求地址:"+config.url().substring(0, (idx>0 ? idx : config.url().length())));
if(idx>0){
Utils.info("请求参数:"+config.url().substring(idx+1));
}
}
//执行请求操作,并拿到结果(同步阻塞)
resp = (config.context()==null)?config.client().execute(request) : config.client().execute(request, config.context()) ;
if(config.isReturnRespHeaders()){
//获取所有response的header信息
config.headers(resp.getAllHeaders());
}
//获取结果实体
return resp;
} catch (IOException e) {
throw new HttpProcessException(e);
}
}
//-----------华----丽----分----割----线--------------
//-----------华----丽----分----割----线--------------
//-----------华----丽----分----割----线--------------
/**
* 转化为字符串
*
* @param resp 响应对象
* @param encoding 编码
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
private static String fmt2String(HttpResponse resp, String encoding) throws HttpProcessException {
String body = "";
try {
if (resp.getEntity() != null) {
// 按指定编码转换结果实体为String类型
body = EntityUtils.toString(resp.getEntity(), encoding);
Utils.info(body);
}else{//有可能是head请求
body =resp.getStatusLine().toString();
}
EntityUtils.consume(resp.getEntity());
} catch (IOException e) {
throw new HttpProcessException(e);
}finally{
close(resp);
}
return body;
}
/**
* 转化为数字
*
* @param resp 响应对象
* @return 返回处理结果
* @throws HttpProcessException http处理异常
*/
private static int fmt2Int(HttpResponse resp) throws HttpProcessException {
int statusCode;
try {
statusCode = resp.getStatusLine().getStatusCode();
EntityUtils.consume(resp.getEntity());
} catch (IOException e) {
throw new HttpProcessException(e);
}finally{
close(resp);
}
return statusCode;
}
/**
* 转化为流
*
* @param resp 响应对象
* @param out 输出流
* @return 返回输出流
* @throws HttpProcessException http处理异常
*/
public static OutputStream fmt2Stream(HttpResponse resp, OutputStream out) throws HttpProcessException {
try {
resp.getEntity().writeTo(out);
EntityUtils.consume(resp.getEntity());
} catch (IOException e) {
throw new HttpProcessException(e);
}finally{
close(resp);
}
return out;
}
/**
* 根据请求方法名,获取request对象
*
* @param url 资源地址
* @param method 请求方式
* @return 返回Http处理request基类
*/
private static HttpRequestBase getRequest(String url, HttpMethods method) {
HttpRequestBase request = null;
switch (method.getCode()) {
case 0:// HttpGet
request = new HttpGet(url);
break;
case 1:// HttpPost
request = new HttpPost(url);
break;
case 2:// HttpHead
request = new HttpHead(url);
break;
case 3:// HttpPut
request = new HttpPut(url);
break;
case 4:// HttpDelete
request = new HttpDelete(url);
break;
case 5:// HttpTrace
request = new HttpTrace(url);
break;
case 6:// HttpPatch
request = new HttpPatch(url);
break;
case 7:// HttpOptions
request = new HttpOptions(url);
break;
default:
request = new HttpPost(url);
break;
}
return request;
}
/**
* 尝试关闭response
*
* @param resp HttpResponse对象
*/
private static void close(HttpResponse resp) {
try {
if(resp == null) return;
//如果CloseableHttpResponse 是resp的父类,则支持关闭
if(CloseableHttpResponse.class.isAssignableFrom(resp.getClass())){
((CloseableHttpResponse)resp).close();
}
} catch (IOException e) {
Utils.exception(e);
}
}
}
使用方式很简单,可详见https://github.com/Arronlong/httpclientutil。