logo资料库

HttpClient4.3教程.PDF

第1页 / 共51页
第2页 / 共51页
第3页 / 共51页
第4页 / 共51页
第5页 / 共51页
第6页 / 共51页
第7页 / 共51页
第8页 / 共51页
资料共51页,剩余部分请下载后查看
转自:http://www.yeetrack.com/?p=779 前言 Http 协议应该是互联网中最重要的协议。持续增长的 web 服务、可联网的家用电器等都在继承并拓 展着 Http 协议,向着浏览器之外的方向发展。 虽然 jdk 中的 java.net 包中提供了一些基本的方法,通过 http 协议来访问网络资源,但是大多数场 景下,它都不够灵活和强大。HttpClient 致力于填补这个空白,它可以提供有效的、最新的、功能丰 富的包来实现 http 客户端。 为了拓展,HttpClient 即支持基本的 http 协议,还支持 http-aware 客户端程序,如 web 浏览器, Webservice 客户端,以及利用 or 拓展 http 协议的分布式系统。 1、HttpClient 的范围/特性  是一个基于 HttpCore 的客户端 Http 传输类库  基于传统的(阻塞)IO  内容无关 2、HttpClient 不能做的事情  HttpClient 不是浏览器,它是一个客户端 http 协议传输类库。HttpClient 被用来发送和接受 Http 消息。HttpClient 不会处理 http 消息的内容,不会进行 javascript 解析,不会关心 content type,如果没有明确设置,httpclient 也不会对请求进行格式化、重定向 url,或者 其他任何和 http 消息传输相关的功能。 第一章 基本概念 1.1. 请求执行 HttpClient 最基本的功能就是执行 Http 方法。一个 Http 方法的执行涉及到一个或者多个 Http 请求 /Http 响应的交互,通常这个过程都会自动被 HttpClient 处理,对用户透明。用户只需要提供 Http 请 求对象,HttpClient 就会将 http 请求发送给目标服务器,并且接收服务器的响应,如果 http 请求执 行不成功,httpclient 就会抛出异样。 下面是个很简单的 http 请求执行的例子:
1. CloseableHttpClient httpclient = HttpClients.createDefault(); 2. HttpGet httpget = new HttpGet("http://localhost/"); 3. CloseableHttpResponse response = httpclient.execute(httpget); 4. try { 5. 6. } finally { 7. 8. } response.close(); <...> 1.1.1. HTTP 请求 所有的 Http 请求都有一个请求行(request line),包括方法名、请求的 URI 和 Http 版本号。 HttpClient 支持 HTTP/1.1 这个版本定义的所有 Http 方法:GET,HEAD,POST,PUT,DELETE,TRACE 和 OPTIONS。对于每一种 http 方法,HttpClient 都定义了一个相应的类: HttpGet, HttpHead, HttpPost, HttpPut, HttpDelete, HttpTrace 和 HttpOpquertions。 Request-URI 即统一资源定位符,用来标明 Http 请求中的资源。Http request URIs 包含协议名、主 机名、主机端口(可选)、资源路径、query(可选)和片段信息(可选)。 1. HttpGet httpget = new HttpGet( 2. f&oq="); "http://www.google.com/search?hl=en&q=httpclient&btnG=Google+Search&aq= HttpClient 提供 URIBuilder 工具类来简化 URIs 的创建和修改过程。 1. URI uri = new URIBuilder() 2. .setScheme("http") 3. .setHost("www.google.com") 4. .setPath("/search") 5. .setParameter("q", "httpclient") 6. .setParameter("btnG", "Google Search") 7. .setParameter("aq", "f") 8. .setParameter("oq", "") 9. .build(); 10. HttpGet httpget = new HttpGet(uri); 11. System.out.println(httpget.getURI());
上述代码会在控制台输出: 1. http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq= 1.1.2. HTTP 响应 服务器收到客户端的 http 请求后,就会对其进行解析,然后把响应发给客户端,这个响应就是 HTTP response.HTTP 响应第一行是协议版本,之后是数字状态码和相关联的文本段。 1. HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 2. HttpStatus.SC_OK, "OK"); 3. 4. System.out.println(response.getProtocolVersion()); 5. System.out.println(response.getStatusLine().getStatusCode()); 6. System.out.println(response.getStatusLine().getReasonPhrase()); 7. System.out.println(response.getStatusLine().toString()); 上述代码会在控制台输出: 1. HTTP/1.1 2. 200 3. OK 4. HTTP/1.1 200 OK 1.1.3. 消息头 一个 Http 消息可以包含一系列的消息头,用来对 http 消息进行描述,比如消息长度,消息类型等 等。HttpClient 提供了方法来获取、添加、移除、枚举消息头。 HttpStatus.SC_OK, "OK"); 1. HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 2. 3. response.addHeader("Set-Cookie", 4. 5. response.addHeader("Set-Cookie", "c1=a; path=/; domain=localhost");
"c2=b; path=\"/\", c3=c; domain=\"localhost\""); 6. 7. Header h1 = response.getFirstHeader("Set-Cookie"); 8. System.out.println(h1); 9. Header h2 = response.getLastHeader("Set-Cookie"); 10. System.out.println(h2); 11. Header[] hs = response.getHeaders("Set-Cookie"); 12. System.out.println(hs.length); 上述代码会在控制台输出: 1. Set-Cookie: c1=a; path=/; domain=localhost 2. Set-Cookie: c2=b; path="/", c3=c; domain="localhost" 3. 2 最有效的获取指定类型的消息头的方法还是使用 HeaderIterator 接口。 HttpStatus.SC_OK, "OK"); "c1=a; path=/; domain=localhost"); "c2=b; path=\"/\", c3=c; domain=\"localhost\""); 1. HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 2. 3. response.addHeader("Set-Cookie", 4. 5. response.addHeader("Set-Cookie", 6. 7. 8. HeaderIterator it = response.headerIterator("Set-Cookie"); 9. 10. while (it.hasNext()) { 11. 12. } System.out.println(it.next()); 上述代码会在控制台输出: 1. Set-Cookie: c1=a; path=/; domain=localhost 2. Set-Cookie: c2=b; path="/", c3=c; domain="localhost" HeaderIterator 也提供非常便捷的方式,将 Http 消息解析成单独的消息头元素。 1. HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, "OK"); "c1=a; path=/; domain=localhost"); response.headerIterator("Set-Cookie")); "c2=b; path=\"/\", c3=c; domain=\"localhost\""); 2. 3. response.addHeader("Set-Cookie", 4. 5. response.addHeader("Set-Cookie", 6. 7. 8. HeaderElementIterator it = new BasicHeaderElementIterator( 9. 10. 11. while (it.hasNext()) { 12. 13. 14. 15. 16. 17. 18. } 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]); } 上述代码会在控制台输出: 1. c1 = a 2. path=/ 3. domain=localhost 4. c2 = b 5. path=/ 6. c3 = c 7. domain=localhost 1.1.4. HTTP 实体 Http 消息可以携带 http 实体,这个 http 实体既可以是 http 请求,也可以是 http 响应的。Http 实 体,可以在某些 http 请求或者响应中发现,但不是必须的。Http 规范中定义了两种包含请求的方 法:POST 和 PUT。HTTP 响应一般会包含一个内容实体。当然这条规则也有异常情况,如 Head 方 法的响应,204 没有内容,304 没有修改或者 205 内容资源重置。 HttpClient 根据来源的不同,划分了三种不同的 Http 实体内容。   streamed 流式: 内容是通过流来接受或者在运行中产生。特别是,streamed 这一类包含 从 http 响应中获取的实体内容。一般说来,streamed 实体是不可重复的。 self-contained 自我包含式:内容在内存中或通过独立的连接或其它实体中获得。self- contained 类型的实体内容通常是可重复的。这种类型的实体通常用于关闭 http 请求。
 wrapping 包装式: 这种类型的内容是从另外的 http 实体中获取的。 当从 Http 响应中读取内容时,上面的三种区分对于连接管理器来说是非常重要的。对于由应用程序 创建而且只使用 HttpClient 发送的请求实体,streamed 和 self-contained 两种类型的不同就不那么 重要了。这种情况下,建议考虑如 streamed 流式这种不能重复的实体,和可以重复的 self- contained 自我包含式实体。 1.1.4.1. 可重复的实体 一个实体是可重复的,也就是说它的包含的内容可以被多次读取。这种多次读取只有 self contained (自包含)的实体能做到(比如 ByteArrayEntity 或者 StringEntity)。 1.1.4.2. 使用 Http 实体 由于一个 Http 实体既可以表示二进制内容,又可以表示文本内容,所以 Http 实体要支持字符编码 (为了支持后者,即文本内容)。 当需要执行一个完整内容的 Http 请求或者 Http 请求已经成功,服务器要发送响应到客户端时,Http 实体就会被创建。 如果要从 Http 实体中读取内容,我们可以利用 HttpEntity 类的 getContent 方法来获取实体的输入流 (java.io.InputStream),或者利用 HttpEntity 类的 writeTo(OutputStream)方法来获取输出流,这个 方法会把所有的内容写入到给定的流中。 当实体类已经被接受后,我们可以利用 HttpEntity 类的 getContentType()和 getContentLength()方 法来读取 Content-Type 和 Content-Length 两个头消息(如果有的话)。由于 Content-Type 包含 mime-types 的字符编码,比如 text/plain 或者 text/html,HttpEntity 类的 getContentEncoding()方法 就是读取这个编码的。如果头信息不存在,getContentLength()会返回-1,getContentType()会返 回 NULL。如果 Content-Type 信息存在,就会返回一个 Header 类。 当为发送消息创建 Http 实体时,需要同时附加 meta 信息。 ContentType.create("text/plain", "UTF-8")); 1. StringEntity myEntity = new StringEntity("important message", 2. 3. 4. System.out.println(myEntity.getContentType()); 5. System.out.println(myEntity.getContentLength()); 6. System.out.println(EntityUtils.toString(myEntity)); 7. System.out.println(EntityUtils.toByteArray(myEntity).length); 上述代码会在控制台输出:
1. Content-Type: text/plain; charset=utf-8 2. 17 3. important message 4. 17 1.1.5. 确保底层的资源连接被释放 为了确保系统资源被正确地释放,我们要么管理 Http 实体的内容流、要么关闭 Http 响应。 HttpEntity entity = response.getEntity(); if (entity != null) { InputStream instream = entity.getContent(); try { 1. CloseableHttpClient httpclient = HttpClients.createDefault(); 2. HttpGet httpget = new HttpGet("http://localhost/"); 3. CloseableHttpResponse response = httpclient.execute(httpget); 4. try { 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. } finally { 15. 16. } // do something useful } finally { instream.close(); } } response.close(); 关闭 Http 实体内容流和关闭 Http 响应的区别在于,前者通过消耗掉 Http 实体内容来保持相关的 http 连接,然后后者会立即关闭、丢弃 http 连接。 请注意 HttpEntity 的 writeTo(OutputStream)方法,当 Http 实体被写入到 OutputStream 后,也要确 保释放系统资源。如果这个方法内调用了 HttpEntity 的 getContent()方法,那么它会有一个 java.io.InpputStream 的实例,我们需要在 finally 中关闭这个流。 但是也有这样的情况,我们只需要获取 Http 响应内容的一小部分,而获取整个内容并、实现连接的 可重复性代价太大,这时我们可以通过关闭响应的方式来关闭内容输入、输出流。 1. CloseableHttpClient httpclient = HttpClients.createDefault(); 2. HttpGet httpget = new HttpGet("http://localhost/");
HttpEntity entity = response.getEntity(); if (entity != null) { 3. CloseableHttpResponse response = httpclient.execute(httpget); 4. try { 5. 6. 7. 8. 9. 10. 11. 12. } finally { 13. 14. } InputStream instream = entity.getContent(); int byteOne = instream.read(); int byteTwo = instream.read(); // Do not need the rest response.close(); } 上面的代码执行后,连接变得不可用,所有的资源都将被释放。 1.1.6. 消耗 HTTP 实体内容 HttpClient 推荐使用 HttpEntity 的 getConent()方法或者 HttpEntity 的 writeTo(OutputStream)方法来 消耗掉 Http 实体内容。HttpClient 也提供了 EntityUtils 这个类,这个类提供一些静态方法可以更容 易地读取 Http 实体的内容和信息。和以 java.io.InputStream 流读取内容的方式相比,EntityUtils 提 供的方法可以以字符串或者字节数组的形式读取 Http 实体。但是,强烈不推荐使用 EntityUtils 这个 类,除非目标服务器发出的响应是可信任的,并且 http 响应实体的长度不会过大。 long len = entity.getContentLength(); if (len != -1 && len < 2048) { HttpEntity entity = response.getEntity(); if (entity != null) { 1. CloseableHttpClient httpclient = HttpClients.createDefault(); 2. HttpGet httpget = new HttpGet("http://localhost/"); 3. CloseableHttpResponse response = httpclient.execute(httpget); 4. try { 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. } finally { 15. 16. } System.out.println(EntityUtils.toString(entity)); // Stream content out response.close(); } else { } }
分享到:
收藏