HttpComponents Client是现在HttpClient的新版本,是对Commons HttpClient 3.x的继承者和替代品,目前官方强烈推荐升级到新
版本,目前最新版本为HttpClient 4.3.1。好不容易对Commons HttpClient有一点眉目了,现在看HttpComponents Client完全就是一头雾水,不知所云。
HttpComponents Client中一个请求执行的简单模板。
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("http://localhost/");
CloseableHttpResponse response = httpclient.execute(httpget);
try {
<...>
} finally {
response.close();
}
HttpComponents Client对HTTP请求方法GET, HEAD, POST, PUT, DELETE, TRACE and OPTIONS提供相应的类:HttpGet,HttpHead,HttpPost,HttpPut, HttpDelete,HttpTrace,HttpOptions。
HTTP请求行包括一个方法名、请求的URI、HTTP的版本。其中HTTP URI包括a protocol scheme, host name, optional port, resource path, optional query, and optional fragment。HttpClient 提供了URIBuilder
utility class 来创建和修改请求URI。
URI uri = new URIBuilder()
.setScheme("http")
.setHost("www.google.com")
.setPath("/search")
.setParameter("q", "httpclient")
.setParameter("btnG", "Google Search")
.setParameter("aq", "f")
.setParameter("oq", "")
.build();
HttpGet httpget = new HttpGet(uri);
System.out.println(httpget.getURI());
HTTP 响应行包含协议的版本、返回状态代码、关联短语。
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, "OK");
System.out.println(response.getProtocolVersion());
System.out.println(response.getStatusLine().getStatusCode());
System.out.println(response.getStatusLine().getReasonPhrase());
System.out.println(response.getStatusLine().toString());
HTTP消息包含一个消息头,描述了消息的内容长度、类型等信息。操作消息头的方法。
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie",
"c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie",
"c2=b; path=\"/\", c3=c; domain=\"localhost\"");
Header h1 = response.getFirstHeader("Set-Cookie");
System.out.println(h1);
Header h2 = response.getLastHeader("Set-Cookie");
System.out.println(h2);
Header[] hs = response.getHeaders("Set-Cookie");
System.out.println(hs.length);
获取HTTP消息头最有效的方法是使用HeaderIterator迭代器。
HeaderIterator it = response.headerIterator("Set-Cookie");
while (it.hasNext()) {
System.out.println(it.next());
}
同时提供了方便的函数来对消息头进行操作。
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator("Set-Cookie"));
while (it.hasNext()) {
HeaderElement elem = it.nextElement();
System.out.println(elem.getName() + " = " + elem.getValue());
NameValuePair[] params = elem.getParameters();
for (int i = 0; i < params.length; i++) {
System.out.println(" " + params[i]);
}
}
HTTP消息体会携带一个与请求和响应相关的内容实体。实体是可选的。Request使用entity来封闭请求,HTTP定义了两种实体请求的方法:POST和PUT。
Response使用entity来封装一个内容实体。有例外的情况。
HTTPClient根据entity的来源区别了3类实体:
- Streamed:内容来源于流,或者动态生成。Streamed包括从HTTP Response中接收的实体。通常是不可重复的。
- Self-contained:自包含的,内容通过独立于其他链接或者实体方法获取。可重复获取使用,通常用来封闭HTTP Request。
- Wrapping:包装的,内容来源其他实体。
实体重复的意思的实体内容能够被多次读取使用。
当创建一个包含内容的请求时,或者当请求成功,响应发送结果给请求时,entity就被创建。
下面是一个StringEntity实例:
package HttpClient4.yang;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import org.apache.http.ParseException;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
public class HttpEntity {
/**
* @param args
*/
public static void main(String[] args) {
try {
StringEntity entity = new StringEntity("这一个字符串实体", "UTF-8");
//内容类型
System.out.println(entity.getContentType());
//内容的编码格式
System.out.println(entity.getContentEncoding());
//内容的长度
System.out.println(entity.getContentLength());
//把内容转成字符串
System.out.println(EntityUtils.toString(entity));
//内容转成字节数组
System.out.println(EntityUtils.toByteArray(entity).length);
//还有个直接获得流
//entity.getContent();
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
} catch (ParseException e) {
} catch (IOException e) {
}
}
}
资源的释放:
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpget = new HttpGet("http://localhost/");
CloseableHttpResponse response = httpclient.execute(httpget);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
try {
// do something useful
} finally {
instream.close();
}
}
} finally {
response.close();
}
关闭内容流和响应的区别在于:前者通过消费实体内容来保持底层连接,而后者直接关闭并丢弃连接。
消费实体的推荐方法是HttpEntity#getContent()
or HttpEntity#writeTo(OutputStream)
,当实体被容需要被多次读取的时候,最好对实体进行缓存,最简单的方法就是通过 BufferedHttpEntity来进行包装。
产生实体方法:StringEntity
, ByteArrayEntity
, InputStreamEntity
, 和FileEntity。
最初,Http被设计成一种无状态的、面向请求-响应的协议。然而,在实际使用中,我们希望能够在一些逻辑相关的请求-响应中,保持状态信息。为了使应用程序可以保持Http的持续状态,HttpClient允许http连接在特定的Http上下文中执行。如果在持续的http请求中使用了同样的上下文,那么这些请求就可以被分配到一个逻辑会话中。HTTP上下文就和一个java.util.Map<String, Object>
功能类似。它实际上就是一个任意命名的值的集合。应用程序可以在Http请求执行前填充上下文的值,也可以在请求执行完毕后检查上下文。HttpContext
可以包含任意类型的对象,因此如果在多线程中共享上下文会不安全。推荐每个线程都只包含自己的http上下文。
在Http请求执行的过程中,HttpClient会自动添加下面的属性到Http上下文中:
-
HttpConnection
的实例,表示客户端与服务器之间的连接 -
HttpHost
的实例,表示要连接的木包服务器 -
HttpRoute
的实例,表示全部的连接路由 -
HttpRequest
的实例,表示Http请求。在执行上下文中,最终的HttpRequest对象会代表http消息的状态。Http/1.0和Http/1.1都默认使用相对的uri。但是如果使用了非隧道模式的代理服务器,就会使用绝对路径的uri。 -
HttpResponse
的实例,表示Http响应 -
java.lang.Boolean
对象,表示是否请求被成功的发送给目标服务器 -
RequestConfig
对象,表示http request的配置信息 -
java.util.List<Uri>
对象,表示Http响应中的所有重定向地址
java.io.IOException
,当遇到I/O异常时抛出(socket超时,或者socket被重置);另一种是
HttpException
,表示Http失败,如Http协议使用不正确。通常认为,I/O错误时不致命、可修复的,而Http协议错误是致命了,不能自动修复的错误。