CloseableHttpClient 类 的 public <T> T execute(final ClassicHttpRequest request, final HttpClientResponseHandler<? extends T> responseHandler ) 的get和post及记录日志
CloseableHttpClient 的 execute 方法通过指定的 HttpClientResponseHandler 对响应进行处理。HttpClientResponseHandler 是一个回调接口,允许我们在响应返回时对其进行自定义处理。这个方法特别有用,因为可以在 execute 调用中直接处理响应,不需要将响应内容提取出来再进行额外的处理。
方法签名
public <T> T execute(final ClassicHttpRequest request, final HttpClientResponseHandler<? extends T> responseHandler) throws IOException
ClassicHttpRequest request:这是请求对象,包含了要发送的 HTTP 请求。HttpClientResponseHandler<? extends T> responseHandler:这是对响应进行处理的回调接口,允许我们自定义响应处理逻辑并返回处理后的结果。<T>:方法的返回值类型,由HttpClientResponseHandler决定。可以是字符串、JSON 对象,甚至是自定义的数据类型。
使用步骤
- 创建 
ClassicHttpRequest对象,例如HttpGet、HttpPost等请求类型。 - 创建一个实现 
HttpClientResponseHandler的实例,定义响应处理逻辑。 - 使用 
execute方法发送请求,并获得响应处理结果。 
示例代码
以下是一个完整的示例,其中 HttpClientResponseHandler 被用于处理 GET 请求的响应并返回响应内容作为字符串:
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.ClassicHttpRequest;
import org.apache.hc.client5.http.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.io.HttpClientResponseHandler;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import java.io.IOException;
public class HttpClientExample {
    public static void main(String[] args) {
        // 创建 HttpClient
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            // 创建请求对象
            ClassicHttpRequest request = new HttpGet("https://jsonplaceholder.typicode.com/posts/1");
            // 创建响应处理器
            HttpClientResponseHandler<String> responseHandler = response -> {
                int status = response.getCode();
                if (status >= 200 && status < 300) {
                    // 处理响应内容
                    return EntityUtils.toString(response.getEntity());
                } else {
                    throw new IOException("Unexpected response status: " + status);
                }
            };
            // 发送请求并获取响应
            String responseBody = httpClient.execute(request, responseHandler);
            System.out.println("Response: " + responseBody);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
代码解读
- 
创建请求对象:使用
HttpGet创建了一个 GET 请求。 - 
定义响应处理器:使用
HttpClientResponseHandler<String>实现了一个处理器来处理响应。- 当响应状态码在 200 到 299 之间时,将响应体解析为字符串并返回。
 - 否则,抛出异常表示处理失败。
 
 - 
执行请求:调用
execute(request, responseHandler)发送请求。- 此方法会阻塞,直到收到响应并通过 
responseHandler进行处理。 - 返回结果为 
String,即处理器处理后的数据。 
 - 此方法会阻塞,直到收到响应并通过 
 - 
输出结果:将处理后的响应输出到控制台。
 
当响应状态码在 200 到 299 之间时分别有如下一些情况:
在 HTTP 协议中,状态码 2xx 表示请求成功。每个具体的 2xx 状态码表示一种特定的成功情况:
常见的 2xx 状态码及其含义
- 
200 OK
- 表示请求成功,并且服务器已成功处理并返回了请求的数据。
 - 适用于几乎所有的成功请求场景,例如 
GET、POST、PUT、DELETE请求的成功处理。 
 - 
201 Created
- 表示服务器成功创建了资源,通常在 
POST请求中使用。 - 响应中可能包含指向新创建资源的 URI。
 - 例如,向服务器发送一个创建新用户的 
POST请求,成功后返回201 Created,并可能包含新用户的 URI。 
 - 表示服务器成功创建了资源,通常在 
 - 
202 Accepted
- 请求已被服务器接受,但尚未被处理。
 - 适用于异步处理请求的情况。请求可能在稍后才会被实际处理。
 - 例如,大型数据处理或异步任务提交时,服务器可以先返回 
202 Accepted,然后在后台处理请求。 
 - 
203 Non-Authoritative Information
- 表示请求成功,但返回的元数据(如响应头或部分响应数据)可能并非源自服务器的原始数据。
 - 通常是在代理服务器或缓存服务器返回的响应,不是服务器的原始版本。
 - 使用场景不常见。
 
 - 
204 No Content
- 请求成功,但没有返回任何内容。
 - 适用于无需返回数据的操作,例如成功执行 
DELETE请求时返回204 No Content。 
 - 
205 Reset Content
- 请求成功,但客户端应重置显示,通常用于刷新表单或清除输入内容。
 - 适用于需要客户端重置输入状态的场景,例如表单提交成功后,服务器可以返回 
205,提示客户端清除表单内容。 
 - 
206 Partial Content
- 表示服务器成功处理了部分请求的数据。
 - 通常在实现 HTTP 分块下载时使用。客户端可以通过 
Range头请求部分资源,而服务器返回206 Partial Content及请求的部分内容。 - 适用于大型文件的分段传输,支持暂停和恢复下载。
 
 
其他可能的 2xx 状态码
一些不太常见的 2xx 状态码可能出现在特定的协议或场景下,如 207 Multi-Status,通常在 WebDAV 扩展中使用,表示多个资源的状态,但在常规 HTTP 应用中很少使用。
总结
200:请求成功并返回数据。201:资源创建成功(用于POST请求)。202:请求已接受,待处理。203:请求成功,返回的可能不是服务器的原始数据。204:请求成功,无返回数据。205:请求成功,要求客户端重置显示。206:请求成功,返回部分数据(用于分块下载)。
这些 2xx 状态码帮助客户端明确知道请求结果和服务器执行的具体操作。
CloseableHttpClient 类 的 public 
使用 CloseableHttpClient 类的 execute 方法发送 POST 请求并记录日志时,可以在自定义的 HttpClientResponseHandler 中实现该逻辑。HttpClientResponseHandler 会在请求成功和失败的情况下捕获响应状态码、响应实体以及响应头。
示例代码
下面的代码演示了如何实现这一要求。使用了 HttpPost 构建请求,通过自定义的 HttpClientResponseHandler 处理响应,将状态码、响应实体以及响应头写入日志:
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.ClassicHttpRequest;
import org.apache.hc.client5.http.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.io.HttpClientResponseHandler;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.apache.hc.core5.http.io.entity.UrlEncodedFormEntity;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
public class HttpClientPostExample {
    private static final Logger logger = Logger.getLogger(HttpClientPostExample.class.getName());
    public static void main(String[] args) {
        // 创建 HttpClient
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            // 创建 POST 请求
            HttpPost postRequest = new HttpPost("https://jsonplaceholder.typicode.com/posts");
            // 设置请求参数
            List<NameValuePair> params = new ArrayList<>();
            params.add(new BasicNameValuePair("title", "foo"));
            params.add(new BasicNameValuePair("body", "bar"));
            params.add(new BasicNameValuePair("userId", "1"));
            postRequest.setEntity(new UrlEncodedFormEntity(params));
            // 自定义响应处理器
            HttpClientResponseHandler<String> responseHandler = new HttpClientResponseHandler<String>() {
                @Override
                public String handleResponse(ClassicHttpResponse response) throws IOException {
                    int statusCode = response.getCode();
                    HttpEntity entity = response.getEntity();
                    String responseBody = entity != null ? EntityUtils.toString(entity) : null;
                    // 记录状态码
                    logger.info("Status Code: " + statusCode);
                    // 记录响应体
                    logger.info("Response Entity: " + responseBody);
                    // 记录响应头
                    response.getHeaders().forEach(header -> 
                        logger.info("Header: " + header.getName() + " = " + header.getValue())
                    );
                    // 返回响应体
                    return responseBody;
                }
            };
            // 执行请求
            String responseBody = httpClient.execute(postRequest, responseHandler);
            System.out.println("Response: " + responseBody);
        } catch (IOException e) {
            e.printStackTrace();
            logger.severe("Request failed: " + e.getMessage());
        }
    }
}
代码解析
- 
创建
HttpPost请求:使用HttpPost设置请求的 URL 和参数。- 设置参数时,使用了 
UrlEncodedFormEntity,将参数转换为表单格式。 
 - 设置参数时,使用了 
 - 
自定义
HttpClientResponseHandler:实现了HttpClientResponseHandler接口,定义了对响应的处理。statusCode:获取并记录响应的状态码。responseBody:通过EntityUtils.toString(entity)获取并记录响应实体内容。response.getHeaders():遍历并记录每个响应头。
 - 
异常处理:捕获
IOException,在请求失败时记录日志。 
日志输出示例
执行此代码时,日志会输出如下信息:
INFO: Status Code: 201
INFO: Response Entity: {"id":101,"title":"foo","body":"bar","userId":1}
INFO: Header: Content-Type = application/json; charset=utf-8
INFO: Header: Content-Length = 48
...
注意事项
- 状态码范围:
handleResponse方法可以捕获所有状态码,包括成功和失败的状态码。 - EntityUtils:
EntityUtils.toString(entity)可以将HttpEntity转为字符串,便于记录,但请注意它会将实体消耗掉,不能重复读取。 
由于 String responseBody = httpClient.execute(postRequest, responseHandler); 可能会抛出 IOException, 有没有可能在请求成功的情况下抛出 IOException?
在调用 httpClient.execute(postRequest, responseHandler) 时,即使请求成功,也可能会在一些特定情况下抛出 IOException。这些情况通常与网络连接问题、流处理、数据读取问题等有关,而不是响应的 HTTP 状态码。以下是一些可能在请求成功的情况下仍抛出 IOException 的情况:
可能抛出 IOException 的情况
- 
连接中断或超时:即使服务器返回了响应,若在客户端读取响应数据时网络连接中断或发生超时,就会抛出
IOException。这种情况较为常见,尤其是当客户端的网络状态不稳定或服务器响应较慢时。 - 
响应体读取失败:在
responseHandler处理响应时,通过EntityUtils.toString(entity)读取HttpEntity。如果在将流读取为字符串的过程中发生错误,例如流的格式无效,或者数据量过大导致内存不足,就会抛出IOException。 - 
响应头读取失败:在解析响应头或遍历响应头时,如果发生 IO 错误(例如流被意外关闭或数据不可访问),也会抛出
IOException。 - 
响应解压缩失败:如果服务器的响应使用了
gzip等压缩方式,而客户端在解压缩时遇到数据损坏或格式错误,也会抛出IOException。 - 
连接池异常:
CloseableHttpClient使用了连接池,如果连接池因配置或资源限制导致连接不可用,或连接池状态异常,则可能在请求成功但尚未读取数据时抛出IOException。 
如何捕获和记录这些异常
为了确保在 IOException 出现时仍然能记录状态码、响应体、响应头等信息,可以在 responseHandler 中进一步处理这些异常。例如,通过 try-catch 包裹读取操作,在读取失败时捕获异常并记录信息。以下是改进后的代码示例:
HttpClientResponseHandler<String> responseHandler = new HttpClientResponseHandler<String>() {
    @Override
    public String handleResponse(ClassicHttpResponse response) throws IOException {
        int statusCode = response.getCode();
        String responseBody = null;
        try {
            // 获取并记录状态码
            logger.info("Status Code: " + statusCode);
            // 获取并记录响应体
            HttpEntity entity = response.getEntity();
            responseBody = entity != null ? EntityUtils.toString(entity) : null;
            logger.info("Response Entity: " + responseBody);
            // 记录响应头
            response.getHeaders().forEach(header ->
                logger.info("Header: " + header.getName() + " = " + header.getValue())
            );
        } catch (IOException e) {
            logger.severe("Error while reading response: " + e.getMessage());
            throw e; // 重新抛出以便捕获此异常
        }
        return responseBody;
    }
};
其他处理建议
在调用 httpClient.execute 时还可以使用外围 try-catch,确保无论 IOException 出现在何处,都可以捕获并记录异常:
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
    String responseBody = httpClient.execute(postRequest, responseHandler);
    System.out.println("Response: " + responseBody);
} catch (IOException e) {
    logger.severe("Request failed with IOException: " + e.getMessage());
}
总结
在请求成功但读取数据或处理响应过程中仍可能抛出 IOException,而不是由响应状态码本身引发。通过在 responseHandler 内部及外部分别进行 try-catch 捕获,可以确保在各类 IO 问题下都能准确记录响应的状态信息。
                    
                
                
            
        
浙公网安备 33010602011771号