How do I send async HTTP requests with HttpClient?

To send asynchronous HTTP requests in Java using the java.net.http.HttpClient, you use the sendAsync() method. This method returns a CompletableFuture<HttpResponse<T>>, allowing you to handle the response without blocking the main thread.

Here is a step-by-step example of how to implement this:

1. Basic Asynchronous GET Request

This example demonstrates how to fire a request and handle the result using thenAccept.

package org.kodejava.httpclient;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;

public class AsyncRequestExample {
    public static void main(String[] args) {
        // 1. Create the HttpClient
        HttpClient client = HttpClient.newHttpClient();

        // 2. Build the HttpRequest
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://jsonplaceholder.typicode.com/posts/1"))
                .GET()
                .build();

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

        // 4. Handle the response when it arrives
        responseFuture.thenAccept(response -> {
            System.out.println("Status Code: " + response.statusCode());
            System.out.println("Response Body: " + response.body());
        }).exceptionally(ex -> {
            System.err.println("Error occurred: " + ex.getMessage());
            return null;
        });

        // The program continues here immediately while the request is in flight
        System.out.println("Request sent! Doing other things...");

        // Optional: Block if you need to wait for the result before the program exits
        responseFuture.join();
    }
}

2. Chaining and Transforming Results

Because sendAsync returns a CompletableFuture, you can chain operations like extracting the body or converting JSON.

CompletableFuture<String> bodyFuture = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
        .thenApply(HttpResponse::body)       // Transform response to just the body
        .thenApply(String::toUpperCase);      // Further transform the string

bodyFuture.thenAccept(System.out::println);

Key Components

  • sendAsync(request, bodyHandler): The non-blocking counterpart to send().
  • HttpResponse.BodyHandlers: Defines how to handle the incoming data (e.g., ofString(), ofByteArray(), or ofFile()).
  • CompletableFuture: Provides methods like .thenApply() (map), .thenAccept() (consume), and .exceptionally() (error handling).

Best Practices

  • Reuse the Client: Don’t create a new HttpClient for every request. It’s designed to be long-lived and shared.
  • Executor Service: By default, HttpClient uses a default executor. For high-load applications, you can provide your own thread pool when building the client:
    HttpClient client = HttpClient.newBuilder()
                .executor(Executors.newFixedThreadPool(10))
                .build();
    
  • Join/Get: In a console application, use .join() or .get() at the very end to prevent the main method from finishing (and the JVM exiting) before the background thread completes.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.