HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。HttpClient已经应用在很多的项目中,比如Apache Jakarta上很著名的另外两个开源项目Cactus和HTMLUnit都使用了HttpClient。
目前最新的版本是4.5,支持HTTP 1.0/HTTP 1.1协议。正在开发中的5.0版本(还在Beta阶段)支持HTTP/2协议。
1. 特性
- 基于标准、纯净的java语言。实现了Http1.0和Http1.1
- 以可扩展的面向对象的结构实现了Http全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)。
- 支持HTTPS协议。
- 通过Http代理建立透明的连接。
- 利用CONNECT方法通过Http代理建立隧道的https连接。
- Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos认证方案。
- 插件式的自定义认证方案。
- 便携可靠的套接字工厂使它更容易的使用第三方解决方案。
- 连接管理器支持多线程应用。支持设置最大连接数,同时支持设置每个主机的最大连接数,发现并关闭过期的连接。
- 自动处理Set-Cookie中的Cookie。
- 插件式的自定义Cookie策略。
- Request的输出流可以避免流中内容直接缓冲到socket服务器。
- Response的输入流可以有效的从socket服务器直接读取相应内容。
- 在http1.0和http1.1中利用KeepAlive保持持久连接。
- 直接获取服务器发送的response code和 headers。
- 设置连接超时的能力。
- 实验性的支持http1.1 response caching。
- 源代码基于Apache License 可免费获取。
2. 使用方法
下面以一个简单的例子来说明一下HttpClient的使用方法。
2.1 pom
1 <dependency>
2 <groupId>org.apache.httpcomponents</groupId>
3 <artifactId>httpclient</artifactId>
4 <version>4.5.4</version>
5 </dependency>
首先引入相关的Maven库。
2.2 代码
1String url = "http://www.baidu.com";
2// 1. 使用默认的配置的httpclient
3CloseableHttpClient client = HttpClients.createDefault();
4// 2. 使用GET方法
5HttpGet httpGet = new HttpGet(url);
6InputStream inputStream = null;
7CloseableHttpResponse response = null;
8try {
9 //3.执行请求,获取响应
10 response = client.execute(httpGet);
11 //看请求是否成功,这儿打印的是http状态码
12 System.out.println(response.getStatusLine().getStatusCode());
13
14 //4.获取响应的实体内容,就是我们所要抓取得网页内容
15 HttpEntity entity = response.getEntity();
16
17 //5.将其打印到控制台上面
18 //方法一:使用EntityUtils
19 if (entity != null) {
20 System.out.println(EntityUtils.toString(entity, "utf-8"));
21 }
22 EntityUtils.consume(entity);
23
24 //方法二:使用inputStream(不能同时使用)
25 if (entity != null) {
26 inputStream = entity.getContent();
27
28 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
29 String line = "";
30 while ((line = bufferedReader.readLine()) != null) {
31 System.out.println(line);
32
33 }
34 }
35
36} catch (ClientProtocolException e) {
37 e.printStackTrace();
38} catch (IOException e) {
39 e.printStackTrace();
40} finally {
41 if (inputStream != null) {
42 try {
43 inputStream.close();
44 } catch (IOException e) {
45 e.printStackTrace();
46 }
47 }
48 if (response != null) {
49 try {
50 response.close();
51 } catch (IOException e) {
52 e.printStackTrace();
53 }
54 }
55}
这段代码的用途是使用GET方法获取百度首页,并打印出来。
2.3 代码分析
使用HttpClient发送请求、接收响应很简单,一般需要如下几步即可。
-
创建HttpClient对象。 例子中使用的是
org.apache.http.impl.client.CloseableHtttpClient
,它是HttpClient接口的一个实例,创建该对象的最简单方法是:CloseableHttpClient client = HttpClients.createDefault();
。 HttpClients是创建CloseableHttpClient的工厂,采用默认的配置来创建实例,一般情况下我们就用这个默认实例就可以了。以后我们会讨论如何自定义这个对象。 -
创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。 其他的类型还有:HttpHead, HttpPut, HttpDelete, HttpTrace, HttpOptions等。分别对应不同的Http请求方法:
方法 | 描述 | 是否包含主体 |
---|---|---|
GET | 从服务器获取一份文档 | 否 |
HEAD | 只从服务器获取文档的首部 | 否 |
POST | 向服务器发送需要处理的数据 | 是 |
PUT | 将请求的主体部分存储在服务器上 | 是 |
TRACE | 对可能经过代理服务器传送到服务器上去的报文进行追踪 | 否 |
OPTIONS | 决定可以在服务器上执行哪些方法 | 否 |
DELETE | 从服务器上删除一份文档 | 否 |
-
如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。
-
调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个CloseableHttpResponse。
-
调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。对getEntity获取的结果,有两种处理方法: 方法一:使用EntityUtils工具类来处理。该类是官方提供的一个处理实体的工具类,toSting方法将返回的实体转换为字符串,但是官网不建议使用这个,除非响应实体从一个可信HTTP服务器发起和已知是有限长度的。 方法二:使用InputStream来读取。因为httpEntity.getContent方法返回的就是InputStream类型。这种方法是官网推荐的方式,需要记得要自己释放底层资源。
-
释放连接。无论执行方法是否成功,都必须释放连接 如果使用EntityUtils来处理返回值,则可以使用
EntityUtils.consume(entity)
来释放资源。
如果使用InputStream来处理返回值,则要关闭inputStream然后关闭response对象就行了。
1if (inputStream != null) {
2 try {
3 inputStream.close();
4 } catch (IOException e) {
5 e.printStackTrace();
6 }
7}
8if (response != null) {
9 try {
10 response.close();
11 } catch (IOException e) {
12 e.printStackTrace();
13 }
14}
2.4 关于资源释放
使用EntityUtils#consume
这个方法会自动关闭底层的inputStream。如果不需要读取全部的实体,则可以直接关闭response,来种植对inputStream的读取。注意:关闭response会自动关闭对应的inputStream。
1CloseableHttpClient httpclient = HttpClients.createDefault();
2HttpGet httpget = new HttpGet("http://localhost/");
3CloseableHttpResponse response = httpclient.execute(httpget);
4try {
5 HttpEntity entity = response.getEntity();
6 if (entity != null) {
7 InputStream instream = entity.getContent();
8 int byteOne = instream.read();
9 int byteTwo = instream.read();
10 // Do not need the rest
11 }
12} finally {
13 response.close(); // 直接关闭response
14}
不推荐使用EntityUtils#toString方法,如果确实需要,最好先自己判断长度:entity.getContentLength()
。这是因为toString方法默认最大的长度是Integer.MAX_VALUE(0x7fffffff)。这个长度太大了,很容易导致缓冲区溢出。
如果希望重复使用Entity,可以使用:new BufferedHttpEntity(entity)
创建一个新对象缓存起来。
2.5 Entity
用于生成请求的Entity的类主要有四种:StringEntity, ByteArrayEntity, InputStreamEntity和FileEntity。 例子:FileEntity:
1File file = new File("somefile.txt");
2FileEntity entity = new FileEntity(file,
3 ContentType.create("text/plain", "UTF-8"));
4HttpPost httppost = new HttpPost("http://localhost/action.do");
5httppost.setEntity(entity);
例子:
1List<NameValuePair> formparams = new ArrayList<NameValuePair>();
2formparams.add(new BasicNameValuePair("param1", "value1"));
3formparams.add(new BasicNameValuePair("param2", "value2"));
4UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
5HttpPost httppost = new HttpPost("http://localhost/handler.do");
6httppost.setEntity(entity);
3. 实例代码
3.1 普通GET请求
1// 创建Httpclient对象
2CloseableHttpClient httpclient = HttpClients.createDefault();
3// 创建http GET请求
4HttpGet httpGet = new HttpGet("http://www.baidu.com/s?wd=java");
5CloseableHttpResponse response = null;
6try {
7 // 执行请求
8 response = httpclient.execute(httpGet);
9 // 判断返回状态是否为200
10 if (response.getStatusLine().getStatusCode() == 200) {
11 ...
12 }
13}
3.2 带有参数的Get请求
1// 定义请求的参数
2URI uri = new URIBuilder("http://www.baidu.com/s").setParameter("wd", "java").build();
3// 创建http GET请求
4HttpGet httpGet = new HttpGet(uri);
3.3 普通的POST请求
1// 创建http POST请求
2HttpPost httpPost = new HttpPost("http://www.oschina.net/");
3 // 伪装浏览器请求
4httpPost.setHeader("User-Agent",
5 "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36");
6// 执行请求
7response = httpclient.execute(httpPost);
3.4 带有参数的Post请求
1// 创建http POST请求
2HttpPost httpPost = new HttpPost("http://www.oschina.net/search");
3
4// 设置2个post参数,一个是scope、一个是q
5List<NameValuePair> parameters = new ArrayList<NameValuePair>();
6parameters.add(new BasicNameValuePair("scope", "project"));
7parameters.add(new BasicNameValuePair("q", "java"));
8// 构造一个form表单式的实体
9UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters);
10// 将请求实体设置到httpPost对象中
11httpPost.setEntity(formEntity);
12// 伪装浏览器请求
13httpPost.setHeader(
14 "User-Agent",
15 "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36");
16
17// 执行请求
18response = httpclient.execute(httpPost);
3.5 配置超时时间等
1// 创建http GET请求
2HttpGet httpGet = new HttpGet("http://www.baidu.com/");
3
4// 构建请求配置信息
5RequestConfig config = RequestConfig.custom()
6 .setConnectTimeout(1000) // 创建连接的最长时间
7 .setConnectionRequestTimeout(500) // 从连接池中获取到连接的最长时间
8 .setSocketTimeout(10 * 1000) // 数据传输的最长时间
9 .setStaleConnectionCheckEnabled(true) // 提交请求前测试连接是否可用
10 .build();
11// 设置请求配置信息
12httpGet.setConfig(config);
几个函数的含义:
- setConnectionRequestTimeout 从链接池获取一个连接的最长等待时间;
- setConnectTimeout 创建链接的最长等待时间;
- setSocketTimeout 获取数据的最长等待时间;
- setStaleConnectionCheckEnabled 检查链接状态是否可用。这个选项会花费最长30ms时间已确定一个连接是否可用。对性能要求高的应用不要开启这个选项。