HttpComponents —— HTTP实体(HttpEntity)

目录:

  • HTTPClient根据其内容出自何处区分三种类型的实体
  • 重复实体
  • 使用HTTP实体
  • 确保低级别资源释放
  • 消耗实体内容
  • 生成实体内容
    • 动态内容实体
    • HTML表单
    • 内容分块


         HTTP报文可以携带和请求或响应相关的内容实体。实体可以在一些请求和响应中找到,因为它们也是可选的。使用了实体的请求被称为封闭实体请求。HTTP规范定义了两种封闭实体的方法:POSTPUT。响应通常期望包含一个内容实体。这个规则也有特例,比如HEAD方法的响应和204 NoContent304 Not Modified205 Reset Content响应。

 

HttpClient根据其内容出自何处区分三种类型的实体:


streamed流式

        内容从流中获得,或者在运行中产生。特别是这种分类包含从HTTP响应中获取的实体。流式实体是不可重复生成的。

self-contained自我包含式

        内容在内存中或通过独立的连接或其它实体中获得。自我包含式的实体是可以重复生成的。这种类型的实体会经常用于封闭HTTP请求的实体。

wrapping包装式

        内容从另外一个实体中获得。


        当从一个HTTP响应中获取流式内容时,这个区别对于连接管理很重要。对于由应用程序创建而且只使用HttpClient发送的请求实体,流式和自我包含式的不同就不那么重要了。这种情况下,建议考虑如流式这种不能重复的实体,和可以重复的自我包含式实体。

 

重复实体


实体可以重复,意味着它的内容可以被多次读取。这就仅仅是自我包含式的实体了(像ByteArrayEntity或StringEntity)。

 

使用HTTP实体


        一个实体既可以代表二进制内容又可以代表字符内容,同时也支持字符编码。实体是在使用封闭内容执行请求,或当请求已经成功执行,或当响应体结果发送到客户端时创建的。

        要从实体中读取内容,可以通过HttpEntity#getContent()方法从输入流中获取,这会返回一个java.io.InputStream对象,或者提供一个输出流到HttpEntity#writeTo(OutputStream)方法中,这会一次返回所有写入到给定流中的内容。

        当实体通过一个收到的报文获取时,HttpEntity#getContentType()方法和 HttpEntity#getContentLength()方法可以用来读取通用的元数据,如Content-TypeContent-Length头部信息(如果它们是可用的)。因为头部信息Content-Type可以包含对文本MIME类型的字符编码,比如text/plaintext /htmlHttpEntity#getContentEncoding()方法用来读取这个信息。如果头部信息不可用,那么就返回长度-1,而对于内容类型返回NULL。如果头部信息Content-Type是可用的,那么就会返回一个Header对象。

StringEntity entity =new StringEntity("important message","UTF-8");

System.out.println(entity.getContentType());

System.out.println(entity.getContentLength());

System.out.println(ContentType.getOrDefault(entity));

System.out.println(EntityUtils.toString(entity));

System.out.println(EntityUtils.toByteArray(entity).length);

输出:

Content-Type: text/plain; charset=UTF-8

17

text/plain; charset=UTF-8

important message

17

 

确保低级别资源释放


        当完成一个响应实体,那么保证所有实体内容已经被完全消耗是很重要的,所以连接可以安全的放回到连接池中,而且可以通过连接管理器对后续的请求重用连接。处理这个操作的最方便的方法是调用HttpEntity#consumeContent()方法来消耗流中的任意可用内容。HttpClient探测到内容流尾部已经到达后,会立即会自动释放低层连接,并放回到连接管理器。HttpEntity#consumeContent()方法调用多次也是安全的。

        也可能会有特殊情况,当整个响应内容的一小部分需要获取,消耗剩余内容而损失性能,还有重用连接的代价太高,则可以仅仅通过调用HttpUriRequest#abort()方法来中止请求。

HttpGet httpGet =new HttpGet("http://www.baidu.com");

HttpResponse response = client.execute(httpGet);

HttpEntity entity = response.getEntity();

if (entity !=null) {

InputStream instream = entity.getContent();

int byteOne = instream.read();

int byteTwo = instream.read();

// Do not need the rest

httpGet.abort();

}

连接不会被重用,但是由它持有的所有级别的资源将会被正确释放。

 

