《HttpClient官方文档》1.1 执行请求(二)

    xiaoxiao2024-01-24  153

    1.1.4. HTTP 实体

    HTTP 消息可以携带与其相关联的请求或响应的内容实体。实体可以在一些请求和响应中找到,因为它们也是可选的。使用了实体的请求被称为封闭实体请求。HTTP规范定义了两种封闭实体的方法: POST 和PUT。响应通常期望包含一个内容实体。 这个规则也有特例,就像HEAD 方法和 204 No Content,304 Not Modified, 205 Reset Content 响应。

    HttpClient根据其内容来源以此区分三种类型的实体:

    streamed(流式):  从流中获取或者是动态生成内容。尤其是这个类型包含了从HTTP响应中获取的实体。流式实体是不可重复生成的。

    self-contained(自包含式):  通过内存、使用独立的连接、其他实体的方式来获得内容。自包含实体可以重复生成。这种类型的实体将主要被用于封闭HTTP请求。

    wrapping(包装式):  通过其他实体来获得内容.

    当一个HTTP请求中正在流式输出内容时,这个区分对于连接管理来说很重要。而对于由应用程序创建,并且只使用 HttpClient 发送的请求实体,流式和自包含的区分是没有多大意义的。在这种情况下,建议考虑如流式这样的非可重复的实体,或者是像自包含式那样的可重复实体。

    1.1.4.1. 重复实体

    一个可重复的实体,这意味着它的内容可以被多次读取。 只有自包含式才可能会有这样的实体 (比如ByteArrayEntity orStringEntity)

    1.1.4.2. 使用 HTTP 实体

    由于一个实体可以同时表示二进制和字符内容,因此它拥有对字符编码的支持(对后者支持,即支持字符内容)。

    当执行一次使用封闭内容的请求或者是当请求成功后,响应体被当做返回结果发回客户端的时候创建实体。

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

    当实体已经接收到传入的消息, 使用HttpEntity#getContentType()方法和HttpEntity#getContentLength() 方法能够读取通用元数据,例如 Content-Type 和Content-Length 头不 (如果它们是可用的). 当Content-Type 头部包含对文本 mime类型的字符编码比如 text/plain 和text/html时,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) 方法也同样确保系统资源在实体被全部写出的时候必须正确的释放,如果该方法获得的实例通过类java.io.InputStream 调用了HttpEntity#getContent()方法, 那么它会被预期在finally子句关闭流。

    当流实体工作时, 能够使用EntityUtils#consume(HttpEntity) 方法确保 实体内容被充分的消耗和关闭底层流.

    有可能出现这样的特殊情况,只需要获取响应内容的其中一小部分或者是通过消耗剩余内容来使连接可重用造成性能损失太高,这样的情况下可以通过关闭响应来终止内容流。

    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(); 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 类这样直接读取的方式,并且也可以使用这个类中的方法以字符串/字节数组的形式获取整个内容体。 但是, EntityUtils 这个类只有在响应实体来自受信任的 HTTP 服务器和长度是已知和有限的情况下才推荐使用。

    CloseableHttpClient httpclient = HttpClients.createDefault(); HttpGet httpget = new HttpGet("http://localhost/"); 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(EntityUtils.toString(entity)); } else { // Stream content out } } } finally { response.close(); }

    在某些情况下可能需要能够读取的实体内容超过一次。在这种情况下的实体内容必须以某种方式缓存在内存或磁盘上。解决这个问题最简单的方式是通过 the 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("http://localhost/action.do"); httppost.setEntity(entity);

    请注意, InputStreamEntity 是不可重复的,因为它仅能从底层数据流中读取一次内容。 通常建议来实现一个自定义 HttpEntity 类, 这个是自包含式的类来替代通用类 InputStreamEntity.FileEntity 也是一个很不错的点子.

    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("http://localhost/handler.do"); httppost.setEntity(entity);

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

    param1=value1¶m2=value2

    1.1.7.2. 内容分块

    开始传输前,通常建议让 HttpClient 选择基于HTTP消息属性的最适当的传输编码方式。 不过当设置HttpEntity#setChunked()方法为true时,通知HttpClient该块编码是首选。 请注意 HttpClient将此标识只作为一个提示. 当使用的HTTP协议不支持块编码,比如使用HTTP/1.0协议时,这个值就会被忽略。

    StringEntity entity = new StringEntity("important message", ContentType.create("plain/text", Consts.UTF_8)); entity.setChunked(true); HttpPost httppost = new HttpPost("http://localhost/acrtion.do"); httppost.setEntity(entity);

    1.1.8. 响应处理程序

    处理响应最简单方便的方式是使用 ResponseHandler 接口, 它包含了handleResponse(HttpResponse response) 方法。 这个方法彻底的解决了用户对于连接管理的困扰。当使用ResponseHandler接口时, 无论是否请求执行成功或引发异常,HttpClient都会自动关注来确保释放的连接返回到连接管理器。

    CloseableHttpClient httpclient = HttpClients.createDefault(); HttpGet httpget = new HttpGet("http://localhost/json"); ResponseHandler<MyJsonObject> rh = new ResponseHandler<MyJsonObject>() { @Override 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);

     转载自 并发编程网 - ifeve.com

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)