httpclient/Apache HttpComponents
HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。
二、特性
1. 基于标准、纯净的java语言。实现了Http1.0和Http1.1
2. 以可扩展的面向对象的结构实现了Http全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)。
3. 支持HTTPS协议。
4. 通过Http代理建立透明的连接。
5. 利用CONNECT方法通过Http代理建立隧道的https连接。
6. Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos认证方案。
7. 插件式的自定义认证方案。
8. 便携可靠的套接字工厂使它更容易的使用第三方解决方案。
9. 连接管理器支持多线程应用。支持设置最大连接数,同时支持设置每个主机的最大连接数,发现并关闭过期的连接。
10. 自动处理Set-Cookie中的Cookie。
11. 插件式的自定义Cookie策略。
12. Request的输出流可以避免流中内容直接缓冲到socket服务器。
13. Response的输入流可以有效的从socket服务器直接读取相应内容。
14. 在http1.0和http1.1中利用KeepAlive保持持久连接。
15. 直接获取服务器发送的response code和 headers。
16. 设置连接超时的能力。
17. 实验性的支持http1.1 response caching。
18. 源代码基于Apache License 可免费获取。
三、使用方法
使用HttpClient发送请求、接收响应很简单,一般需要如下几步即可。
1. 创建HttpClient对象。
2. 创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。
3. 如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。
4. 调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse。
5. 调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
6. 释放连接。无论执行方法是否成功,都必须释放连接
Apache HttpComponents™项目负责创建和维护一个专注于HTTP和相关协议的低级Java组件工具集。
案例:
package com.aimai.test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import javax.ws.rs.HttpMethod; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIUtils; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.HTTP; /** * 最简单的HTTP客户端,用来演示通过GET或者POST方式访问某个页面 */ public class SimpleClient { public static void main(String[] args) throws Exception { SimpleClient simpleClient = new SimpleClient(); simpleClient.postMethod(); } public void getMethod() throws Exception { // (1) 创建HttpGet实例 HttpGet get = new HttpGet("http://www.126.com"); // (2) 使用HttpClient发送get请求,获得返回结果HttpResponse HttpClient http = new DefaultHttpClient(); HttpResponse response = http.execute(get); // (3) 读取返回结果 if (response.getStatusLine().getStatusCode() == 200) { HttpEntity entity = response.getEntity(); InputStream in = entity.getContent(); readResponse(in); } } /*** * get 请求带参 * * @throws Exception */ public void getMethodParams() throws Exception { // (1)创建查询参数 List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("name", "ahopedog")); params.add(new BasicNameValuePair("work", "程序员")); String queryString = URLEncodedUtils.format(params, "utf-8"); // (2) 创建Get实例 URI uri = URIUtils.createURI("http", "localhost", 8080, "/jsx/servlet", queryString, null); HttpGet get = new HttpGet(uri); // (2) 使用HttpClient发送get请求,获得返回结果HttpResponse HttpClient http = new DefaultHttpClient(); HttpResponse response = http.execute(get); // (3) 读取返回结果 if (response.getStatusLine().getStatusCode() == 200) { HttpEntity entity = response.getEntity(); InputStream in = entity.getContent(); readResponse(in); } } public void postMethod() throws Exception { // (1) Post请求 HttpPost post = new HttpPost("http://www.126.com"); // (2) 发送请求 HttpClient http = new DefaultHttpClient(); HttpResponse response = http.execute(post); // (3) 处理响应结果 if (response.getStatusLine().getStatusCode() == 200) { HttpEntity entity = response.getEntity(); // (4) 从输入流读取网页字符串内容 System.out.println(entity.getContentType()); System.out.println(entity.getContentEncoding()); System.out.println(entity.getContentLength()); InputStream in = null; try { in = entity.getContent(); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); String line = null; while ((line = reader.readLine()) != null) { System.out.println(line); } } finally { // 记得关闭输入流 if (in != null) in.close(); } } } /*** * post带参 * * @throws Exception */ private void postMethodParams() throws Exception { // (1) Post请求 HttpPost post = new HttpPost("http://localhost:8080/jsx/servlet"); // 添加参数 List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("name", "ahopedog")); params.add(new BasicNameValuePair("work", "程序员")); post.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); // (3) 发送请求 HttpClient http = new DefaultHttpClient(); HttpResponse response = http.execute(post); // (3) 读取返回结果 if (response.getStatusLine().getStatusCode() == 200) { HttpEntity entity = response.getEntity(); InputStream in = entity.getContent(); readResponse(in); } } public static void readResponse(InputStream in) throws Exception { BufferedReader reader = new BufferedReader(new InputStreamReader(in)); String line = null; while ((line = reader.readLine()) != null) { System.out.println(line); } } }
(1)HttpGet的实例就是一个get请求,构造函数只有一个字符串参数,即要获取的网页地址。另外一种构造形式是使用URI实例作为HttpGet的参数。HttpComponents提供了URIUtils类,它的createURI()返回一个URI实例,将请求地址拆分构造不失为一种更加清晰的方式。
URI uri = URIUtils.createURI("http", "www.126.com", 80, "/", "", null);
HttpGet get = new HttpGet(uri);
(2)请求最后被HttpClient发送出去,new DefaultHttpClient()创建一个基本的HttpClient实例。由于底层是基于阻塞的JAVA I/O模型,执行execute()的时间与具体请求的远程服务器和网络速度有关,在实际运行场景中应特别注意此问题。如果是在tomcat等环境中执行可能会造成线程等待,浪费服务器资源,或拒绝其它的连接。
(3)请求返回后就可以读取返回内容了,但有一个前提是此次请求是否真的成功了?服务器地址错误,或请求的页面不存在等问题都会让请求失败。为了确保得到了正确的响应首先应判断返回码是否正确。调用response.getStatusLine()返回一个StatusLine的实例,此实例描述了一次请求的响应信息。一个成功响应的StatusLine实例本身包含如下信息:
HTTP/1.0 200 OK
HTTP/1.0:是请求协议和版本号
200:是响应码
StatusLine的下面2个方法分别用于获取响应信息的各部分内容
getProtocolVersion(): 得到请求协议和协议版本号,如HTTP/1.0
getStatusCode():得到响应码,如200
HttpEntity entity = response.getEntity()返回一个HttpEntity实例,进而调用getContent()就得到了一个输入流。后面的事情应该很明确了。readResponse()是一个自己写的读取输入流中字符串的方法
四 参数传递
Get与Post在传递参数时有一些区别,Get请求的参数作为查询字符串出传递,而Post请求的参数则作为实体传递。在开发WEB项目时经常遇到乱码的问题,使用HttpComponents也会涉及到这个问题,所以在使用时应特别注意。服务器端的处理方法与WEB项目相同, HttpComponents只要注意字符编码就可以了。
Get请求传递参数方法一:将查询字符串作为请求地址的一部分
这是一种最简单的传参方式,将查询参数用(&)连接,然后放在请求地址?的后面,如下面这个请求地址
http://localhost:8080/servlet1?name=ahopedog&work=programer
Get请求传递参数方法二:使用URI携带查询字符串
还记得上面提到的HttpGet有一种用URI构造的方法吗?这第二种传递的方式就是借助了这个机制,只是HttpComponents提供了一种创建查询参数比较清晰的方式NameValuePair
(1)NameValuePair用一对键、值表示一个查询参数,将多个NameValuePair放在一个List中,就形成了一组查询参数。但是List<NameValuePair>并不能直接被HttpGet使用,所以需要用URLEncodedUtils.format()方法将其编码成字符串。URLEncodedUtils是HttpComponents提供的一个编译查询字符串的工具类。
(2)使用编译好的查询字符串构造URI对象,这样查询参数就一起被发送到了服务器上。
其实,这里的查询字符串完全可以手工的方式拼凑出来,只是,从代码的清晰性和维护性方面考虑,NameValuePair和URLEncodedUtils的方式更加可取。值得一提的是,在开发J2EE项目时,经常遇到一些查询条件或请求条件众多的情况,有的是将多个值放在一个Map中管理,有的则创建一个固定结构的Java Bean类。在这方面不同人可能会有不同的看法。Map方式固然省事,而且也很灵活,但是如果缺少了文档和注释时,会很难知道这个Map中放的到底是什么。而创建成Java Bean的话,代码本身就是一个很好的说明,让人一目了然,缺点是导致Java Bean的急剧增加,以致混乱和难以管理。
本人想不到什么很完美的解决办法,但是,任何极端的方式都是不可取,我想在这时折中或许稍好些,什么方法由场景决定。
HttpEntity有3个获取返回数据的描述信息(或叫做元数据)
getContentType():获取响应体的类型
getContentEncoding():获取响应体的字符编码
getContentLength():获取响应体的字节长度
元数据的内容由远程服务器返回,实际上这些信息是包含在响应的头部信息中的,HTTP请求的响应头中还包含了其它有用的信息,HttpComponents将返回头中的关键元数据封装到了HttpEntity中,已便于使用。
entity.getContent()可以得到响应体的InputStream,有了这个流对象,基本上就可以"为所欲为"了。因为InputStream是Java I/O中底层的基础类,结合相对上层的输入流对象或者对字节进行编码等方法就可以获得不同类型和形式的响应数据了。在本例中用BufferedReader将响应体以字符串形式读取(返回的内容确实也是字符串的内容)。
五、用途
远程访问数据,加载远程页面到本地系统
使用Httpclient(post请求)上传文件及其他参数到https协议的服务器
apache的HttpComponents项目的介绍和简单使用
浙公网安备 33010602011771号