HttpClient(学习笔记)
HttpClient是Apache Jakarta Common
下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。HttpClient已经应用在很多的项目中,比如Apache Jakarta上很著名的另外两个开源项目Cactus和HTMLUnit都使用了HttpClient
一: 依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.11</version>
</dependency>
二: 简单发送无参数GET
请求
@Test
public void test1() throws Exception {
//1.创建HTTP请求客户端
CloseableHttpClient httpClient = HttpClients.createDefault();
//2.创建发送Get请求对象
HttpGet httpGet = new HttpGet("http://localhost:8080/nolanj");
//请求配置使用默认
httpGet.setConfig(RequestConfig.DEFAULT);
//3.执行请求, 获取响应对象response
CloseableHttpResponse response = httpClient.execute(httpGet);
//4.获取响应内容实体
HttpEntity responseEntity = response.getEntity();
//5.解析内容为字符串 读取完毕后会将流关闭
String string = EntityUtils.toString(responseEntity,"utf-8");
//6.释放资源
httpClient.close();
}
三: 简单发送有参数GET
请求
@Test
public void test1() throws Exception {
//构造请求参数
StringBuffer buffer = new StringBuffer();
Map<String,String> paramsMap = new HashMap<>();
paramsMap.put("name","张先生");
paramsMap.put("age","25");
paramsMap.put("sex","男");
//进行参数拼接 name=xxx&age=xxx
buffer.append("?");
for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
buffer.append(entry.getKey()).append("=")
.append(entry.getValue()).append("&");
}
//删除拼接完成后的最后一个 '&'
buffer.deleteCharAt(buffer.length()-1);
//1.创建HTTP请求客户端
CloseableHttpClient httpClient = HttpClients.createDefault();
//2.创建发送Get请求对象 //拼接参数
HttpGet httpGet = new HttpGet("http://localhost:8080/nolanj"+buffer.toString());
//请求配置使用默认
httpGet.setConfig(RequestConfig.DEFAULT);
//5.执行请求, 获取响应对象response
CloseableHttpResponse response = httpClient.execute(httpGet);
//6.获取响应内容实体
HttpEntity responseEntity = response.getEntity();
//7.解析内容为字符串 读取完毕后会将流关闭
String string = EntityUtils.toString(responseEntity,"utf-8");
//8.释放资源
httpClient.close();
}
四: 简单发送参数为表单格式POST
请求
@Test
public void test1() throws Exception {
//1.创建HTTP请求客户端
CloseableHttpClient httpClient = HttpClients.createDefault();
//2.创建发送post请求对象
HttpPost httpPost = new HttpPost("http://localhost:8080/byte");
//请求配置使用默认
httpPost.setConfig(RequestConfig.DEFAULT);
//构造请求参数
LinkedList<NameValuePair> parameters = new LinkedList<>();
parameters.add(new BasicNameValuePair("name","张韶涵"));
//3.创建表单实体请求参数对象
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters,"utf-8");
//4.添加请求参数实体
httpPost.setEntity(entity);
//5.执行请求, 获取响应对象response
CloseableHttpResponse response = httpClient.execute(httpPost);
//6.获取响应内容实体
HttpEntity responseEntity = response.getEntity();
//7.解析内容为字符串 读取完毕后会将流关闭
String string = EntityUtils.toString(responseEntity,"utf-8");
//8.释放资源
httpClient.close();
}
五: 简单发送无参数POST`请求
@Test
public void test1() throws Exception {
//1.创建HTTP请求客户端
CloseableHttpClient httpClient = HttpClients.createDefault();
//2.创建发送post请求对象
HttpPost httpPost = new HttpPost("http://localhost:8080/byte");
//请求配置使用默认
httpPost.setConfig(RequestConfig.DEFAULT);
//3.执行请求, 获取响应对象response
CloseableHttpResponse response = httpClient.execute(httpPost);
//4.获取响应内容实体
HttpEntity responseEntity = response.getEntity();
//5.解析内容为字符串 读取完毕后会将流关闭
String string = EntityUtils.toString(responseEntity,"utf-8");
//6.释放资源
httpClient.close();
}
六: 转码为String
类型发送POST
请求来传输文件
@Test
public void test1() throws Exception {
//1.创建HTTP请求客户端
HttpClient httpClient = HttpsUtils.getSSLHttpClient();
//2.创建发送post请求对象
HttpPost HttpPost = new HttpPost("http://localhost:8080/byte");
//3.设置请求配置
HttpPost.setConfig(HttpsUtils.getRequestConfig());
//将读取文件方法返回的字节数组编码为字符串
String encode = Base64.getEncoder().encodeToString(this.test02());
//4.添加请求参数实体
LinkedList<NameValuePair> parameters = new LinkedList<>();
parameters.add(new BasicNameValuePair("file",encode));
parameters.add(new BasicNameValuePair("fileName","张韶涵.jpg"));
HttpPost.setEntity(new UrlEncodedFormEntity(parameters,"utf-8"));
//5.执行请求, 获取响应对象response
HttpResponse response = httpClient.execute(HttpPost);
//6.获取响应内容实体
HttpEntity responseEntity = response.getEntity();
//7.解析内容为字符串 读取完毕后会将流关闭
String string = EntityUtils.toString(responseEntity,"utf-8");
}
/**
* 读取本地文件通过字节输出流转换成字节数组
*
* @return byte[]
* @author: zhihao
* @date: 27/2/2020
*/
public byte[] test02() throws Exception {
BufferedInputStream is = new BufferedInputStream(new FileInputStream("D:\\admin\\4.jpg"));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[1024*2];
int len = 0;
while ((len = is.read(bytes) ) != -1) {
outputStream.write(bytes,0,len);
}
outputStream.close();
byte[] resultByte = outputStream.toByteArray();
return resultByte;
}
//----------------------------------其他后端控制层------------------------------------------
@PostMapping("/byte")
public void test03(HttpServletRequest request) {
BufferedOutputStream os = null;
try {
//从参数获取文件名与文件字符串
String fileName = request.getParameter("fileName");
String file = request.getParameter("file");
os = new BufferedOutputStream(new FileOutputStream(new File("D:admin\\123\\" + fileName)));
//将文件字符串解码为字节数组
byte[] decode = Base64.getDecoder().decode(file);
os.write(decode);
} catch (IOException e) {
e.printStackTrace();
} finally {
IoUtil.close(os);
}
}
七: 发送字节数组请求来进行传输文件
//1.创建HTTP请求客户端
//2.创建发送post请求对象
//3.设置请求配置
//4.添加请求参数实体 //获取读取文件后的字节输出流的数组
HttpPost.setEntity(new ByteArrayEntity(this.test02()));
//文件名通过请求头发送 解决乱码问题
HttpPost.setHeader("fielName",URLEncoder.encode("66.jpg","utf-8"));
//5.执行请求, 获取响应对象response
//6.获取响应内容实体
//7.解析内容为字符串 读取完毕后会将流关闭
//--------------------------------其他后端控制层--------------------------------------------
@PostMapping("/byte")
public void test03(HttpServletRequest request) {
BufferedOutputStream os = null;
ServletInputStream is = null;
try {
is = request.getInputStream();
//从请求头获取文件名
String fileName = URLDecoder.decode(request.getHeader("fielName"),"utf-8");
os = new BufferedOutputStream(new FileOutputStream(new File("D:\\123\\" + fileName)));
byte[] bytes = new byte[1024*2];
int len = 0;
while ((len = is.read(bytes)) != -1) {
os.write(bytes,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
IoUtil.close(os);
IoUtil.close(is);
}
}
八:其他请求方式省略中…
进行抽取get与post请求的工具类:
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
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.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.*;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* @Author: zhihao
* @Date: 29/2/2020 下午 3:58
* @Description: HTTP请求工具类 (http走的是默认Http客户端,https走的是SSL协议)
* @Versions 1.0
**/
public class HttpsUtils {
//连接池管理器
private static PoolingHttpClientConnectionManager connMgr;
//请求配置
private static RequestConfig requestConfig;
//http请求重试处理程序
private static HttpRequestRetryHandler httpRequestRetryHandler;
//http客户端
private static HttpClient httpClient;
//最大超时时间
private static final int MAX_TIMEOUT = 10000;
//cookie存储,可选
private static BasicCookieStore basicCookieStore = new BasicCookieStore();
private static final Logger log = LoggerFactory.getLogger(HttpsUtils.class);
private HttpsUtils() {}
static {
//初始化
init();
}
/**
* 发送 GET请求,不带参数
*
* @param url 地址
* @return java.lang.String 结果字符串,自行json解析
*/
public static String sendGetRequest(String url) {
return sendGetRequest(url, new HashMap<String, Object>());
}
/**
* 发送GET带参数请求 ,K-V形式
*
* @param url
* @param params
* @return java.lang.String 结果字符串,自行json解析
*/
public static String sendGetRequest(String url, Map<String, Object> params) {
StringBuffer param = new StringBuffer();
//先添加请求链接
param.append(url);
int i = 0;
for (String key : params.keySet()) {
if (i == 0) {
param.append("?");
} else {
param.append("&");
}
param.append(key).append("=").append(params.get(key));
i++;
}
String resultString = null;
//如果是发送https请求使用安全ssl构建
if (param.toString().startsWith("https")) {
httpClient = getSSLHttpClient();
} else {
httpClient = HttpClients.createDefault();
}
try {
HttpGet httpGet = new HttpGet(param.toString());
httpGet.setConfig(requestConfig);
HttpResponse response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
resultString = EntityUtils.toString(entity);
} catch (IOException e) {
log.error("请求异常: "+getErrorString(e));
}
return resultString;
}
/**
* 发送 POST 请求(HTTP),不带输入数据
*
* @param apiUrl 地址
* @return java.lang.String 结果字符串,自行json解析
* @date: 27/2/2020
*/
public static String sendPostRequest(String apiUrl) {
return sendPostRequest(apiUrl, new HashMap<String, Object>());
}
/**
* 发送POST请求,K-V形式 表单形式
*
* @param apiUrl API接口URL
* @param params 参数map
* @return java.lang.String 结果字符串,自行json解析
*/
public static String sendPostRequest(String apiUrl, Map<String, Object> params) {
if (apiUrl.startsWith("https")) {
httpClient = getSSLHttpClient();
} else {
httpClient = HttpClients.createDefault();
}
String resultString = null;
HttpPost httpPost = new HttpPost(apiUrl);
try {
httpPost.setConfig(requestConfig);
//构造请求参数
List<NameValuePair> pairList = new ArrayList<>(params.size());
for (Map.Entry<String, Object> entry : params.entrySet()) {
NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry.getValue().toString());
pairList.add(pair);
}
httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("UTF-8")));
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
resultString = EntityUtils.toString(entity, "UTF-8");
} catch (IOException e) {
log.error("请求异常: "+getErrorString(e));
}
return resultString;
}
/**
* 发送json方式POST请求
*
* @param json json对象
* @param apiUrl 地址
* @return java.lang.String 结果字符串,自行json解析
*/
public static String sendJsonPostRequest(String apiUrl, Object json) {
if (apiUrl.startsWith("https")) {
httpClient = getSSLHttpClient();
} else {
httpClient = HttpClients.createDefault();
}
String resultString = null;
HttpPost httpPost = new HttpPost(apiUrl);
try {
httpPost.setConfig(requestConfig);
StringEntity stringEntity = new StringEntity(json.toString(), "UTF-8");// 解决中文乱码问题
//json请求
httpPost.addHeader("content-type", "application/json;charset=UTF-8");
httpPost.setEntity(stringEntity);
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
resultString = EntityUtils.toString(entity, "UTF-8");
} catch (IOException e) {
log.error("请求异常: "+getErrorString(e));
}
return resultString;
}
/**
* <p>
* 获取HttpClient客户端进行自定义发送请求(例如发送带cookie请求),
* 注意: 由于使用的是共享的cookie集合, 如果请求过多的不同网站,cookie集合可能会非常大,
* 并且如果使用过添加cookie方法,那么此工具类获取的getSSLHttpClient请求都会带上所有cookie发送,
* 并由于配置了DefaultCookieStore(默认cookie存储),
* 所有getSSLHttpClient请求都会带上响应的cookie进行下一次请求!
* </p>
*
* @param
* @return org.apache.http.client.HttpClient
* @author: zhihao
* @date: 29/2/2020
* {@link #}
*/
public static HttpClient getSSLHttpClient() {
// basicCookieStore = new BasicCookieStore(); //每次请求都是新的cookie
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(createSSLConnSocketFactory())
.setConnectionManager(connMgr)
.setDefaultRequestConfig(requestConfig)
.setRetryHandler(httpRequestRetryHandler)
//可选配置,如不设置下面的添加,获取,清空cookie将无效
.setDefaultCookieStore(basicCookieStore)
.build();
//清理过期的cookie节省资源
basicCookieStore.clearExpired(new Date());
return httpClient;
}
/**
* 添加cookie
*
* @param domain 域名 例如:发送的请求是 www.baidu.com
* @param minute 过期时间
* @param path cookie范围
* @param key
* @param value
* @return boolean
* @author: zhihao
* @date: 29/2/2020
*/
public static boolean addCookie(String domain, Integer minute,
String path, String key, String value) throws UnsupportedEncodingException {
//对value进行编码解决中文乱码
value = URLEncoder.encode(value, "utf-8");
BasicClientCookie cookie = new BasicClientCookie(key, value);
cookie.setDomain(domain);
cookie.setExpiryDate(new Date(System.currentTimeMillis() + 60 * minute * 1000));
cookie.setPath(path);
//添加进cookieStore静态变量中 httpClient使用的是静态变量中的cookieStore,修改对象里面的值相当修改httpClient里面的cookieStore
basicCookieStore.addCookie(cookie);
return true;
}
/**
* <p>
* 进行清空cookie
* </p>
*
* @author: zhihao
* @date: 29/2/2020 DoNotShare
*/
public static void ClearBasicCookieStore() {
basicCookieStore.clear();
}
/**
* <p>
* 获取响应后的cookie
* 使用在执行发送请求execute()后才能获取响应的cookie值
* 使用在之前是获取发送cookie的数据
* </p>
*
* @param
* @return java.util.List<org.apache.http.cookie.Cookie>
* @author: zhihao
* @date: 29/2/2020
*/
public static List<Cookie> getResponseCookie() {
return basicCookieStore.getCookies();
}
/**
* 获取请求配置
*
* @return org.apache.http.client.config.RequestConfig
* @author: zhihao
* @date: 27/2/2020
*/
public static RequestConfig getRequestConfig() {
return requestConfig;
}
/**
* 创建SSL安全连接
*
* @return
*/
private static SSLConnectionSocketFactory createSSLConnSocketFactory() {
SSLConnectionSocketFactory sslsf = null;
try {
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
// 信任所有
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
}).build();
sslsf = new SSLConnectionSocketFactory(sslContext, new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
} catch (GeneralSecurityException e) {
log.error(e.getMessage());
throw new RuntimeException(e.getMessage());
}
return sslsf;
}
/**
* 初始化连接参数
*
* @return void
* @date: 27/2/2020
*/
private static void init() {
// 设置连接池
connMgr = new PoolingHttpClientConnectionManager();
// 设置连接池大小
connMgr.setMaxTotal(100);
connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal());
// Validate connections after 1 sec of inactivity
connMgr.setValidateAfterInactivity(1000);
RequestConfig.Builder configBuilder = RequestConfig.custom();
// 设置连接超时
configBuilder.setConnectTimeout(MAX_TIMEOUT);
// 设置读取超时
configBuilder.setSocketTimeout(MAX_TIMEOUT);
// 设置从连接池获取连接实例的超时
configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT);
//设置Cookie规范
configBuilder.setCookieSpec(CookieSpecs.STANDARD_STRICT);
requestConfig = configBuilder.build();
//http请求重试处理程序
httpRequestRetryHandler = new HttpRequestRetryHandler() {
@Override
public boolean retryRequest(IOException exception,
int executionCount, HttpContext context) {
// 如果已经重试了3次,就放弃
if (executionCount >= 3) {
log.error("----链接已经重连了3次未能成功,将放弃->>");
return false;
}
// 如果服务器丢掉了连接,那么就重试
if (exception instanceof NoHttpResponseException) {
return true;
}
if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常
log.error("----不要重试SSL握手异常->>");
return false;
}
if (exception instanceof InterruptedIOException) {// 超时
log.error("----超时异常->>");
return false;
}
if (exception instanceof UnknownHostException) {// 目标服务器不可达
log.error("----目标服务器不可达异常->>");
return false;
}
if (exception instanceof ConnectTimeoutException) {// 连接被拒绝
log.error("----连接被拒绝异常->>");
return false;
}
if (exception instanceof SSLException) {// SSL握手异常
log.error("----SSL握手异常->>");
return false;
}
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpRequest request = clientContext.getRequest();
if (!(request instanceof HttpEntityEnclosingRequest)) {
return true;
}
return false;
}
};
}
/**
* 将异常转换为字符串
*
* @param e 异常
* @return java.lang.String
* @author: zhihao
* @date: 1/3/2020
*/
private static String getErrorString(Throwable e) {
if (null == e){
return null;
}
StringBuilder builder = new StringBuilder();
builder.append(e.getMessage());
StackTraceElement[] stackTrace = e.getStackTrace();
for (StackTraceElement traceElement : stackTrace) {
builder.append("\n");
builder.append(traceElement.toString());
}
return builder.toString();
}
}
扩展资料:
Request请求对象不会继承客户端级别的请求配置,所以在自定义Request的时候,需要将客户端的默认配置设置过去:HttpPost.setConfig(HttpsUtils.getRequestConfig());
-----------------------------------------------------------------------------
备忘录:
获取响应状态码: response.getStatusLine().getStatusCode()
获取响应流: InputStream inputStream = response.getEntity().getContent();
禁止过多无用的日记信息:
如果未安装Log4j库,则HttpClient(以及JWebUnit)将使用logback。 在这种情况下,创建或编辑logback.xml以包括:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<!-- 定义有颜色的日记输出格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="%red(%d{yyyy-MM-dd HH:mm:ss.SSS}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger) --- %cyan(%msg%n)"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 包括下面3个-->
<logger name="org.apache" level="ERROR"/>
<logger name="org.apache.http.wire" level="ERROR"/>
<logger name="org.apache.http.headers" level="ERROR"/>
<root level="info">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>