
HttpClient是基于HttpCore实现的一个HTTP/1.1兼容的HTTP客户端,它提供了一系列可重用的客户端身份验证、HTTP状态保持、HTTP连接管理module。功能丰富的HTTPClient同时兼具出色的可扩展性和健壮性,目前已经成为了最为流行的Java HTTP客户端组件,为开发Web浏览器、Web Service客户端提供了很大的便利。
HttpClient(4.x)是Jakarta Commons HttpClient 3.x的继承,当前最新版本HttpClient 4.0-beta2。Android SDK在M5这个Milestone对HttpClient做了重大更新,开始捆绑HttpClient 4.x(当时还处于Alpha阶段)的包,而之前的M3则是捆绑了Jakarta Commons HttpClient 3.x。这一举动虽然在当时看起来太过激进,但是保证了在Android正式发布以后的API一致性。而嘀咕作为国内为博客的后起之秀,以众多好用的插件赢得了不少用户的青睐,这背后所依靠的则是它开放的API。看起来嘀咕API + HttpClient + Android的组合会非常有意思。
当然,实际上Android中的HttpClient在使用上与PC环境下并没有什么区别,我们只要记得在创建Project后给当前的Application设定Internet访问权限即可。
一般情况下,使用 HttpClient 需要以下5个步骤:
1. 创建 HttpClient 的实例
2. 创建某种连接方法的实例,在这里是最常见的是Get和Post
3. 调用第一步中创建好的HttpClient实例的execute方法,得到执行结果
4. 释放连接
5. 对得到后的内容进行处理
下面就结合代码和注释来说明
Get方法
1、创建HttpClient实例,调用DefaultHttpClient的默认构造函数,这里的DefaultHttpClient取代了HttpClient 3.x中的HttpClient,4.x中的HttpClient是作为一个接口存在。
DefaultHttpClient httpclient = new DefaultHttpClient();
2、创建HttpGet实例,把请求的URL地址传进去,比如我们想得到嘀咕的Public_Timeline,这个方法不需要身份验证。
HttpGet httpget = new HttpGet(“http://api.digu.com/statuses/public_timeline.xml?count=10&page=1”);
3、调用HttpClient实例的execute方法,对于执行结果为2XX的情况,BasicResponseHandler会自动把response body以String方式返回,对于3xx的response它会在内部自动重定向,而对于没有response body的情况,他会返回null。
ResponseHandler<String> responseHandler = new BasicResponseHandler(); String response = httpclient.execute(httpget, responseHandler);
对于execute可能抛出的IOException这个可恢复异常,请参考HttpRequestRetryHandler接口来解决
4、最后,释放httpClient资源
httpclient.getConnectionManager().shutdown();
需要身份验证的Get方法
在嘀咕API中,绝大多数是需要身份验证的,接下来我们就来看一看如何通过HttpClient调用对需要身份验证的嘀咕API。
我起初参考了HttpClient Example中的Client authentication方法来调用需要身份验证的嘀咕API,比如friends_timeline,但是无法成功,一直是ClientProtocolException和MalformedChallengeException。从Wireshark抓包的情况来分析,似乎是发出的Get请求包头中并没有包含Authentiction信息。由于网上的HOWTO基本也都是以3.x为主,我花了很多时间也没能找到这个Exmaple的问题在哪里。
好在后来看了Preemptive BASIC authentication,至少有了一个功能正常的解决办法。
与Client authentication中的做法最大的不同就在于给HttpClient设置了一个请求拦截器(Request Interceptor),当Http请求即将被执行的时候,如果发现AuthScheme为null就尝试初始化。
// Add as the first request intercepter httpclient.addRequestInterceptor(new PreemptiveAuth(), 0);
Post方法
我们以更新一个嘀咕消息为例,Post方法比较特别的地方就在于
1、我们创建的是HttpPost实例,而非HttpGet实例
HttpPost httppost = new HttpPost("http://api.digu.com/statuses/public_timeline.xml");
2、我们把需要向服务端传递的数据封装在一个ArrayList
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("content", "test digu api, 测试嘀咕API"));
httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
3、后面的execute等方法调用就和Get没有什么区别了,并且因为我们是在向服务端提交内容,所以会比较关心请求是否成功执行,用esponse.getStatusLine()就可以得到请求执行的状态。
结语
在尝试用HttpClient调用嘀咕API的过程中,WireShark帮了我很大的忙。另外大家如果想在编码前就测试一下每个API的调用方法和返回结果,curl是个简便的途径,通过-u username:password的方法,它还能够支持需要身份验证的API调用。
得到了XML数据以后,下一步就是解析数据了,关于这一点,我将在下一篇中介绍。
这个很高级
评论 by 智康博客 — 2009-05-24 @ 11:42
还没看到是虾米东东~~~
评论 by tt — 2009-05-25 @ 15:34
问下,PHPMYADMIN登录时报错1130,怎么解决?
评论 by 短歌行 — 2009-05-26 @ 11:09
我来了,这个太专业了?呼呼
问个问题,就是你地址链接后的“acccess-digu-api-through-apache-httpclient-4”是怎么实现的,我前段时间摸索了很久,无果……望指教
评论 by Estoremap — 2009-05-26 @ 12:32
@Estoremap
首先要打开“永久链接”,即permalink,这个我在你blog里留言提到过。在“设置”–>“永久链接”里面选择启用即可,至于永久链接的格式推荐“月份和名称”,即类似http://www.williamhua.com/2009/05/sample-post/ 这样,不想要月份年份信息也没有关系,这个主要还是看喜好。像我这样加上日期稍微有点多余:P 只是我不想让搜索引擎重新爬了。
另外,permalink需要服务器启用Apache的mod_rewrite模块,至于IIS怎么搞,我也不太清楚了,不过WP中文论坛里有相关的帖子
设置完以后,在写blog的时候就会在标题下方设置永久链接
评论 by William Hua — 2009-05-26 @ 13:23
@短歌行
是在你自己的NAS上吗?
基本上你看看这个post就明白了 http://home.mysql.cn/space-55789-do-blog-id-79.html
评论 by William Hua — 2009-05-26 @ 13:28
你那个example的图片我还以为是 wordpress代码折叠插件所为,点了半天不见展开。
评论 by febird — 2009-05-26 @ 16:18
@febird
哈哈,那个是wireshark的截图
评论 by William Hua — 2009-05-26 @ 17:55
@William Hua 原来是这样子的,我主要是用WLW发的日志,所以没办法了,那就算了……
评论 by Estoremap — 2009-05-31 @ 14:45
嗯Webkit也是可以直接用Curl的认证功能,不用自己再搞个SSL模块
评论 by Chocolly — 2009-05-31 @ 21:55
@Estoremap
WLW没有用过,不知道是不是支持发布时改permalink的功能,只要支持就ok
评论 by William Hua — 2009-05-31 @ 22:08
您好,我现在正在研究这方面的东西,看到您的这篇博客,很受用。现在我遇到一个问题和您的一样,在使用HttpGet的时候会发出ClientProtocolException和MalformedChallengeException异常,不知道你最后是如何处理的。可不可以看一下你的源代码呢?
还有就是如果用HttpPost的时候该如何处理呢?
我的邮箱是:xiaosena@gmail.com
急切希望收到您的回复,谢谢。
评论 by xiaosena — 2009-06-01 @ 10:40
@xiaosena
你好,
对于ClientProtocolException和MalformedChallengeException异常的问题,我后来参考了Preemptive BASIC authentication这个例子的代码,关键就是添加了一个addRequestInterceptor,代码可以看这里
http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/httpclient/src/examples/org/apache/http/examples/client/ClientPreemptiveBasicAuthentication.java
HttpPost你遇到了什么问题? 按照我上面贴的代码,最后调一个httpclient.execute(httppost); 应该就ok了
不好意思,现在代码不在身边,如果还有问题请留言。
评论 by William Hua — 2009-06-01 @ 17:11
我最近想搞个自动顶贴机,哈。
但是用到这个httpclient的post 方法,代码参照了apache例子的,我简直就是直接copy过去,
比如登陆163邮箱吧,form里写的方法是post·执行代码没有异常,不过显示操作结果的时候看到了用户名密码错误的提示,什么原因?
是不是密码的传送不能用basicNameValuePair这个类?
DefaultHttpClient httpclient = new DefaultHttpClient();
List nvps = new ArrayList();
nvps.add(new BasicNameValuePair(“userName”, “用户名”));
nvps.add(new BasicNameValuePair(“password”, “密码”));
UrlEncodedFormEntity loginEntity = new UrlEncodedFormEntity(nvps, “UTF-8″);
HttpPost httpost = new HttpPost(“https://reg.163.com/logins.jsp”);
httpost.setEntity(loginEntity);
response = httpclient.execute(httpost);
entity = response.getEntity();
System.out.println(“Login form get: ” + response.getStatusLine());
if (entity != null) {
// 显示服务器反馈内容
ResponseHandler responseHandler = new BasicResponseHandler();
String responseBody = httpclient.execute(httpost, responseHandler);
System.out.println(responseBody);
//
entity.consumeContent();
}
评论 by 毛毛 — 2009-07-30 @ 07:13
您好,我看了您的博客受益匪浅。有一个问题就是用get方法进行身份验证的时候,设置一个请求拦截器,我对这个实在是不太了解,弄了好久都调试成功。不知道楼主能不能分享一下身份验证的代码呢?谢谢。
评论 by seashell — 2009-11-06 @ 22:33
晕,你们是不是没有抓过包啊,这可都要设置header信息,还有cookie的啊,我猜那个请求拦截器没必要吧。
评论 by 卢卡斯 — 2011-05-13 @ 16:13