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

Java实现HTTP Client的使用

2024-12-186.0k 阅读

Java实现HTTP Client的使用

Java中HTTP Client的发展历程

在Java的发展过程中,实现HTTP客户端功能的方式不断演进。早期,Java开发者主要使用HttpURLConnection类,它是Java标准库中处理HTTP请求的基础工具。HttpURLConnection自JDK 1.4开始引入,它提供了基本的HTTP请求和响应处理能力,例如设置请求方法(GET、POST等)、添加请求头、读取响应数据等。

然而,HttpURLConnection存在一些局限性。它的API相对繁琐,特别是在处理复杂的HTTP场景,如处理重定向、设置连接超时、处理不同类型的请求体等方面,代码编写较为复杂。而且,在性能和可扩展性方面,HttpURLConnection也不能很好地满足现代应用开发的需求。

随着Java的发展,为了更好地处理HTTP通信,Java 9引入了全新的HTTP客户端API,即java.net.http包。这个新的HTTP客户端API设计理念更加先进,它采用了现代的异步、非阻塞I/O模型,使得HTTP请求处理更加高效,并且提供了简洁易用的API,大大简化了开发者处理HTTP通信的工作。新的HTTP客户端支持HTTP/1.1和HTTP/2协议,并且在处理复杂的HTTP场景时表现得更加灵活和强大。

