HttpClient程序包是一个实现了 HTTP 协议的客户端编程工具包,要想熟练的掌握它,必须熟悉 HTTP协议。一个最简单的调用如下:
01.
import
java.io.IOException;
02.
import
org.apache.http.HttpResponse;
03.
import
org.apache.http.client.ClientProtocolException;
04.
import
org.apache.http.client.HttpClient;
05.
import
org.apache.http.client.methods.HttpGet;
06.
import
org.apache.http.client.methods.HttpUriRequest;
07.
import
org.apache.http.impl.client.DefaultHttpClient;
08.
09.
public
class
Test {
10.
public
static
void
main(String[] args) {
11.
12.
// 核心应用类
13.
HttpClient httpClient =
new
DefaultHttpClient();
14.
15.
// HTTP请求
16.
HttpUriRequest request =
17.
new
HttpGet(
"http://localhost/index.html"
);
18.
19.
// 打印请求信息
20.
System.out.println(request.getRequestLine());
21.
try
{
22.
// 发送请求,返回响应
23.
HttpResponse response = httpClient.execute(request);
24.
25.
// 打印响应信息
26.
System.out.println(response.getStatusLine());
27.
}
catch
(ClientProtocolException e) {
28.
// 协议错误
29.
e.printStackTrace();
30.
}
catch
(IOException e) {
31.
// 网络异常
32.
e.printStackTrace();
33.
}
34.
}
35.
}
如果HTTP服务器正常并且存在相应的服务,则上例会打印出两行结果:
GET http://localhost/index.html HTTP/1.1
HTTP/1.1 200 OK
核心对象httpClient的调用非常直观,其execute方法传入一个request对象,返回一个response对象。使用httpClient发出HTTP请求时,系统可能抛出两种异常,分别是ClientProtocolException和IOException。第一种异常的发生通常是协议错误导致,如在构造HttpGet对象时传入的协议不对(例如不小心将”http”写成”htp”),或者服务器端返回的内容不符合HTTP协议要求等;第二种异常一般是由于网络原因引起的异常,如HTTP服务器未启动等。
从实际应用的角度看,HTTP协议由两大部分组成:HTTP请求和HTTP响应。那么HttpClient程序包是如何实现HTTP客户端应用的呢?实现过程中需要注意哪些问题呢?
HTTP请求
HTTP 1.1由以下几种请求组成:GET, HEAD, POST, PUT, DELETE, TRACE and OPTIONS, 程序包中分别用HttpGet, HttpHead, HttpPost, HttpPut, HttpDelete, HttpTrace, and HttpOptions 这几个类创建请求。所有的这些类均实现了HttpUriRequest接口,故可以作为execute的执行参数使用。
所有请求中最常用的是GET与POST两种请求,与创建GET请求的方法相同,可以用如下方法创建一个POST请求:
1.
HttpUriRequest request =
new
HttpPost(
2.
"http://localhost/index.html"
);
HTTP请求格式告诉我们,有两个位置或者说两种方式可以为request提供参数:request-line方式与request-body方式。
request-line
request-line方式是指在请求行上通过URI直接提供参数。
(1)
我们可以在生成request对象时提供带参数的URI,如:
1.
HttpUriRequest request =
new
HttpGet(
2.
"http://localhost/index.html?param1=value1&;param2=value2"
);
(2)
另外,HttpClient程序包为我们提供了URIUtils工具类,可以通过它生成带参数的URI,如:
1.
URI uri = URIUtils.createURI(
"http"
,
"localhost"
, -
1
,
"/index.html"
,
2.
"param1=value1¶m2=value2"
,
null
);
3.
HttpUriRequest request =
new
HttpGet(uri);
4.
System.out.println(request.getURI());
上例的打印结果如下:
http://localhost/index.html?param1=value1¶m2=value2
(3)
需要注意的是,如果参数中含有中文,需将参数进行URLEncoding处理,如:
1.
String param =
"param1="
+ URLEncoder.encode(
"中国"
,
"UTF-8"
) +
"¶m2=value2"
;
2.
URI uri = URIUtils.createURI(
"http"
,
"localhost"
,
8080
,
3.
"/sshsky/index.html"
, param,
null
);
4.
System.out.println(uri);
上例的打印结果如下:
http://localhost/index.html?param1=%E4%B8%AD%E5%9B%BD¶m2=value2
(4)
对于参数的URLEncoding处理,HttpClient程序包为我们准备了另一个工具类:URLEncodedUtils。通过它,我们可以直观的(但是比较复杂)生成URI,如:
1.
List<NAMEVALUEPAIR> params =
new
ArrayList<NAMEVALUEPAIR>();
2.
params.add(
new
BasicNameValuePair(
"param1"
,
"中国"
));
3.
params.add(
new
BasicNameValuePair(
"param2"
,
"value2"
));
4.
String param = URLEncodedUtils.format(params,
"UTF-8"
);
5.
URI uri = URIUtils.createURI(
"http"
,
"localhost"
,
8080
,
6.
"/sshsky/index.html"
, param,
null
);
7.
System.out.println(uri);
上例的打印结果如下:
http://localhost/index.html?param1=%E4%B8%AD%E5%9B%BD¶m2=value2
request-body
与request-line方式不同,request-body方式是在request-body中提供参数,此方式只能用于POST请求。在HttpClient程序包中有两个类可以完成此项工作,它们分别是UrlEncodedFormEntity类与MultipartEntity类。这两个类均实现了HttpEntity接口。
(1)
使用最多的是UrlEncodedFormEntity类。通过该类创建的对象可以模拟传统的HTML表单传送POST请求中的参数。如下面的表单:
1.
<
form
action
=
"http://localhost/index.html"
method
=
"POST"
>
2.
<
input
type
=
"text"
name
=
"param1"
value
=
"中国"
/>
3.
<
input
type
=
"text"
name
=
"param2"
value
=
"value2"
/>
4.
<
inupt
type
=
"submit"
value
=
"submit"
/>
5.
</
form
>
我们可以用下面的代码实现:
1.
List<NAMEVALUEPAIR> formParams =
new
ArrayList<NAMEVALUEPAIR>();
2.
formParams.add(
new
BasicNameValuePair(
"param1"
,
"中国"
));
3.
formParams.add(
new
BasicNameValuePair(
"param2"
,
"value2"
));
4.
HttpEntity entity =
new
UrlEncodedFormEntity(formParams,
"UTF-8"
);
5.
6.
HttpPost request =
new
HttpPost(“http:
//localhost/index.html”);
7.
request.setEntity(entity);
当然,如果想查看HTTP数据格式,可以通过HttpEntity对象的各种方法取得。如:
01.
List<NAMEVALUEPAIR> formParams =
new
ArrayList<NAMEVALUEPAIR>();
02.
formParams.add(
new
BasicNameValuePair(
"param1"
,
"中国"
));
03.
formParams.add(
new
BasicNameValuePair(
"param2"
,
"value2"
));
04.
UrlEncodedFormEntity entity =
new
UrlEncodedFormEntity(formParams,
"UTF-8"
);
05.
06.
System.out.println(entity.getContentType());
07.
System.out.println(entity.getContentLength());
08.
System.out.println(EntityUtils.getContentCharSet(entity));
09.
System.out.println(EntityUtils.toString(entity));
上例的打印结果如下:
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
39
UTF-8
param1=%E4%B8%AD%E5%9B%BD¶m2=value2
(2)
除了传统的application/x-www-form-urlencoded表单,我们另一个经常用到的是上传文件用的表单,这种表单的类型为multipart/form-data。在HttpClient程序扩展包(HttpMime)中专门有一个类与之对应,那就是MultipartEntity类。此类同样实现了HttpEntity接口。如下面的表单:
1.
<
form
action
=
"http://localhost/index.html"
method
=
"POST"
2.
enctype
=
"multipart/form-data"
>
3.
<
input
type
=
"text"
name
=
"param1"
value
=
"中国"
/>
4.
<
input
type
=
"text"
name
=
"param2"
value
=
"value2"
/>
5.
<
input
type
=
"file"
name
=
"param3"
/>
6.
<
inupt
type
=
"submit"
value
=
"submit"
/>
7.
</
form
>
我们可以用下面的代码实现:
1.
MultipartEntity entity =
new
MultipartEntity();
2.
entity.addPart(
"param1"
,
new
StringBody(
"中国"
, Charset.forName(
"UTF-8"
)));
3.
entity.addPart(
"param2"
,
new
StringBody(
"value2"
, Charset.forName(
"UTF-8"
)));
4.
entity.addPart(
"param3"
,
new
FileBody(
new
File(
"C:\\1.txt"
)));
5.
6.
HttpPost request =
new
HttpPost(“http:
//localhost/index.html”);
7.
request.setEntity(entity);
HTTP响应
HttpClient程序包对于HTTP响应的处理较之HTTP请求来说是简单多了,其过程同样使用了HttpEntity接口。我们可以从HttpEntity对象中取出数据流(InputStream),该数据流就是服务器返回的响应数据。需要注意的是,HttpClient程序包不负责解析数据流中的内容。如:
01.
HttpUriRequest request = ...;
02.
HttpResponse response = httpClient.execute(request);
03.
04.
// 从response中取出HttpEntity对象
05.
HttpEntity entity = response.getEntity();
06.
07.
// 查看entity的各种指标
08.
System.out.println(entity.getContentType());
09.
System.out.println(entity.getContentLength());
10.
System.out.println(EntityUtils.getContentCharSet(entity));
11.
12.
// 取出服务器返回的数据流
13.
InputStream stream = entity.getContent();
14.
15.
// 以任意方式操作数据流stream
16.
// 调用方式 略