Neko

Post

Exploring Java HTTP Clients for Modern Web Applications

Traversing Java’s HTTP Landscape: A Survey of Clients for Modern Web Applications

In the ever-evolving landscape of web development, HTTP REST clients play a pivotal role in enabling communication between distributed systems. For Java developers, selecting the appropriate HTTP REST client is a crucial decision that impacts the efficiency, maintainability, and performance of applications. This article delves into why choosing the right Java HTTP REST client is essential and explores some of the most popular clients available today.

Why Choosing the Right Java HTTP REST Client Matters

  • Performance and Efficiency: Different HTTP clients offer varying levels of performance. Some are optimized for speed and low resource consumption, which is critical for applications that demand high throughput and minimal latency.

  • Feature Set: The features provided by HTTP clients can differ significantly. Advanced features such as connection pooling, timeout management, and support for various authentication mechanisms can simplify development and improve the robustness of the application.

  • Ease of Use: A well-designed API can drastically reduce the complexity of coding HTTP interactions. Clients that offer intuitive and straightforward APIs enable developers to write cleaner, more maintainable code.

  • Compatibility and Flexibility: Ensuring compatibility with existing frameworks and libraries is vital. Some clients offer better integration with popular Java frameworks like Spring, making them more suitable for specific projects.

  • Community and Support: A strong community and active development can be invaluable. Clients with robust documentation, frequent updates, and active support forums help developers resolve issues faster and stay up-to-date with the latest advancements.

Several HTTP clients are available for Java, each with its strengths and ideal use cases. Here’s an overview of some of the most prominent ones:

Java 11 HttpClient

Overview

Introduced in Java 11, the java.net.http.HttpClient is a modern, feature-rich HTTP client that provides a simple API for sending HTTP requests and handling responses. It supports both synchronous and asynchronous operations and is designed to handle HTTP/1.1 and HTTP/2 protocols, making it suitable for a wide range of applications. **Strength is that is **Native support in the JDK and ease of use. It is perfect for projects that require minimal dependencies and leverage the latest Java features.

Key Features

  • Asynchronous and Synchronous Requests: Supports both blocking (synchronous) and non-blocking (asynchronous) operations.

  • HTTP/2 Support: Out-of-the-box support for HTTP/2, allowing for multiplexing of multiple requests over a single connection.

  • Reactive Streams: Integration with Java’s reactive streams API for handling asynchronous data streams.

  • Built-in Authentication: Supports basic and digest authentication.

Making a Simple GET Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.io.IOException;