使用HttpURLConnection实现HTTP Client

  1. 发送GET请求

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    public class HttpGetExample {
        public static void main(String[] args) {
            try {
                URL url = new URL("http://example.com");
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                int responseCode = connection.getResponseCode();
                System.out.println("Response Code : " + responseCode);
    
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(connection.getInputStream()));
                String inputLine;
                StringBuilder response = new StringBuilder();
    
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();
    
                System.out.println(response.toString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    在上述代码中,首先创建一个URL对象,指定要访问的URL地址。然后通过openConnection方法获取HttpURLConnection实例,并设置请求方法为GET。通过getResponseCode方法获取响应状态码,通过getInputStream方法获取响应输入流,读取响应内容。

  2. 发送POST请求

    import java.io.BufferedReader;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.nio.charset.StandardCharsets;
    
    public class HttpPostExample {
        public static void main(String[] args) {
            String url = "http://example.com/api";
            String params = "param1=value1&param2=value2";
            try {
                URL obj = new URL(url);
                HttpURLConnection con = (HttpURLConnection) obj.openConnection();
                con.setRequestMethod("POST");
                con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                con.setRequestProperty("Content-Length", String.valueOf(params.length()));
                con.setDoOutput(true);
                DataOutputStream wr = new DataOutputStream(con.getOutputStream());
                wr.writeBytes(params);
                wr.flush();
                wr.close();
    
                int responseCode = con.getResponseCode();
                System.out.println("Response Code : " + responseCode);
    
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(con.getInputStream()));
                String inputLine;
                StringBuilder response = new StringBuilder();
    
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();
    
                System.out.println(response.toString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    对于POST请求,除了设置请求方法为POST,还需要设置Content - Type请求头,并通过DataOutputStream将请求参数写入输出流。

使用Java 9+的HTTP Client实现HTTP请求

  1. 发送GET请求

    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.io.IOException;
    import java.util.concurrent.ExecutionException;
    
    public class Java9HttpGetExample {
        public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                   .uri(URI.create("http://example.com"))
                   .GET()
                   .build();
    
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.statusCode());
            System.out.println(response.body());
        }
    }
    

    这里通过HttpClient.newHttpClient()创建一个HttpClient实例,通过HttpRequest.newBuilder构建请求,设置请求的URI并指定请求方法为GET。最后通过client.send方法发送请求,并使用HttpResponse.BodyHandlers.ofString()处理响应体,将响应内容以字符串形式返回。

  2. 发送POST请求

    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.nio.charset.StandardCharsets;
    import java.io.IOException;
    import java.util.concurrent.ExecutionException;
    
    public class Java9HttpPostExample {
        public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
            HttpClient client = HttpClient.newHttpClient();
            String params = "param1=value1&param2=value2";
            HttpRequest request = HttpRequest.newBuilder()
                   .uri(URI.create("http://example.com/api"))
                   .POST(HttpRequest.BodyPublishers.ofString(params, StandardCharsets.UTF_8))
                   .header("Content-Type", "application/x-www-form-urlencoded")
                   .build();
    
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.statusCode());
            System.out.println(response.body());
        }
    }
    

    对于POST请求,使用HttpRequest.BodyPublishers.ofString将请求参数设置到请求体中,并设置Content - Type请求头。

处理HTTP请求头

  1. 使用HttpURLConnection设置请求头

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    public class HttpHeaderExampleWithUrlConnection {
        public static void main(String[] args) {
            try {
                URL url = new URL("http://example.com");
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setRequestProperty("User - Agent", "Mozilla/5.0");
                connection.setRequestProperty("Accept", "application/json");
    
                int responseCode = connection.getResponseCode();
                System.out.println("Response Code : " + responseCode);
    
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(connection.getInputStream()));
                String inputLine;
                StringBuilder response = new StringBuilder();
    
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();
    
                System.out.println(response.toString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    HttpURLConnection中,通过setRequestProperty方法设置请求头,这里设置了User - AgentAccept请求头。

  2. 使用Java 9+的HTTP Client设置请求头

    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.io.IOException;
    import java.util.concurrent.ExecutionException;
    
    public class HttpHeaderExampleWithJava9Client {
        public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                   .uri(URI.create("http://example.com"))
                   .GET()
                   .header("User - Agent", "Mozilla/5.0")
                   .header("Accept", "application/json")
                   .build();
    
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.statusCode());
            System.out.println(response.body());
        }
    }
    

    在Java 9+的HTTP Client中,通过header方法设置请求头,同样设置了User - AgentAccept请求头。

处理HTTP响应头

  1. 使用HttpURLConnection获取响应头

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.Map;
    
    public class HttpResponseHeaderExampleWithUrlConnection {
        public static void main(String[] args) {
            try {
                URL url = new URL("http://example.com");
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
    
                int responseCode = connection.getResponseCode();
                System.out.println("Response Code : " + responseCode);
    
                Map<String, List<String>> headers = connection.getHeaderFields();
                for (String key : headers.keySet()) {
                    System.out.println(key + " : " + headers.get(key));
                }
    
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(connection.getInputStream()));
                String inputLine;
                StringBuilder response = new StringBuilder();
    
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();
    
                System.out.println(response.toString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    通过HttpURLConnectiongetHeaderFields方法获取所有响应头,它返回一个Map,其中键是响应头名称,值是一个List,因为可能存在多个同名的响应头。

  2. 使用Java 9+的HTTP Client获取响应头

    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.io.IOException;
    import java.util.concurrent.ExecutionException;
    
    public class HttpResponseHeaderExampleWithJava9Client {
        public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                   .uri(URI.create("http://example.com"))
                   .GET()
                   .build();
    
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.statusCode());
            response.headers().map().forEach((key, value) -> System.out.println(key + " : " + value));
            System.out.println(response.body());
        }
    }
    

    在Java 9+的HTTP Client中,通过response.headers().map()获取响应头的Map,并遍历输出。

处理HTTP重定向

  1. HttpURLConnection处理重定向

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    public class HttpRedirectExampleWithUrlConnection {
        public static void main(String[] args) {
            try {
                URL url = new URL("http://redirect.example.com");
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setInstanceFollowRedirects(true);
    
                int responseCode = connection.getResponseCode();
                System.out.println("Response Code : " + responseCode);
    
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(connection.getInputStream()));
                String inputLine;
                StringBuilder response = new StringBuilder();
    
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();
    
                System.out.println(response.toString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    HttpURLConnection中,通过设置setInstanceFollowRedirects(true)来自动处理重定向。如果设置为false,开发者需要手动处理重定向,通过获取Location响应头并重新发起请求。

  2. Java 9+的HTTP Client处理重定向

    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.io.IOException;
    import java.util.concurrent.ExecutionException;
    
    public class HttpRedirectExampleWithJava9Client {
        public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
            HttpClient client = HttpClient.newHttpClient()
                   .newBuilder()
                   .followRedirects(HttpClient.Redirect.ALWAYS)
                   .build();
            HttpRequest request = HttpRequest.newBuilder()
                   .uri(URI.create("http://redirect.example.com"))
                   .GET()
                   .build();
    
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.statusCode());
            System.out.println(response.body());
        }
    }
    

    在Java 9+的HTTP Client中,通过HttpClient.newHttpClient().newBuilder().followRedirects(HttpClient.Redirect.ALWAYS)设置自动处理重定向,HttpClient.Redirect枚举还提供了NEVERPROMPT等选项,用于更灵活地控制重定向处理。

设置连接超时和读取超时

  1. HttpURLConnection设置超时

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    public class HttpTimeoutExampleWithUrlConnection {
        public static void main(String[] args) {
            try {
                URL url = new URL("http://example.com");
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setConnectTimeout(5000); // 连接超时5秒
                connection.setReadTimeout(5000); // 读取超时5秒
    
                int responseCode = connection.getResponseCode();
                System.out.println("Response Code : " + responseCode);
    
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(connection.getInputStream()));
                String inputLine;
                StringBuilder response = new StringBuilder();
    
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();
    
                System.out.println(response.toString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    HttpURLConnection中,通过setConnectTimeout设置连接超时时间,通过setReadTimeout设置读取超时时间,单位都是毫秒。

  2. Java 9+的HTTP Client设置超时

    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.io.IOException;
    import java.time.Duration;
    import java.util.concurrent.ExecutionException;
    
    public class HttpTimeoutExampleWithJava9Client {
        public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
            HttpClient client = HttpClient.newHttpClient()
                   .newBuilder()
                   .connectTimeout(Duration.ofSeconds(5))
                   .build();
            HttpRequest request = HttpRequest.newBuilder()
                   .uri(URI.create("http://example.com"))
                   .GET()
                   .build();
    
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.statusCode());
            System.out.println(response.body());
        }
    }
    

    在Java 9+的HTTP Client中,通过HttpClient.newHttpClient().newBuilder().connectTimeout(Duration.ofSeconds(5))设置连接超时时间,这里使用Duration类来表示时间。对于读取超时,目前Java 9+的HTTP Client没有直接提供设置读取超时的方法,但可以通过自定义BodyHandler来实现类似功能。

处理HTTPS请求

  1. 使用HttpURLConnection处理HTTPS请求

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import javax.net.ssl.HttpsURLConnection;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    
    public class HttpsExampleWithUrlConnection {
        public static void main(String[] args) {
            try {
                TrustManager[] trustAllCerts = new TrustManager[]{
                        new X509TrustManager() {
                            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                                return null;
                            }
                            public void checkClientTrusted(X509Certificate[] certs, String authType) {
                            }
                            public void checkServerTrusted(X509Certificate[] certs, String authType) {
                            }
                        }
                };
                SSLContext sc = SSLContext.getInstance("TLS");
                sc.init(null, trustAllCerts, new java.security.SecureRandom());
                HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    
                URL url = new URL("https://example.com");
                HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
    
                int responseCode = connection.getResponseCode();
                System.out.println("Response Code : " + responseCode);
    
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(connection.getInputStream()));
                String inputLine;
                StringBuilder response = new StringBuilder();
    
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();
    
                System.out.println(response.toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    在处理HTTPS请求时,HttpURLConnection需要处理SSL证书验证。上述代码通过创建一个信任所有证书的TrustManager,并设置到SSLContext中,然后设置HttpsURLConnection的默认SSLSocketFactory来实现忽略证书验证。在实际应用中,应该使用更安全的证书验证方式,例如加载合法的证书到信任库。

  2. 使用Java 9+的HTTP Client处理HTTPS请求

    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.io.IOException;
    import java.util.concurrent.ExecutionException;
    
    public class HttpsExampleWithJava9Client {
        public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                   .uri(URI.create("https://example.com"))
                   .GET()
                   .build();
    
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.statusCode());
            System.out.println(response.body());
        }
    }
    

    Java 9+的HTTP Client默认会进行证书验证。如果服务器使用的是合法的证书,上述代码可以直接正常工作。如果需要忽略证书验证(仅适用于开发和测试环境),可以通过自定义HttpClientSslContext来实现,类似HttpURLConnection的处理方式,但需要更多的代码来配置。

异步HTTP请求

  1. Java 9+的HTTP Client异步请求
    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.io.IOException;
    import java.util.concurrent.CompletableFuture;
    
    public class AsyncHttpExampleWithJava9Client {
        public static void main(String[] args) {
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                   .uri(URI.create("http://example.com"))
                   .GET()
                   .build();
    
            CompletableFuture<HttpResponse<String>> future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
            future.thenApply(HttpResponse::body)
                   .thenAccept(System.out::println)
                   .join();
        }
    }
    
    Java 9+的HTTP Client提供了异步请求的能力。通过sendAsync方法发送请求,它返回一个CompletableFuture。可以通过thenApplythenAccept等方法对异步结果进行处理,这里获取响应体并打印。异步请求在处理I/O密集型操作时非常有用,可以提高应用程序的性能和响应性,避免线程阻塞。

总结不同方式的优缺点

  1. HttpURLConnection

    • 优点:是Java标准库的一部分,不需要额外引入依赖,广泛支持,兼容性好,在老版本的Java环境中也能使用。
    • 缺点:API繁琐,处理复杂HTTP场景(如重定向、认证等)代码复杂,性能和可扩展性相对较差,在高并发场景下表现不佳。
  2. Java 9+的HTTP Client

    • 优点:简洁易用的API,采用现代异步、非阻塞I/O模型,性能和可扩展性好,支持HTTP/2协议,对复杂HTTP场景的处理更加灵活,并且在处理异步请求方面有很好的支持。
    • 缺点:依赖Java 9及以上版本,对于使用老版本Java的项目无法使用。

在实际项目中,应根据项目的具体需求和所使用的Java版本来选择合适的HTTP客户端实现方式。如果项目需要兼容老版本Java,HttpURLConnection可能是一个选择;而如果项目基于Java 9及以上版本,并且对性能和开发效率有较高要求,Java 9+的HTTP Client则是更好的选择。

通过以上对Java中不同HTTP客户端实现方式的介绍,开发者可以根据实际情况灵活选择,以满足项目中HTTP通信的需求。无论是简单的GET和POST请求,还是复杂的HTTPS、异步请求等场景,都能找到合适的解决方案来实现高效、可靠的HTTP通信。