MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java使用Apache HttpClient进行网络请求

2023-09-027.7k 阅读

1. 简介

在Java开发中,进行网络请求是一项常见的任务。Apache HttpClient是一个广泛使用的Java库,它提供了丰富且强大的功能来处理HTTP请求和响应。它支持最新的HTTP标准,并且具有高度可定制性和灵活性,能够满足各种复杂的网络请求场景。

2. 环境搭建

首先,需要在项目中引入Apache HttpClient的依赖。如果使用Maven进行项目管理,可以在pom.xml文件中添加以下依赖:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

如果使用Gradle,则在build.gradle文件中添加:

implementation 'org.apache.httpcomponents:httpclient:4.5.13'

确保网络连接正常,因为在实际使用中,HttpClient需要与远程服务器进行通信。

3. 基本的GET请求

3.1 使用CloseableHttpClient创建GET请求

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;

public class GetRequestExample {
    public static void main(String[] args) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("https://www.example.com");
        try {
            HttpResponse response = httpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() == 200) {
                String responseBody = EntityUtils.toString(response.getEntity());
                System.out.println("Response Body: " + responseBody);
            } else {
                System.out.println("Request failed with status code: " + response.getStatusLine().getStatusCode());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在上述代码中,首先通过HttpClients.createDefault()创建了一个默认的CloseableHttpClient实例。然后创建了一个HttpGet对象,指定请求的URL。通过httpClient.execute(httpGet)执行请求并获取HttpResponse。如果响应状态码为200,说明请求成功,通过EntityUtils.toString(response.getEntity())获取响应体内容并打印。最后,在finally块中关闭httpClient以释放资源。

3.2 设置请求头

有时候需要在GET请求中设置请求头,比如设置User - Agent。可以通过HttpGetsetHeader方法来实现。

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;

public class GetRequestWithHeadersExample {
    public static void main(String[] args) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("https://www.example.com");
        httpGet.setHeader("User - Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
        try {
            HttpResponse response = httpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() == 200) {
                String responseBody = EntityUtils.toString(response.getEntity());
                System.out.println("Response Body: " + responseBody);
            } else {
                System.out.println("Request failed with status code: " + response.getStatusLine().getStatusCode());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

这里通过httpGet.setHeader("User - Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")设置了User - Agent请求头,模拟浏览器发送请求。

4. 基本的POST请求

4.1 使用UrlEncodedFormEntity发送表单数据

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class PostRequestExample {
    public static void main(String[] args) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost("https://www.example.com/api/post");
        List<NameValuePair> params = new ArrayList<>();
        params.add(new BasicNameValuePair("username", "testuser"));
        params.add(new BasicNameValuePair("password", "testpassword"));
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(params));
            HttpResponse response = httpClient.execute(httpPost);
            if (response.getStatusLine().getStatusCode() == 200) {
                String responseBody = EntityUtils.toString(response.getEntity());
                System.out.println("Response Body: " + responseBody);
            } else {
                System.out.println("Request failed with status code: " + response.getStatusLine().getStatusCode());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在这段代码中,首先创建了HttpPost对象并指定请求的URL。然后通过List<NameValuePair>来存储表单参数,这里添加了usernamepassword参数。接着使用UrlEncodedFormEntity将参数设置到HttpPost请求中。执行请求后,处理响应并在控制台打印响应体或错误状态码。

4.2 使用MultipartEntityBuilder发送复杂数据(如文件上传)

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.File;
import java.io.IOException;

public class MultipartPostRequestExample {
    public static void main(String[] args) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost("https://www.example.com/api/upload");
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        builder.addPart("username", new StringBody("testuser", ContentType.TEXT_PLAIN));
        File file = new File("path/to/file.txt");
        builder.addPart("file", new FileBody(file, ContentType.APPLICATION_OCTET_STREAM));
        try {
            httpPost.setEntity(builder.build());
            HttpResponse response = httpClient.execute(httpPost);
            if (response.getStatusLine().getStatusCode() == 200) {
                String responseBody = EntityUtils.toString(response.getEntity());
                System.out.println("Response Body: " + responseBody);
            } else {
                System.out.println("Request failed with status code: " + response.getStatusLine().getStatusCode());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在这个例子中,MultipartEntityBuilder用于构建复杂的POST请求,比如文件上传。首先设置了请求模式为BROWSER_COMPATIBLE,然后添加了username参数作为StringBody,并添加了一个文件作为FileBody。最后将构建好的实体设置到HttpPost请求中并执行请求,处理响应。

5. 处理响应

5.1 获取响应状态码

在前面的示例中,已经展示了如何通过response.getStatusLine().getStatusCode()获取响应状态码。状态码可以告诉我们请求是否成功,以及失败的原因。常见的状态码有:

  • 200:请求成功,服务器已成功处理请求并返回响应。
  • 400:客户端请求错误,比如请求参数格式不正确。
  • 401:未授权,请求需要用户认证但未提供有效认证信息。
  • 404:请求的资源不存在。
  • 500:服务器内部错误,服务器在处理请求时发生了错误。
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import java.io.IOException;

public class GetStatusCodeExample {
    public static void main(String[] args) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("https://www.example.com");
        try {
            HttpResponse response = httpClient.execute(httpGet);
            int statusCode = response.getStatusLine().getStatusCode();
            System.out.println("Status Code: " + statusCode);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

这段代码只获取并打印响应状态码,用于演示获取状态码的方法。

5.2 获取响应头

可以通过HttpResponsegetFirstHeadergetHeaders等方法获取响应头信息。

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import java.io.IOException;

public class GetResponseHeadersExample {
    public static void main(String[] args) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("https://www.example.com");
        try {
            HttpResponse response = httpClient.execute(httpGet);
            Header contentTypeHeader = response.getFirstHeader("Content - Type");
            if (contentTypeHeader != null) {
                System.out.println("Content - Type: " + contentTypeHeader.getValue());
            }
            Header[] allHeaders = response.getHeaders("Cache - Control");
            for (Header header : allHeaders) {
                System.out.println("Cache - Control: " + header.getValue());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在上述代码中,使用getFirstHeader获取Content - Type响应头的值,使用getHeaders获取所有Cache - Control响应头并打印其值。

5.3 获取响应体

前面的示例中已经使用EntityUtils.toString(response.getEntity())获取了字符串形式的响应体。但如果响应体是二进制数据,比如图片、文件等,就需要使用不同的方法。

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class GetBinaryResponseBodyExample {
    public static void main(String[] args) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("https://www.example.com/image.jpg");
        try {
            HttpResponse response = httpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() == 200) {
                InputStream inputStream = response.getEntity().getContent();
                FileOutputStream fileOutputStream = new FileOutputStream("downloaded_image.jpg");
                byte[] buffer = new byte[1024];
                int length;
                while ((length = inputStream.read(buffer)) != -1) {
                    fileOutputStream.write(buffer, 0, length);
                }
                fileOutputStream.close();
                inputStream.close();
            } else {
                System.out.println("Request failed with status code: " + response.getStatusLine().getStatusCode());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

这段代码用于下载图片,通过response.getEntity().getContent()获取输入流,然后将数据写入本地文件。

6. 连接管理与配置

6.1 连接池

在高并发场景下,频繁地创建和销毁HTTP连接会消耗大量资源,影响性能。Apache HttpClient提供了连接池来管理连接,提高连接的复用率。

import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;

public class ConnectionPoolExample {
    public static void main(String[] args) throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sslContext = SSLContexts.createSystemDefault();
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
               .register("http", PlainConnectionSocketFactory.getSocketFactory())
               .register("https", sslConnectionSocketFactory)
               .build();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
        cm.setMaxTotal(100);
        cm.setDefaultMaxPerRoute(20);
        RequestConfig requestConfig = RequestConfig.custom()
               .setConnectTimeout(5000)
               .setConnectionRequestTimeout(5000)
               .setSocketTimeout(5000)
               .build();
        CloseableHttpClient httpClient = HttpClients.custom()
               .setConnectionManager(cm)
               .setDefaultRequestConfig(requestConfig)
               .build();
        // 使用httpClient进行请求
    }
}

在这段代码中,首先创建了SSLContextSSLConnectionSocketFactory,用于处理HTTPS连接。然后通过RegistryBuilder注册了HTTP和HTTPS的连接工厂。接着创建了PoolingHttpClientConnectionManager并设置了最大连接数和每个路由的最大连接数。RequestConfig用于设置连接超时、请求超时和套接字超时。最后通过HttpClients.custom()构建了带有连接池和请求配置的CloseableHttpClient

6.2 代理设置

如果需要通过代理服务器发送请求,可以在RequestConfig中进行设置。

import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import java.net.InetSocketAddress;
import java.net.Proxy;

public class ProxyExample {
    public static void main(String[] args) {
        Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.example.com", 8080));
        RequestConfig requestConfig = RequestConfig.custom()
               .setProxy(proxy)
               .build();
        CloseableHttpClient httpClient = HttpClients.custom()
               .setDefaultRequestConfig(requestConfig)
               .build();
        // 使用httpClient进行请求
    }
}

这里通过RequestConfig.custom().setProxy(proxy).build()设置了代理服务器,然后将其应用到CloseableHttpClient中。

7. 认证机制

7.1 基本认证(Basic Authentication)

import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;

public class BasicAuthenticationExample {
    public static void main(String[] args) {
        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(
                new AuthScope("www.example.com", 443),
                new UsernamePasswordCredentials("username", "password")
        );
        CloseableHttpClient httpClient = HttpClients.custom()
               .setDefaultCredentialsProvider(credentialsProvider)
               .build();
        HttpGet httpGet = new HttpGet("https://www.example.com/api/protected");
        try {
            httpClient.execute(httpGet);
            HttpResponse response = httpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() == 200) {
                String responseBody = EntityUtils.toString(response.getEntity());
                System.out.println("Response Body: " + responseBody);
            } else {
                System.out.println("Request failed with status code: " + response.getStatusLine().getStatusCode());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在这个示例中,通过BasicCredentialsProvider设置了用户名和密码,并通过HttpClients.custom().setDefaultCredentialsProvider(credentialsProvider)将认证信息应用到CloseableHttpClient中。

7.2 OAuth认证

OAuth认证相对复杂,涉及到获取令牌等多个步骤。这里以OAuth 2.0为例,假设已经获取到了访问令牌。

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;

public class OAuthExample {
    public static void main(String[] args) {
        String accessToken = "your_access_token";
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("https://www.example.com/api/oauth - protected");
        httpGet.setHeader("Authorization", "Bearer " + accessToken);
        try {
            HttpResponse response = httpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() == 200) {
                String responseBody = EntityUtils.toString(response.getEntity());
                System.out.println("Response Body: " + responseBody);
            } else {
                System.out.println("Request failed with status code: " + response.getStatusLine().getStatusCode());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在这个代码中,通过在请求头中设置Authorization: Bearer your_access_token来进行OAuth认证。

8. 异常处理

在使用Apache HttpClient时,可能会遇到各种异常。常见的异常有:

  • IOException:这是最常见的异常,可能在执行请求、读取响应等操作时发生,比如网络连接中断、服务器无响应等。在前面的示例中,已经通过catch (IOException e) { e.printStackTrace(); }来捕获并简单处理这种异常。
  • URISyntaxException:当请求的URL格式不正确时会抛出此异常。例如:
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import java.net.URISyntaxException;

public class URISyntaxExceptionExample {
    public static void main(String[] args) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        try {
            HttpGet httpGet = new HttpGet("invalid_url");
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }
}
  • NoHttpResponseException:当服务器在一定时间内没有响应时抛出此异常。可以通过设置合适的超时时间来尽量避免这种异常,并且在捕获到该异常时,可以选择重试请求等操作。
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;

public class NoHttpResponseExceptionExample {
    public static void main(String[] args) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("https://www.example.com/slow - responding - endpoint");
        try {
            HttpResponse response = httpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() == 200) {
                String responseBody = EntityUtils.toString(response.getEntity());
                System.out.println("Response Body: " + responseBody);
            } else {
                System.out.println("Request failed with status code: " + response.getStatusLine().getStatusCode());
            }
        } catch (IOException e) {
            if (e instanceof org.apache.http.NoHttpResponseException) {
                // 处理无响应异常,可选择重试
                System.out.println("Server did not respond, can retry...");
            } else {
                e.printStackTrace();
            }
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

通过合理地处理这些异常,可以使程序更加健壮,提高用户体验。

9. 高级特性

9.1 异步请求

Apache HttpClient从4.4版本开始支持异步请求,这在处理大量请求或者需要提高响应速度的场景下非常有用。

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.concurrent.Future;

public class AsyncRequestExample {
    public static void main(String[] args) {
        CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault();
        httpClient.start();
        HttpGet httpGet = new HttpGet("https://www.example.com");
        Future<HttpResponse> future = httpClient.execute(httpGet, null);
        try {
            HttpResponse response = future.get();
            if (response.getStatusLine().getStatusCode() == 200) {
                String responseBody = EntityUtils.toString(response.getEntity());
                System.out.println("Response Body: " + responseBody);
            } else {
                System.out.println("Request failed with status code: " + response.getStatusLine().getStatusCode());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        httpClient.close();
    }
}

在这个示例中,通过HttpAsyncClients.createDefault()创建了一个异步的CloseableHttpAsyncClient。然后启动客户端,执行异步请求并通过Future获取响应。还可以使用FutureCallback来更灵活地处理异步响应。

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;

public class AsyncRequestWithCallbackExample {
    public static void main(String[] args) {
        CloseableHttpAsyncClient httpClient = HttpAsyncClients.createDefault();
        httpClient.start();
        HttpGet httpGet = new HttpGet("https://www.example.com");
        httpClient.execute(httpGet, new FutureCallback<HttpResponse>() {
            @Override
            public void completed(HttpResponse response) {
                try {
                    if (response.getStatusLine().getStatusCode() == 200) {
                        String responseBody = EntityUtils.toString(response.getEntity());
                        System.out.println("Response Body: " + responseBody);
                    } else {
                        System.out.println("Request failed with status code: " + response.getStatusLine().getStatusCode());
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void failed(Exception ex) {
                ex.printStackTrace();
            }

            @Override
            public void cancelled() {
                System.out.println("Request cancelled");
            }
        });
        // 防止主线程退出
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        httpClient.close();
    }
}

这里通过FutureCallbackcompleted方法处理成功的响应,failed方法处理失败的情况,cancelled方法处理请求被取消的情况。

9.2 自定义拦截器

可以通过自定义拦截器来对请求和响应进行预处理和后处理。

import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpRequestInterceptor;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.Date;

public class CustomInterceptorExample {
    public static class MyInterceptor implements HttpRequestInterceptor {
        @Override
        public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
            System.out.println("Request intercepted at " + new Date());
            request.addHeader("Custom - Header", "Value");
        }
    }

    public static void main(String[] args) {
        CloseableHttpClient httpClient = HttpClients.custom()
               .addInterceptorFirst(new MyInterceptor())
               .build();
        HttpGet httpGet = new HttpGet("https://www.example.com");
        try {
            HttpResponse response = httpClient.execute(httpGet, HttpClientContext.create());
            if (response.getStatusLine().getStatusCode() == 200) {
                String responseBody = EntityUtils.toString(response.getEntity());
                System.out.println("Response Body: " + responseBody);
            } else {
                System.out.println("Request failed with status code: " + response.getStatusLine().getStatusCode());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在这个例子中,自定义了一个MyInterceptor实现HttpRequestInterceptor接口,在process方法中添加了一个自定义请求头并打印拦截时间。然后通过HttpClients.custom().addInterceptorFirst(new MyInterceptor())将拦截器添加到CloseableHttpClient中。

通过以上全面深入的介绍,涵盖了从基础的GET、POST请求到高级的连接管理、认证机制、异常处理以及异步请求和自定义拦截器等内容,希望能帮助开发者在Java项目中熟练运用Apache HttpClient进行高效的网络请求开发。