public class SimpleGetRequest {
    public static void main(String[] args) {
        // Create an HttpClient instance
        HttpClient client = HttpClient.newHttpClient();

        // Create a HttpRequest
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://api.github.com"))
                .GET()
                .build();

        try {
            // Send the request and get the response
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

            // Print the response status code and body
            System.out.println("Status code: " + response.statusCode());
            System.out.println("Response body: " + response.body());
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Making an Asynchronous POST Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpRequest.BodyPublishers;
import java.util.concurrent.CompletableFuture;

public class AsyncPostRequest {
    public static void main(String[] args) {
        // Create an HttpClient instance
        HttpClient client = HttpClient.newHttpClient();

        // Create a HttpRequest
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://jsonplaceholder.typicode.com/posts"))
                .header("Content-Type", "application/json")
                .POST(BodyPublishers.ofString("{\"title\":\"foo\",\"body\":\"bar\",\"userId\":1}"))
                .build();

        // Send the request asynchronously
        CompletableFuture<HttpResponse<String>> responseFuture = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

        // Handle the response
        responseFuture.thenApply(HttpResponse::body)
                      .thenAccept(System.out::println)
                      .join(); // Wait for the response
    }
}

Apache HttpClient

Overview

Apache HttpClient is a robust and flexible HTTP client library that has been widely used in the Java community for many years. Known for its rich set of features and configurability, it allows developers to handle HTTP communications in a highly customizable manner. It supports advanced features like connection management, cookie handling, and authentication. It is highly configurable and has a large community and extensive documentation. Suitable for applications requiring complex HTTP interactions and fine-grained control over HTTP operations.

Key Features

  • Connection Management: Advanced connection pooling and management for efficient resource use.

  • Authentication: Supports various authentication schemes, including basic, digest, NTLM, and Kerberos.

  • Customizability: Allows fine-grained control over HTTP requests and responses, including custom headers, parameters, and configurations.

  • Retry and Redirection Handling: Built-in mechanisms for retrying failed requests and handling HTTP redirects.

  • Streaming and Compression: Supports streaming of request and response bodies, and handling of compressed content.

Making a Simple GET Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
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 SimpleGetRequest {
    public static void main(String[] args) {
        // Create a CloseableHttpClient instance
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            // Create an HttpGet request
            HttpGet request = new HttpGet("https://api.github.com");

            // Execute the request and get the response
            try (CloseableHttpResponse response = httpClient.execute(request)) {
                // Get the response entity
                HttpEntity entity = response.getEntity();

                // Print the response status and body
                System.out.println("Status code: " + response.getStatusLine().getStatusCode());
                if (entity != null) {
                    System.out.println("Response body: " + EntityUtils.toString(entity));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Making a POST Request with JSON Payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
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 PostRequestWithJson {
    public static void main(String[] args) {
        // Create a CloseableHttpClient instance
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            // Create an HttpPost request
            HttpPost post = new HttpPost("https://jsonplaceholder.typicode.com/posts");

            // Set the request headers
            post.setHeader("Content-Type", "application/json");

            // Set the request payload
            String json = "{\"title\":\"foo\",\"body\":\"bar\",\"userId\":1}";
            post.setEntity(new StringEntity(json));

            // Execute the request and get the response
            try (CloseableHttpResponse response = httpClient.execute(post)) {
                // Print the response status and body
                System.out.println("Status code: " + response.getStatusLine().getStatusCode());
                System.out.println("Response body: " + EntityUtils.toString(response.getEntity()));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

RestTemplate (Spring)

Overview

RestTemplate is part of the Spring Framework and provides a synchronous client for consuming RESTful web services. It’s designed to work seamlessly within the Spring ecosystem, offering a variety of utility methods to simplify HTTP interactions. Seamless integration with Spring applications, extensive utility methods for various HTTP methods, and robust error handling. Best usage is for Spring-based applications needing a straightforward, synchronous HTTP client.

Key Features

  • Simplicity and Ease of Use: Offers straightforward methods for common HTTP operations (GET, POST, PUT, DELETE).

  • Integration with Spring: Works well with other Spring components, including Spring Boot, for configuration and dependency injection.

  • Error Handling: Built-in mechanisms for handling various HTTP errors and exceptions.

  • Message Converters: Supports automatic conversion of HTTP responses to Java objects and vice versa using various message converters (e.g., JSON, XML).

Making a Simple GET Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import org.springframework.web.client.RestTemplate;
import org.springframework.http.ResponseEntity;

public class SimpleGetRequest {
    public static void main(String[] args) {
        // Create a RestTemplate instance
        RestTemplate restTemplate = new RestTemplate();

        // Define the URL
        String url = "https://api.github.com";

        // Make the GET request and get the response
        ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);

        // Print the response status code and body
        System.out.println("Status code: " + response.getStatusCode());
        System.out.println("Response body: " + response.getBody());
    }
}

Making a POST Request with JSON Payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import org.springframework.web.client.RestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

import java.util.HashMap;
import java.util.Map;

public class PostRequestWithJson {
    public static void main(String[] args) {
        // Create a RestTemplate instance
        RestTemplate restTemplate = new RestTemplate();

        // Define the URL
        String url = "https://jsonplaceholder.typicode.com/posts";

        // Create the request headers
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        // Create the request payload
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("title", "foo");
        requestBody.put("body", "bar");
        requestBody.put("userId", 1);

        // Wrap the payload in an HttpEntity
        HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);

        // Make the POST request and get the response
        ResponseEntity<String> response = restTemplate.postForEntity(url, requestEntity, String.class);

        // Print the response status code and body
        System.out.println("Status code: " + response.getStatusCode());
        System.out.println("Response body: " + response.getBody());
    }
}

WebClient (Spring WebFlux)

Overview

WebClient is part of the Spring WebFlux framework and provides a reactive, non-blocking alternative to RestTemplate. It’s designed for modern applications that require high concurrency and low-latency interactions, leveraging Project Reactor for its reactive programming model. Supports reactive programming paradigms, making it suitable for high-throughput and low-latency applications. Ideal for applications that benefit from reactive programming and require non-blocking IO operations.

Key Features

  • Reactive and Non-blocking: Built for reactive programming, allowing non-blocking HTTP requests and responses.

  • HTTP/2 Support: Supports HTTP/2, enabling efficient use of network resources.

  • Functional API: Provides a fluent API for building and executing requests, making the code more readable and maintainable.

  • Integration with Spring: Works seamlessly with other Spring WebFlux components and Project Reactor.

Making a Simple GET Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

public class SimpleGetRequest {
    public static void main(String[] args) {
        // Create a WebClient instance
        WebClient webClient = WebClient.create("https://api.github.com");

        // Make the GET request and get the response
        Mono<String> response = webClient.get()
                                         .retrieve()
                                         .bodyToMono(String.class);

        // Subscribe to the response and print the body
        response.subscribe(System.out::println);
    }
}

Making a POST Request with JSON Payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.http.MediaType;
import reactor.core.publisher.Mono;

public class PostRequestWithJson {
    public static void main(String[] args) {
        // Create a WebClient instance
        WebClient webClient = WebClient.create("https://jsonplaceholder.typicode.com");

        // Define the request payload
        String requestBody = "{\"title\":\"foo\",\"body\":\"bar\",\"userId\":1}";

        // Make the POST request and get the response
        Mono<String> response = webClient.post()
                                         .uri("/posts")
                                         .contentType(MediaType.APPLICATION_JSON)
                                         .bodyValue(requestBody)
                                         .retrieve()
                                         .bodyToMono(String.class);

        // Subscribe to the response and print the body
        response.subscribe(System.out::println);
    }
}

OkHttp

Overview

OkHttp is a modern, efficient HTTP client developed by Square. Known for its performance and ease of use, it provides a powerful, flexible API for managing HTTP connections and requests. Efficient connection pooling, HTTP/2 support, and built-in caching mechanisms. It’s lightweight and easy to integrate with other libraries. Ideal for mobile and web applications where performance and simplicity are paramount.

Key Features

  • Connection Pooling: Reuses HTTP/1.1 and HTTP/2 connections to reduce latency.

  • HTTP/2 and WebSocket Support: Supports HTTP/2 and WebSocket, enabling efficient and persistent connections.

  • Asynchronous and Synchronous Requests: Supports both blocking and non-blocking requests.

  • Built-in Caching: Provides automatic response caching to improve performance and reduce network load.

  • Interceptors: Allows custom logic to be inserted into request and response processing.

Making a Simple GET Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;

public class SimpleGetRequest {
    public static void main(String[] args) {
        // Create an OkHttpClient instance
        OkHttpClient client = new OkHttpClient();

        // Create a request
        Request request = new Request.Builder()
                .url("https://api.github.com")
                .build();

        // Execute the request and get the response
        try (Response response = client.newCall(request).execute()) {
            // Print the response status code and body
            System.out.println("Status code: " + response.code());
            System.out.println("Response body: " + response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Making an Asynchronous POST Request with JSON Payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import okhttp3.*;

import java.io.IOException;

public class AsyncPostRequest {
    public static void main(String[] args) {
        // Create an OkHttpClient instance
        OkHttpClient client = new OkHttpClient();

        // Define the request payload
        String json = "{\"title\":\"foo\",\"body\":\"bar\",\"userId\":1}";

        // Create a request body
        RequestBody body = RequestBody.create(json, MediaType.get("application/json; charset=utf-8"));

        // Create a request
        Request request = new Request.Builder()
                .url("https://jsonplaceholder.typicode.com/posts")
                .post(body)
                .build();

        // Execute the request asynchronously
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                // Print the response status code and body
                System.out.println("Status code: " + response.code());
                System.out.println("Response body: " + response.body().string());
            }
        });
    }
}

Conclusion

Choosing the right Java HTTP REST client is a critical decision that can significantly impact your application’s performance, maintainability, and scalability. By understanding the strengths and ideal use cases of each client, developers can make informed decisions that align with their project requirements and constraints. Whether you need the extensive features of Apache HttpClient, the modern efficiency of OkHttp, or the native simplicity of Java 11 HttpClient, there’s a Java HTTP client that fits your needs.

This post is licensed under CC BY 4.0 by the author.