HttpClinet4.5学习笔记
HttpClient4.5 对java版本要求
HttpClient 要求使用java1.5 或1.5+的版本。
入门demo
下面是一段官方的示例代码,简单个get和post请求。
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://targethost/homepage");
CloseableHttpResponse response1 = httpclient.execute(httpGet);
//底层HTTP连接仍由响应对象保留
//允许响应内容直接从网络套接字流式传输。
//以确保正确释放系统资源
//用户必须从finally子句调用closeablehttpresponse close()。
//请注意,如果响应内容未完全使用底层
//无法安全地重新使用连接,将关闭并丢弃连接
//通过连接管理器。
try {
System.out.println(response1.getStatusLine());
HttpEntity entity1 = response1.getEntity();
//对响应体执行一些有用的操作
//并确保完全消耗
EntityUtils.consume(entity1);
} finally {
//释放资源
response1.close();
}
//post demo
HttpPost httpPost = new HttpPost("http://targethost/login");
List <NameValuePair> nvps = new ArrayList <NameValuePair>();
nvps.add(new BasicNameValuePair("username", "vip"));
nvps.add(new BasicNameValuePair("password", "secret"));
httpPost.setEntity(new UrlEncodedFormEntity(nvps));
CloseableHttpResponse response2 = httpclient.execute(httpPost);
try {
System.out.println(response2.getStatusLine());
HttpEntity entity2 = response2.getEntity();
//对响应体执行一些有用的操作
//并确保完全消耗
EntityUtils.consume(entity2);
} finally {
response2.close();
}
上面的demo使用起来还是相对繁琐的,那么有没有更简单的方法呢?答案是肯定的。具体的代码如下所示,通过这种方式使用httpclient我们可以不用再手动去管理资源。代码更简单方便,推荐使用这种方式。
// The fluent API relieves the user from having to deal with manual deallocation of system
// resources at the cost of having to buffer response content in memory in some cases.
Request.Get("http://targethost/homepage")
.execute().returnContent();
Request.Post("http://targethost/login")
.bodyForm(Form.form().add("username", "vip").add("password", "secret").build())
.execute().returnContent();
响应处理
此示例演示如何使用ResponseHandler处理HTTP响应。这是执行HTTP请求和处理HTTP响应的推荐方法。这种方法使调用者能够专注于消化HTTP响应的过程,并将系统资源释放的任务委托给httpclient。使用HTTP响应处理程序可以保证在所有情况下,底层HTTP连接都将自动释放回连接管理器。
public final static void main(String[] args) throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
HttpGet httpget = new HttpGet("http://httpbin.org/");
System.out.println("Executing request " + httpget.getRequestLine());
// Create a custom response handler
ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
@Override
public String handleResponse(
final HttpResponse response) throws ClientProtocolException, IOException {
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
HttpEntity entity = response.getEntity();
return entity != null ? EntityUtils.toString(entity) : null;
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
}
};
String responseBody = httpclient.execute(httpget, responseHandler);
System.out.println("----------------------------------------");
System.out.println(responseBody);
} finally {
httpclient.close();
}
}
手动连接释放
以下示例为手动处理http响应时如何释放资源,在处理完只有一定要调用close方法,释放资源。
public final static void main(String[] args) throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
HttpGet httpget = new HttpGet("http://httpbin.org/get");
System.out.println("Executing request " + httpget.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httpget);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
// Get hold of the response entity
HttpEntity entity = response.getEntity();
// If the response does not enclose an entity, there is no need
// to bother about connection release
if (entity != null) {
InputStream inStream = entity.getContent();
try {
inStream.read();
// do something useful with the response
} catch (IOException ex) {
// In case of an IOException the connection will be released
// back to the connection manager automatically
throw ex;
} finally {
// Closing the input stream will trigger connection release
inStream.close();
}
}
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
HttpClient 常用配置
使用自定义的分析器从数据流中读取http消息的方式。
HttpMessageParserFactory<HttpResponse> responseParserFactory = new DefaultHttpResponseParserFactory() {
@Override
public HttpMessageParser<HttpResponse> create(
SessionInputBuffer buffer, MessageConstraints constraints) {
LineParser lineParser = new BasicLineParser() {
@Override
public Header parseHeader(final CharArrayBuffer buffer) {
try {
return super.parseHeader(buffer);
} catch (ParseException ex) {
return new BasicHeader(buffer.toString(), null);
}
}
};
return new DefaultHttpResponseParser(
buffer, lineParser, DefaultHttpResponseFactory.INSTANCE, constraints) {
@Override
protected boolean reject(final CharArrayBuffer line, int count) {
// 尝试无限地忽略状态行前面的所有垃圾
return false;
}
};
}
};
使用自定义连接工厂自定义传出HTTP连接的初始化过程。除了标准连接配置参数之外,HTTP连接工厂还可以定义单个连接使用的消息解析器/编写器例程。
HttpMessageWriterFactory<HttpRequest> requestWriterFactory = new DefaultHttpRequestWriterFactory();
HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory = new ManagedHttpClientConnectionFactory(
requestWriterFactory, responseParserFactory);
/**
*完全初始化后的客户端HTTP连接对象可以绑定到任意网络套接字。网络套接字初始化、
*与远程地址的连接以及与
*本地地址的绑定过程由连接套接字工厂控制。
*安全连接的SSL上下文可以基于系统或特定于应用程序的属性创建。
*/
SSLContext sslcontext = SSLContexts.createSystemDefault();
// Create a registry of custom connection socket factories for supported
// protocol schemes.
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", new SSLConnectionSocketFactory(sslcontext))
.build();
// 使用自定义DNS解析程序覆盖系统DNS解析。
DnsResolver dnsResolver = new SystemDefaultDnsResolver() {
@Override
public InetAddress[] resolve(final String host) throws UnknownHostException {
if (host.equalsIgnoreCase("myhost")) {
return new InetAddress[] { InetAddress.getByAddress(new byte[] {127, 0, 0, 1}) };
} else {
return super.resolve(host);
}
}
};
使用自定义配置创建连接管理器
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(
socketFactoryRegistry, connFactory, dnsResolver);
// 创建socket配置
SocketConfig socketConfig = SocketConfig.custom()
.setTcpNoDelay(true)
.build();
//将连接管理器配置为在默认情况下或针对特定主机使用套接字配置。
connManager.setDefaultSocketConfig(socketConfig);
connManager.setSocketConfig(new HttpHost("somehost", 80), socketConfig);
// 不活动时间超过1s,执行验证连接
connManager.setValidateAfterInactivity(1000);
// 创建消息约束
MessageConstraints messageConstraints = MessageConstraints.custom()
.setMaxHeaderCount(200)
.setMaxLineLength(2000)
.build();
// 创建连接配置
ConnectionConfig connectionConfig = ConnectionConfig.custom()
.setMalformedInputAction(CodingErrorAction.IGNORE)
.setUnmappableInputAction(CodingErrorAction.IGNORE)
.setCharset(Consts.UTF_8)
.setMessageConstraints(messageConstraints)
.build();
// 设计连接管理器在特定的host下使用配置
connManager.setDefaultConnectionConfig(connectionConfig);
connManager.setConnectionConfig(new HttpHost("somehost", 80), ConnectionConfig.DEFAULT);
// 配置连接最大数、默认最大路由数以及路由限制
// that can be kept in the pool or leased by the connection manager.
connManager.setMaxTotal(100);
connManager.setDefaultMaxPerRoute(10);
connManager.setMaxPerRoute(new HttpRoute(new HttpHost("somehost", 80)), 20);
// 使用自定义cookies存储消息
CookieStore cookieStore = new BasicCookieStore();
// 如有必要,请使用自定义凭据提供程序。
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
//创建全局请求配置
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setCookieSpec(CookieSpecs.DEFAULT)
.setExpectContinueEnabled(true)
.setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST))
.setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
.build();
// 使用上述自定义项配置httpclient
CloseableHttpClient httpclient = HttpClients.custom()
.setConnectionManager(connManager)
.setDefaultCookieStore(cookieStore)
.setDefaultCredentialsProvider(credentialsProvider)
.setProxy(new HttpHost("myproxy", 8080))//代理服务器
.setDefaultRequestConfig(defaultRequestConfig)
.build();
try {
HttpGet httpget = new HttpGet("http://httpbin.org/get");
// 请求配置可以在请求级别上覆盖.
// 他们将优先于客户端的配置.
RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig)
.setSocketTimeout(5000)
.setConnectTimeout(5000)
.setConnectionRequestTimeout(5000)
.setProxy(new HttpHost("myotherproxy", 8080))
.build();
httpget.setConfig(requestConfig);
// 可以在本地自定义执行上下文
HttpClientContext context = HttpClientContext.create();
// 上下文属性本地配置优先于客户端配置
context.setCookieStore(cookieStore);
context.setCredentialsProvider(credentialsProvider);
System.out.println("executing request " + httpget.getURI());
CloseableHttpResponse response = httpclient.execute(httpget, context);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
System.out.println("----------------------------------------");
// Last executed request
context.getRequest();
// Execution route
context.getHttpRoute();
// Target auth state
context.getTargetAuthState();
// Proxy auth state
context.getProxyAuthState();
// Cookie origin
context.getCookieOrigin();
// Cookie spec used
context.getCookieSpec();
// User security token
context.getUserToken();
} finally {
response.close();
}
} finally {
httpclient.close();
}
提前终止htpp请求
接下来我们看一下如何在http请求未正常结束的情况下,提前终止请求。
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
HttpGet httpget = new HttpGet("http://httpbin.org/get");
System.out.println("Executing request " + httpget.getURI());
CloseableHttpResponse response = httpclient.execute(httpget);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
//不想读取请求响应上下文,我们可以调用.abort方法提前终止。这种场景可以用于请求超时处理
httpget.abort();
} finally {
response.close();
}
} finally {
httpclient.close();
}
HttpClient 身份验证
现在不少网站都要求输入身份验证才可以正常请求,针对需要用户身份验证的目标站点httpclient也提供了支持。
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("httpbin.org", 80),
new UsernamePasswordCredentials("user", "passwd"));
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.build();
try {
HttpGet httpget = new HttpGet("http://httpbin.org/basic-auth/user/passwd");
System.out.println("Executing request " + httpget.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httpget);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
} finally {
response.close();
}
} finally {
httpclient.close();
}
HttpClient通过代理访问站点
加入公司网络需要通过代理服务器才可以访问外网,下面我们就演示一下httpclient如何使用代理访问。
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
HttpHost target = new HttpHost("httpbin.org", 443, "https");//目标主机
HttpHost proxy = new HttpHost("127.0.0.1", 8080, "http");//代理服务器
RequestConfig config = RequestConfig.custom()
.setProxy(proxy)
.build();
HttpGet request = new HttpGet("/");
request.setConfig(config);
System.out.println("Executing request " + request.getRequestLine() + " to " + target + " via " + proxy);
CloseableHttpResponse response = httpclient.execute(target, request);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
} finally {
response.close();
}
} finally {
httpclient.close();
}
如果代理服务器需要需要身份验证:
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("localhost", 8888),
new UsernamePasswordCredentials("squid", "squid"));
credsProvider.setCredentials(
new AuthScope("httpbin.org", 80),
new UsernamePasswordCredentials("user", "passwd"));
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider).build();
try {
HttpHost target = new HttpHost("httpbin.org", 80, "http");
HttpHost proxy = new HttpHost("localhost", 8888);
RequestConfig config = RequestConfig.custom()
.setProxy(proxy)
.build();
HttpGet httpget = new HttpGet("/basic-auth/user/passwd");
httpget.setConfig(config);
System.out.println("Executing request " + httpget.getRequestLine() + " to " + target + " via " + proxy);
CloseableHttpResponse response = httpclient.execute(target, httpget);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
} finally {
response.close();
}
} finally {
httpclient.close();
}
使用chunk编码输出实体
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.out.println("File path not given");
System.exit(1);
}
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
HttpPost httppost = new HttpPost("http://httpbin.org/post");
File file = new File(args[0]);
InputStreamEntity reqEntity = new InputStreamEntity(
new FileInputStream(file), -1, ContentType.APPLICATION_OCTET_STREAM);
reqEntity.setChunked(true);
// It may be more appropriate to use FileEntity class in this particular
// instance but we are using a more generic InputStreamEntity to demonstrate
// the capability to stream out data from any arbitrary source
// FileEntity entity = new FileEntity(file, "binary/octet-stream");
httppost.setEntity(reqEntity);
System.out.println("Executing request: " + httppost.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httppost);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
使用本地上下文填充自定义属性
public final static void main(String[] args) throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
// Create a local instance of cookie store
CookieStore cookieStore = new BasicCookieStore();
// Create local HTTP context
HttpClientContext localContext = HttpClientContext.create();
// Bind custom cookie store to the local context
localContext.setCookieStore(cookieStore);
HttpGet httpget = new HttpGet("http://httpbin.org/cookies");
System.out.println("Executing request " + httpget.getRequestLine());
// Pass local context as a parameter
CloseableHttpResponse response = httpclient.execute(httpget, localContext);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
List<Cookie> cookies = cookieStore.getCookies();
for (int i = 0; i < cookies.size(); i++) {
System.out.println("Local cookie: " + cookies.get(i));
}
EntityUtils.consume(response.getEntity());
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
httpclient基于表单登陆
此示例演示如何使用httpclient执行基于表单的登录。
public static void main(String[] args) throws Exception {
BasicCookieStore cookieStore = new BasicCookieStore();
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.build();
try {
HttpGet httpget = new HttpGet("https://someportal/");
CloseableHttpResponse response1 = httpclient.execute(httpget);
try {
HttpEntity entity = response1.getEntity();
System.out.println("Login form get: " + response1.getStatusLine());
EntityUtils.consume(entity);
System.out.println("Initial set of cookies:");
List<Cookie> cookies = cookieStore.getCookies();
if (cookies.isEmpty()) {
System.out.println("None");
} else {
for (int i = 0; i < cookies.size(); i++) {
System.out.println("- " + cookies.get(i).toString());
}
}
} finally {
response1.close();
}
HttpUriRequest login = RequestBuilder.post()
.setUri(new URI("https://someportal/"))
.addParameter("IDToken1", "username")
.addParameter("IDToken2", "password")
.build();
CloseableHttpResponse response2 = httpclient.execute(login);
try {
HttpEntity entity = response2.getEntity();
System.out.println("Login form get: " + response2.getStatusLine());
EntityUtils.consume(entity);
System.out.println("Post logon cookies:");
List<Cookie> cookies = cookieStore.getCookies();
if (cookies.isEmpty()) {
System.out.println("None");
} else {
for (int i = 0; i < cookies.size(); i++) {
System.out.println("- " + cookies.get(i).toString());
}
}
} finally {
response2.close();
}
} finally {
httpclient.close();
}
}
使用多个线程来执行httpclient
执行来自多个工作线程的HTTP请求的示例。
public static void main(String[] args) throws Exception {
//使用threadsafeclientconmanager创建httpclient。
//如果多个线程将使用httpclient,则必须使用此连接管理器。
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(100);
CloseableHttpClient httpclient = HttpClients.custom()
.setConnectionManager(cm)
.build();
try {
// create an array of URIs to perform GETs on
String[] urisToGet = {
"http://hc.apache.org/",
"http://hc.apache.org/httpcomponents-core-ga/",
"http://hc.apache.org/httpcomponents-client-ga/",
};
// create a thread for each URI
GetThread[] threads = new GetThread[urisToGet.length];
for (int i = 0; i < threads.length; i++) {
HttpGet httpget = new HttpGet(urisToGet[i]);
threads[i] = new GetThread(httpclient, httpget, i + 1);
}
// start the threads
for (int j = 0; j < threads.length; j++) {
threads[j].start();
}
// join the threads
for (int j = 0; j < threads.length; j++) {
threads[j].join();
}
} finally {
httpclient.close();
}
}
/**
* A thread that performs a GET.
*/
static class GetThread extends Thread {
private final CloseableHttpClient httpClient;
private final HttpContext context;
private final HttpGet httpget;
private final int id;
public GetThread(CloseableHttpClient httpClient, HttpGet httpget, int id) {
this.httpClient = httpClient;
this.context = new BasicHttpContext();
this.httpget = httpget;
this.id = id;
}
/**
* Executes the GetMethod and prints some status information.
*/
@Override
public void run() {
try {
System.out.println(id + " - about to get something from " + httpget.getURI());
CloseableHttpResponse response = httpClient.execute(httpget, context);
try {
System.out.println(id + " - get executed");
// get the response body as an array of bytes
HttpEntity entity = response.getEntity();
if (entity != null) {
byte[] bytes = EntityUtils.toByteArray(entity);
System.out.println(id + " - " + bytes.length + " bytes read");
}
} finally {
response.close();
}
} catch (Exception e) {
System.out.println(id + " - error: " + e);
}
}
}
SSL安全连接
此示例演示如何使用自定义SSL上下文创建安全连接。
public final static void main(String[] args) throws Exception {
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(new File("my.keystore"), "nopassword".toCharArray(),
new TrustSelfSignedStrategy())
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
try {
HttpGet httpget = new HttpGet("https://httpbin.org/");
System.out.println("Executing request " + httpget.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httpget);
try {
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
``