消耗实体内容


        推荐消耗实体内容的方式是使用它的HttpEntity#getContent()HttpEntity#writeTo(OutputStream)方法。HttpClient也自带EntityUtils类,这会暴露出一些静态方法,这些方法可以更加容易地从实体中读取内容或信息。代替直接读取 java.io.InputStream,也可以使用这个类中的方法以字符串/字节数组的形式获取整个内容体。然而,EntityUtils的使用是强烈不鼓励的,除非响应实体源自可靠的HTTP服务器和已知的长度限制。

HttpGet httpGet =new HttpGet("http://localhost/");

HttpResponse response = client.execute(httpGet);

HttpEntity entity = response.getEntity();

if (entity !=null) {

long len = entity.getContentLength();

if (len != -1 && len < 2048) {

System.out.println(EntityUtils.toString(entity));

} else {

// Stream content out

}

}

        在一些情况下可能会不止一次的读取实体。此时实体内容必须以某种方式在内存或磁盘上被缓冲起来。最简单的方法是通过使用BufferedHttpEntity类来包装源实体完成。这会引起源实体内容被读取到内存的缓冲区中。在其它所有方式中,实体包装器将会得到源实体。

HttpGet httpGet =new HttpGet("http://localhost/");

HttpResponse response = client.execute(httpGet);

HttpEntity entity = response.getEntity();

if (entity !=null) {

entity = new BufferedHttpEntity(entity);

}

 

生成实体内容


        HttpClient提供一些类,它们可以用于生成通过HTTP连接获得内容的有效输出流。为了封闭实体从HTTP请求中获得的输出内容,那些类的实例可以和封闭如POSTPUT请求的实体相关联。HttpClient为很多公用的数据容器,比如字符串,字节数组,输入流和文件提供了一些类:StringEntityByteArrayEntityInputStreamEntityFileEntity

File file =new File("somefile.txt");

ContentType contentType = ContentType.create("text/plain","UTF-8");

FileEntity entity =new FileEntity(file, contentType);

HttpPost httpPost =new HttpPost("http://localhost/action.do");

httpPost.setEntity(entity);

        请注意InputStreamEntity是不可重复的,因为它仅仅能从低层数据流中读取一次内容。通常来说,我们推荐实现一个定制的HttpEntity类,这是自我包含式的,用来代替使用通用的InputStreamEntity。FileEntity也是一个很好的起点。


动态内容实体


        通常来说,HTTP实体需要基于特定的执行上下文来动态地生成。通过使用EntityTemplate实体类和ContentProducer接口,HttpClient提供了动态实体的支持。内容生成器是按照需求生成它们内容的对象,将它们写入到一个输出流中。它们是每次被请求时来生成内容。所以用EntityTemplate创建的实体通常是自我包含而且可以重复的。

ContentProducer cp =new ContentProducer() {

public void writeTo(OutputStream outstream) throws IOException {

Writer writer = new OutputStreamWriter(outstream,"UTF-8");

writer.write("");

writer.write(" ");

writer.write(" important stuff");

writer.write(" ");

writer.write("");

writer.flush();

}

};

HttpEntity entity =new EntityTemplate(cp);

HttpPost httppost =new HttpPost("http://localhost/handler.do");

httppost.setEntity(entity);

HTML表单

        许多应用程序需要频繁模拟提交一个HTML表单的过程,比如,为了来记录一个Web应用程序或提交输出数据。HttpClient提供了特殊的实体类UrlEncodedFormEntity来这个满足过程。

List fromParams =new ArrayList();

fromParams.add(new BasicNameValuePair("param1","value1"));

fromParams.add(new BasicNameValuePair("param2","中文参数值"));

UrlEncodedFormEntity entity =new UrlEncodedFormEntity(fromParams,"UTF-8");

HttpPost httpPost =new HttpPost("http://localhost/handler.do");

httpPost.setEntity(entity);

UrlEncodedFormEntity实例将会使用URL编码来编码参数,生成如下的内容:

param1=value1¶m2=%E4%B8%AD%E6%96%87%E5%8F%82%E6%95%B0%E5%80%BC

内容分块


        通常,我们推荐让HttpClient选择基于被传递的HTTP报文属性的最适合的编码转换。这是可能的,但是,设置HttpEntity#setChunked()方法为true是通知HttpClient分块编码的首选。请注意HttpClient将会使用标识作为提示。当使用的HTTP协议版本,如HTTP/1.0版本,不支持分块编码时,这个值会被忽略。

StringEntity entity =new StringEntity("important message",

"text/plain; charset=\"UTF-8\"");

entity.setChunked(true);

HttpPost httppost =new HttpPost("http://localhost/acrtion.do");

httppost.setEntity(entity);

你可能感兴趣的:(HttpClient)