第一章。基础java
1.1 请求的执行程序员
HttpClient最重要的函数是用于执行HTTP方法.执行一次HTTP方法包含一次或数次HTTP请求和HTTP响应的交互,一般在httpClient内部完成.程序员只须要提供一个请求对象用于执行,HttpClient发送请求到目标服务器并得到对应的响应对象,或者在执行不成功时抛出异常.web
HttpClient API的主要入口点是HttpClient接口.
json
如下是请求执行处理过程的简单示例数组
CloseableHttpClient httpclient = HttpClients.createDefault(); HttpGet httpget = new HttpGet("http://localhost"); CloseableHttpResponse response = httpclient.execute(httpget); try { <...> } finally { resopnse.close(); }
1.1.1 HTTP请求缓存
全部的HTTP请求都有一个由方法名,请求路径和HTTP协议版本组成的请求行.
服务器
HttpClient支持HTTP/1.1定义的全部HTTP方法,具体是;GET,HEAD,POST,PUT,DELETE,TRACE和OPTIONS.每一个类型的方法分别对应具体的类:HttpGet,HttpHead,HttpPost,HttpPut,HttpDelete,HttpTrace和HttpOptions.
app
Request-URI是用于请求惟一的统一资源定位符.HTTP请求路径由协议,主机名,可选的端口号,资源路径,可选的查询参数和可选的段(能够理解为页面元素的ID,可直接跳转).
dom
HttpGet httpget = new HttpGet("http://www.google.com/searche?h1=en&q=httpclient&btnG=Google+Search&aq=f&oq=");
HttpClient提供了URIBuilder工具类能够方便的建立和修改请求路径函数
URI uri = new URIBuilder().setScheme("http").setHost(".setPath("search").setParameter("q","httpclient").setParameter("btnG","Google Search").setPatameter("aq","f").setParameter("oq","").build(); HttpGet httpget = new HttpGet(uri); System.out.println(httpget.getURI());
输出
http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq=
1.1.2 HTTP 响应
HTTP响应是由服务器接收和解释请求信息后返回给客户端的信息.消息的第一行由协议及版本后跟着数值的状态code及缘由组成.
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,HttpStatus.SC_OK,"OK"); System.out.println(response.getProtocolVersion()); System.out.println(response.getStatusLine().getStatusCode()); System.out.println(response.getStatusLine().getReasonPhrase()); System.out.println(response.getStatusLine().toString());
输出
HTTP/1.1 200 OK HTTP/1.1 200 OK
1.1.3 处理消息头部信息
HTTP消息包含多个头部用于描述消息的属性好比:content length,content type等等.HttpClient提供了方法用于检索,添加,移除和枚举头部信息.
HttpResponse response = new BasicHttpResponse(HttpVersioin.HTTP_1_1,HttpStatus.SC_OK,"OK"); response.addHeader("Set-Cookie","c1=a;path=/;domain-localhost"); response.addHeader("Set-Cookie","c2=b;path=\"/\",c3=c;domain=\"localhost\""); Header h1 = response.getFirstHeader("Set-Cookie"); System.out.println(h1); Header h2 = response.getLastHeader("Set-Cookie"); System.out.println(h2); Header[] hs = response.getHeaders("Set-Cookie"); System.out.println(hs.length);
输出
Set-Cookie:c1=a;path=/;domain=localhost Set-Cookie:c2=b;path="/",c3=c;domain="localhost" 2
获取全部给定类型头部信息最有效的方法是使用HeaderIterator接口.
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,HttpStatus.SC,"OK"); response.addHeader("Set-Cookie","c1=a;path=/;domain-localhost"); response.addHeader("Set-Cookie","c2=b;path=\"/\",c3=c;domain=\"localhost\""); HeaderIterator it = response.headerIterator("Set-Cookie"); while(it.hasNext){ System.out.println(it.next()); }
输出
Set-Cookie: c1=a; path=/; domain=localhost Set-Cookie: c2=b; path="/", c3=c; domain="localhost"
一样提供了简便的方法解析HTTP消息单个的头元素
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,HttpStatus.SC,"OK"); response.addHeader("Set-Cookie","c1=a;path=/;domain-localhost"); response.addHeader("Set-Cookie","c2=b;path=\"/\",c3=c;domain=\"localhost\""); HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator("Set-Cookie")); while (it.hasNext()){ 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]); } }
输出
c1 = a path=/ domain=localhost c2 = b path=/ c3 = c domain=localhost
1.1.4 HTTP实体
HTTP消息能够携带一个与请求或响应相关的内容实体.实体是可选的,能够在某些请求或响应中找到.HTTP规范定义的两个包含实体的请求方法:POST和PUT.响应一般包含一个内容实体.某些状况下例外,好比:HEADE方法的响应,204 Not Content,304 Not Modified,205 Reset Content这些响应.
HttpClient根据内容的来源将实体分为三类:
streamed:内容从流中接收或者在运行过程当中产生.具体的,这类包含接收自HTTP响应的实体.Streamed实体一般是不可重复的.
self-contained:存储在内存中,独立于HTTP链接或其余的实体.一般是可重复的.这一类的实体经常使用于HTTP请求封装.
wrapping:内容来自其余的实体
对于链接管理,当从HTTP响应中得到流时这些区别十分重要.
1.1.4.1 可重复的实体
一个实体可重复意味着他的内容能够读屡次.仅对于self contained是可能的(好比ByteArrayEntity或StringEntity)
1.1.4.2 使用HTTP实体
因为实体能够表明二进制和字符内容,所以有编码的支持.
实体在执行请求过程当中封装内容时建立,或者请求成功,响应体用来向客户端返回结果.
从实体中读取内容,一种经过HttpEntity#getContent()方法得到输入流,该方法返回一个java.io.InputStream,或者为HttpEntity#writeTo(OuputStream)方法提供输出流,该方法将全部内容写入提供的流中.
当实体已经经过传入消息被接收,HttpEntity#getContentType()和HttpEntity#getContentLength()方法能够用来读取普通的元数据好比Content-Type和Content-Length头部(若是它们存在).因为Content-Type头部能够包含字符编码,HttpEntity#getContentEncoding()方法用于读取这部分信息.当该头部不存在时,长度返回-1,内容类型返回NULL.当Content-Type头部存在,返回Header对象.
当建立实体用于输出消息是,这些实体的元数据须由实体的建立者提供.
StringEntity myEntity = new StringEntity("important message",ContentType.create("text/plain","UTF-8" )); System.out.println(myEntity.getContentType()); System.out.println(myEntity.getContentLength()); System.out.println(EntityUtils.toString(myEntity)); System.out.println(EntityUtils.toByteArray(myEntity).length);
输出
Content-Type: text/plain; charset=utf-8 17 important message 17
1.1.5 确认释放底层资源
为了保证适当的释放系统资源,必须关闭实体相关联的流或者响应自己.
CloseableHttpClient httpclient = HttpClients.createDefault(); HttpGet httpget = new HttpGet("http://localhost/"); CloseableHttpResponse response = httpclient.execute(httpget); try { HttpEntity entity = response.getEntity(); if(entity != null){ InputStream instream = entity.getContent(); try { //do something useful } finally { instream.close(); } } } finally { response.close(); }
关闭内容流和关闭响应两种方式的区别在于前者尝试经过消耗实体内容保持底层链接的活动然后者则马上关闭并丢弃链接.
请注意HttpEntity#writeTo(OutputStream)方法一样要求确保在内容被彻底写出后释放系统资源.若是该方法包含经过调用HttpEntity#getContent()获得的java.io.InputStream实例,一样应该在finally代码块中关闭该流.
当使用stream实体时,能够经过使用EntityUtils#consume(HttpEntity)方法确保实体内容被彻底消耗掉以及底层的流已经被关闭.
有这样一种状况,当仅须要检索响应内容中的一小部分时,消耗剩余内容复用链接代价太高,这种状况能够经过关闭响应来终止流.
CloseableHttpClient httpclient = HttpClient.createDefault(); HttpGet httpget = new HttpGet(); CloseableHttpResponse response = httpClient.execute(httpget); try { HttpEntity entity = response.getEntity(); if(entity != null){ InputStream instream = entity.getContent(); int byteOne = instream.read(); int byteTwo = instream.read(); //Do not need the rest } } finally { response.close(); }
该链接将不会被复用,且全部引用的资源都将被正确的释放.
1.1.6 消耗实体内容
消耗实体内容推荐使用HttpEntity#getContent()或者HttpEntity#writeTo(OutputStream)方法.HttpClient还配备了一EntityUtils类,该类提供了几个静态方法可以更容易的读取实体的内容或信息.除了直接读取java.io.InputStream,能够经过该类的方法在字符串或字节数组中检索内容body部分.然而,强烈不鼓励使用EntityUtils,除非响应来自可信任的HTTP服务器且已知是有长度限制的.
CloseableHttpClient httpclient = HttpClients.createDefault(); HttpGet httpGet = new HttpGet(); CloseableHttpResponse response = httpclient.execute(httpget); try { HttpEntity entity = response.getEntity(); if(entity != null){ long len = entity.getContentLength(); if(len != -1 && len < 2048) { System.out.println(EntityUtil.toString(entity)); } else { // Steam content out } } } finally { response.close(); }
在某些状况下须要屡次读取实体内容.这时必须经过某种方式缓存实体内容,经过内存或磁盘.最简单的方法是使用BufferedHttpEntity类封装原始的实体.这样可使原始实体的内容被读入到内存缓冲区.
CloseableHttpResponse response = <...>; HttpEntity entity = response.getEntity(); if(entity != null) { entity = new BufferedHttpEntity(entity); }
1.1.7 生产实体内容
HttpClient提供了多个类经过HTTP链接高效的流出内容.这些类的实例能够关联请求内附的实体好比POST和PUT以将实体内容
装入传出HTTP请求.HttpClient提供了多个类做为大部分数据的容器好比字符串,字节数组,输入流和文件:StringEntity,ByteArrayEntity,InputStreamEntity,和FileEntity.
File file = new File("somefile.txt"); FileEntity entity = new FileEntity(file,ContentType.create("text/plain","UTF-8")); HttpPost httppost = new HttpPost(""); httppost.setEntity(entity);
请注意InputStreamEntity是不可重复的,由于它只能从底层数据流读取一次.通常状况下建议实现自定义HttpEntity类.
1.1.7.1 HTML表单
许多程序须要模拟HTML表单提交操做,好比登陆到web应用程序或者提交输入数据.HttpClient提供实体类UrlEncodedFormEntity来帮助实现这个过程.
List<NameValuePair> formparams = new ArrayList<NameValuePair>(); formparams.add(new BasicNameValuePair("param1","value1")); formparams.add(new BasicNameValuePair("param2","value2")); UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams,Consts.UTF_8); HttpPost httppost = new HttpPost(); httppost.setEntity(entity);
UrlEncodedFormEntity实例将调用URL encoding对参数编码并产生如下内容:
param1=value1¶m2=value2
1.1.7.2 内容分块
一般状况下推荐由HttpClient根据传输的HTTP消息的属性选择合适的传输编码.然而也能够经过设置HttpEntity#setChunked()为true告知HttpClient该块优先使用的编码.HttpClient仅将该标记做为提示使用.当使用的HTTP协议版本不支持分块编码时该值将被忽略.好比HTTP/1.0.
StringEntity entity = new StringEntity("important messae",ContentType.create("plain/text",Consts.UTF_8)); entity.setChunked(true); HttpPost httpPost = new HttpPost(); httppost.setEntity(entity);
1.1.8 响应处理
处理响应最简单和方便的方式是使用ResponseHandler接口,该接口包含handleResponse(HttpRespinse response)方法.该方法不须要用户担忧链接管理.当使用ResponseHandler,HttpClient将自动链接的管理和释放不管请求执行成功仍是致使异常.
CloseableHttpClient httpclient = HttpClients.createDefault(); HttpGet httpget = new HttpGet(); ResponseHandler<MyJsonObject> rh = new ResponseHandler<MyJsonObject>(){ public JsonObject handleResponse(final HttpResponse response) throws IOException{ StatusLine statusLine = response.getStatusLine(); HttpEntity entity = response.getEntity(); if(statusLine.getStatusCode() >= 300){ throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); } if(entity == null){ throw new ClientProtocolException("Response contains no content"); } Gson gson = new GsonBuilder().create(); ContentType contentType = ContentType.getOrDefault(entity); Charset charset = contentType.getCharset(); Reader reader = new InputStreamReader(entity.getContent(),charset); return gson.fromJson(reader,MyJsonObject.class); } }; MyJsonObject myjson = client.execute(httpget,rh);