Java开源工具库使用之httpclient
HttpClient成立于2001年,是项目下的子项目,2004 年离开Commons,提升成为一个单独的 Jakarta 项目。2005 年,Jakarta 创建了项目,目标是开发的继任者。2007 年,Commons项目,也就是HttpClient项目的发源地,离开了Jakarta, 成为了1个新的顶级项目。不久之后,也离开了Jakarta, 成为一个独立的顶级项目,负责维护HttpClien
文章目录
前言
HttpClient 成立于2001年,是 Apache Jakarta Commons 项目下的子项目,2004 年离开 Commons,提升成为一个单独的 Jakarta 项目。2005 年,Jakarta 创建了 HttpComponents 项目,目标是开发 HttpClient 3.x 的继任者。2007 年,Commons 项目,也就是 HttpClient 项目的发源地,离开了 Jakarta, 成为了1个新的顶级项目。不久之后,HttpComponents 也离开了 Jakarta, 成为一个独立的顶级项目,负责维护 HttpClient 的工作。
-
HttpClient 提供了高效、最新、功能丰富的支持 HTTP 协议的客户端编程工具包,支持最新版本的 HTTP 协议。
-
HttpComponents 项目,包含 HttpClient 和 HttpCore, AsyncClient 三大模块,提供了更好的性能和更大的灵活性。
-
HttpClient 是依赖于 HttpCore 的,最新的 HttpClient 版本为 5.2
-
HttpClient 是以 3.1 版本为分隔,大版本之间用法有很多不同
-
最新文档地址:https://hc.apache.org/httpcomponents-client-5.2.x/index.html
-
旧版文档地址:https://hc.apache.org/httpclient-legacy/userguide.html
-
pom 依赖
<!-- 最新版本5 --> <dependency> <groupId>org.apache.httpcomponents.client5</groupId> <artifactId>httpclient5</artifactId> <version>5.2.1</version> </dependency> <!-- 版本4 --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> <!-- 旧版本3,07年后没更新 --> <dependency> <groupId>commons-httpclient</groupId> <artifactId>commons-httpclient</artifactId> <version>3.1</version> </dependency>
一、简单使用
1.1 get 请求
String url = "http://httpbin.org/get";
try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {
final HttpGet httpget = new HttpGet(url);
// Create a custom response handler
final HttpClientResponseHandler<String> responseHandler = response -> {
final int status = response.getCode();
if (status >= HttpStatus.SC_SUCCESS && status < HttpStatus.SC_REDIRECTION) {
final HttpEntity entity = response.getEntity();
try {
return entity != null ? EntityUtils.toString(entity) : null;
} catch (final ParseException ex) {
throw new ClientProtocolException(ex);
}
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
};
final String responseBody = httpclient.execute(httpget, responseHandler);
System.out.println(responseBody);
}
1.2 post 简单表单请求
String url = "http://httpbin.org/post";
String username = "root";
String loginPw = "";
try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {
final HttpPost httppost = new HttpPost(url);
final List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("username", username));
params.add(new BasicNameValuePair("password", loginPw));
httppost.setEntity(new UrlEncodedFormEntity(params));
// Create a custom response handler
final HttpClientResponseHandler<String> responseHandler = response -> {
final int status = response.getCode();
if (status >= HttpStatus.SC_SUCCESS && status < HttpStatus.SC_REDIRECTION) {
final HttpEntity entity = response.getEntity();
try {
return entity != null ? EntityUtils.toString(entity) : null;
} catch (final ParseException ex) {
throw new ClientProtocolException(ex);
}
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
};
final String responseBody = httpclient.execute(httppost, responseHandler);
System.out.println(responseBody);
}
1.3 表单上传文件
final HttpPost httppost = new HttpPost(url);
final MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("username", username);
builder.addTextBody("password", password);
builder.addBinaryBody("file", new File("src/test/resources/test.txt"), ContentType.APPLICATION_OCTET_STREAM, "test.txt");
final HttpEntity multipart = builder.build();
httppost.setEntity(multipart);
1.4 上传 json 数据
final HttpPost httppost = new HttpPost(url);
httppost.setHeader("Accept", "application/json");
httppost.setHeader("Content-type", "application/json");
final String json = "{\"id\":1,\"name\":\"John\"}";
final StringEntity stringEntity = new StringEntity(json);
httppost.setEntity(stringEntity);
二、高级用法
2.1 超时和重试
超时控制可以通过 RequestConfig 这个类控制
String url = "http://httpbin.org/get";
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(Timeout.ofSeconds(100L))//连接请求超时, 0为无限。默认值:3分钟。
.setResponseTimeout(Timeout.ofSeconds(600L)) // 响应超时时间,0为无限。带有消息复用的HTTP传输可能不支持响应超时
.build();
try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {
final HttpGet httpGet = new HttpGet(url);
httpGet.setConfig(requestConfig);
final HttpClientResponseHandler<String> responseHandler = response -> {
final int status = response.getCode();
if (status >= HttpStatus.SC_SUCCESS && status < HttpStatus.SC_REDIRECTION) {
final HttpEntity entity = response.getEntity();
try {
return entity != null ? EntityUtils.toString(entity) : null;
} catch (final ParseException ex) {
throw new ClientProtocolException(ex);
}
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
};
httpclient.execute(httpGet, responseHandler);
}
重试,默认重试策略为最大次数1次,重试间隔为1秒。
String url = "http://httpbin.org/get";
try (final CloseableHttpClient httpclient = HttpClients.custom()
.setRetryStrategy(new DefaultHttpRequestRetryStrategy(3, TimeValue.ofSeconds(20L)))
.build()) {
final HttpGet httpGet = new HttpGet(url);
final HttpClientResponseHandler<String> responseHandler = response -> {
final int status = response.getCode();
if (status >= HttpStatus.SC_SUCCESS && status < HttpStatus.SC_REDIRECTION) {
final HttpEntity entity = response.getEntity();
try {
return entity != null ? EntityUtils.toString(entity) : null;
} catch (final ParseException ex) {
throw new ClientProtocolException(ex);
}
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
};
httpclient.execute(httpGet, responseHandler);
}
2.2 Cookie
HttpClients.createDefault 已经内置默认 Cookie 管理器可以用来携带 Cookie 访问
String url = "http://httpbin.org/cookies/set/foo/bar";
String url2 = "http://httpbin.org/cookies";
try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {
final HttpGet httpGet = new HttpGet(url);
final HttpClientResponseHandler<String> responseHandler = response -> {
final int status = response.getCode();
if (status >= HttpStatus.SC_SUCCESS && status < HttpStatus.SC_REDIRECTION) {
final HttpEntity entity = response.getEntity();
try {
return entity != null ? EntityUtils.toString(entity) : null;
} catch (final ParseException ex) {
throw new ClientProtocolException(ex);
}
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
};
final HttpGet httpGet2 = new HttpGet(url2);
String responseBody2 = httpclient.execute(httpGet2, responseHandler);
System.out.println(responseBody2);
final String responseBody = httpclient.execute(httpGet, responseHandler);
System.out.println(responseBody);
responseBody2 = httpclient.execute(httpGet2, responseHandler);
System.out.println(responseBody2);
}
还可以访问通过本地上下文绑定 cookie,从而获取cookie 信息
String url = "http://httpbin.org/cookies/set/foo/bar";
try (final CloseableHttpClient httpclient = HttpClients.createDefault()) {
// 创建一个本地的 Cookie 存储
final CookieStore cookieStore = new BasicCookieStore();
final HttpClientContext localContext = HttpClientContext.create();
// 绑定 cookieStore 到 localContext
localContext.setCookieStore(cookieStore);
final HttpGet httpget = new HttpGet(url);
final HttpClientResponseHandler<String> responseHandler = response -> {
final int status = response.getCode();
if (status >= HttpStatus.SC_SUCCESS && status < HttpStatus.SC_REDIRECTION) {
final HttpEntity entity = response.getEntity();
try {
final List<Cookie> cookies = cookieStore.getCookies();
for (Cookie cookie : cookies) {
System.out.println("Local cookie: " + cookie);
}
return entity != null ? EntityUtils.toString(entity) : null;
} catch (final ParseException ex) {
throw new ClientProtocolException(ex);
}
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
};
String response = httpclient.execute(httpget, localContext, responseHandler);
System.out.println(response);
}
2.3 拦截器
httpclient 支持通过拦截器对请求进行一定的处理,有如下几个方法添加拦截器
- addRequestInterceptorFirst
- addRequestInterceptorLast
- addResponseInterceptorFirst
- addResponseInterceptorLast
- addExecInterceptorFirst
- addExecInterceptorLast
- addExecInterceptorBefore
- addExecInterceptorAfter
添加的拦截器可分为3种类型: request, response和 exec,对应请求,响应和执行。其中Exec执行的名字在枚举ChainElement 中,在 HttpClientBuilder 类源码中,可以发现除了 CACHING 其它都可以通过配置使用,并且枚举中的顺序也是Exec执行的顺序,其中 MAIN_TRANSPORT 执行包含 request 和 response 拦截器执行
ChainElement 定义了一组可用于构建HTTP请求处理管道的元素,每个元素都可以实现特定的功能,如添加自定义HTTP头、添加身份验证信息等。
public enum ChainElement {
REDIRECT, COMPRESS, BACK_OFF, RETRY, CACHING, PROTOCOL, CONNECT, MAIN_TRANSPORT
}
下面是一个对官方拦截器例子修改的代码
AtomicLong count = new AtomicLong();
try (final CloseableHttpClient httpclient = HttpClients.custom()
.addExecInterceptorAfter(ChainElement.PROTOCOL.name(), "custom", (request, scope, chain) -> {
request.setHeader("request-id", Long.toString(count.incrementAndGet()));
return chain.proceed(request, scope);
})
.addExecInterceptorAfter("custom", "quit3rd", ((request, scope, chain) -> {
final Header idHeader = request.getFirstHeader("request-id");
if (idHeader != null && "3".equalsIgnoreCase(idHeader.getValue())) {
final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_NOT_FOUND, "Oppsie");
response.setEntity(new StringEntity("bad luck", ContentType.TEXT_PLAIN));
return response;
} else {
return chain.proceed(request, scope);
}
}))
.addExecInterceptorBefore(ChainElement.CONNECT.name(), "AAA", (request, scope, chain) -> {
System.out.println("AAA");
return chain.proceed(request, scope);
})
.addExecInterceptorBefore("AAA", "BBB", (request, scope, chain) -> {
System.out.println("BBB");
return chain.proceed(request, scope);
})
.addExecInterceptorAfter("AAA", "CCC", (request, scope, chain) -> {
System.out.println("CCC");
return chain.proceed(request, scope);
})
.addRequestInterceptorFirst((request, entity, context) -> {
System.out.println("第一个request first现在获取:" + context.getAttribute("foo"));
})
.addRequestInterceptorFirst((request, entity, context) -> {
System.out.println("第二个request first, 现在设置name");
context.setAttribute("foo", "bar");
})
.addRequestInterceptorLast((request, entity, context) -> {
System.out.println("第一个request last现在获取:" + context.getAttribute("foo"));
})
.build()) {
for (int i = 0; i < 5; i++) {
final HttpGet httpget = new HttpGet("http://httpbin.org/get");
System.out.println("Executing request " + httpget.getMethod() + " " + httpget.getUri());
httpclient.execute(httpget, response -> {
System.out.println("----------------------------------------");
System.out.println(httpget + "->" + new StatusLine(response));
EntityUtils.consume(response.getEntity());
return null;
});
}
}
下面动图显示的是调试过程中 execChain 执行链的顺序
下面是 调试过程中的request和response 拦截器,从名字就可以看出除了Main类是自定义的拦截器,其余都是自带的,其中cookie处理也是通过拦截器实现的。
2.4 fluent API
HttpClienet 4.5 版本以上支持fluent API, 优点是代码更简洁,同时为线程安全的。
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>fluent-hc</artifactId>
<version>4.5.13</version>
</dependency>
String urlGet = "http://httpbin.org/get";
String urlPost = "http://httpbin.org/post";
String response = Request.Get(urlGet)
.addHeader("Authorization", "Bear:dw")
.execute()
.handleResponse(httpResponse -> {
int code = httpResponse.getStatusLine().getStatusCode();
if (code == HttpStatus.SC_SUCCESS) {
return org.apache.http.util.EntityUtils.toString(httpResponse.getEntity());
}
return null;
});
System.out.println(response);
String result = Request.Post(urlPost)
.bodyForm(Form.form().add("foo", "bar").build())
.execute()
.returnContent()
.asString();
System.out.println(result);
三、3.1旧版本使用
3.1 Get 请求
String url = "http://httpbin.com";
HttpClient client = new HttpClient();
GetMethod method = new GetMethod(url);
method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false));
try {
// Execute the method.
int statusCode = client.executeMethod(method);
if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: " + method.getStatusLine());
}
// Read the response body.
byte[] responseBody = method.getResponseBody();
// Deal with the response.
// Use caution: ensure correct character encoding and is not binary data
System.out.println(new String(responseBody));
} catch (HttpException e) {
System.err.println("Fatal protocol violation: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
System.err.println("Fatal transport error: " + e.getMessage());
e.printStackTrace();
} finally {
// Release the connection.
method.releaseConnection();
}
3.2 Post 请求
String url = "http://httpbin.org/post";
HttpClient client = new HttpClient();
PostMethod method = new PostMethod(url);
NameValuePair[] data = {
new NameValuePair("user", "joe"),
new NameValuePair("password", "bloggs")
};
method.setRequestBody(data);
try {
int statusCode = client.executeMethod(method);
if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: " + method.getStatusLine());
}
byte[] responseBody = method.getResponseBody();
System.out.println(new String(responseBody));
} catch (HttpException e) {
System.err.println("Fatal protocol violation: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
System.err.println("Fatal transport error: " + e.getMessage());
e.printStackTrace();
} finally {
method.releaseConnection();
}
四、异步版本使用
4.1 基本请求
final IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setSoTimeout(Timeout.ofSeconds(5))
.build();
final CloseableHttpAsyncClient client = HttpAsyncClients.custom()
.setIOReactorConfig(ioReactorConfig)
.build();
client.start();
final HttpHost target = new HttpHost("httpbin.org");
final String[] requestUris = new String[] {"/", "/ip", "/user-agent", "/headers"};
for (final String requestUri: requestUris) {
final SimpleHttpRequest request = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath(requestUri)
.build();
System.out.println("请求url:" + requestUri);
final Future<SimpleHttpResponse> future = client.execute(
SimpleRequestProducer.create(request),
SimpleResponseConsumer.create(),
new FutureCallback<SimpleHttpResponse>() {
@Override
public void completed(final SimpleHttpResponse response) {
System.out.println(requestUri + " 返回状态码:" + response.getCode() + ",返回内容:" + response.getBodyText());
}
@Override
public void failed(final Exception ex) {
System.out.println(request + "->" + ex);
}
@Override
public void cancelled() {
System.out.println(request + " cancelled");
}
});
future.get();
}
System.out.println("Shutting down");
client.close(CloseMode.GRACEFUL);
4.2 请求流水线执行
final MinimalHttpAsyncClient client = HttpAsyncClients.createMinimal(
H2Config.DEFAULT,
Http1Config.DEFAULT,
IOReactorConfig.DEFAULT,
PoolingAsyncClientConnectionManagerBuilder.create()
.setDefaultTlsConfig(TlsConfig.custom()
.setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_1)
.build())
.build());
client.start();
final HttpHost target = new HttpHost("httpbin.org");
final Future<AsyncClientEndpoint> leaseFuture = client.lease(target, null);
final AsyncClientEndpoint endpoint = leaseFuture.get(30, TimeUnit.SECONDS);
try {
final String[] requestUris = new String[] {"/", "/ip", "/user-agent", "/headers"};
final CountDownLatch latch = new CountDownLatch(requestUris.length);
for (final String requestUri: requestUris) {
final SimpleHttpRequest request = SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath(requestUri)
.build();
System.out.println("Executing request " + request);
endpoint.execute(
SimpleRequestProducer.create(request),
SimpleResponseConsumer.create(),
new FutureCallback<SimpleHttpResponse>() {
@Override
public void completed(final SimpleHttpResponse response) {
latch.countDown();
System.out.println(request + "->" + new StatusLine(response));
System.out.println(response.getBody());
}
@Override
public void failed(final Exception ex) {
latch.countDown();
System.out.println(request + "->" + ex);
}
@Override
public void cancelled() {
latch.countDown();
System.out.println(request + " cancelled");
}
});
}
latch.await();
} finally {
endpoint.releaseAndReuse();
}
System.out.println("Shutting down");
client.close(CloseMode.GRACEFUL);
参考
更多推荐
所有评论(0)