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.

How do I use Java 11 HttpClient to send asynchronous requests?

In Java 11, the HttpClient API provides a modern and user-friendly way to send both synchronous and asynchronous HTTP requests. To send asynchronous requests, you’ll use the sendAsync method, which returns a CompletableFuture.

Here’s how to use it:


Step-by-Step Guide to Sending Asynchronous Requests:

  1. Initialize the HttpClient:
    Use HttpClient to create an instance. This is the central object for sending requests.
  2. Create an HttpRequest:
    Prepare your HTTP request using the HttpRequest class, where you can specify the URI, HTTP method, headers, body, etc.
  3. Send an Asynchronous Request with sendAsync:
    Call the sendAsync method of the HttpClient, passing the request and body handler as arguments. This returns a CompletableFuture, which allows you to perform non-blocking operations.
  4. Process the Response:
    Use the CompletableFuture chain methods, like thenApply and thenAccept, to process the response once it’s available.

Example: Sending an Asynchronous GET Request

package org.kodejava.net.http;

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

public class AsyncHttpClientExample {

    public static void main(String[] args) {
        // Create HttpClient instance
        HttpClient client = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(10)) // Optional timeout
                .build();

        // Prepare HttpRequest
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://jsonplaceholder.typicode.com/posts"))
                .GET()
                .header("Accept", "application/json")
                .build();

        // Send asynchronous request
        CompletableFuture<HttpResponse<String>> futureResponse =
                client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

        // Process response asynchronously
        futureResponse.thenApply(HttpResponse::body) // Extract the response body
                .thenAccept(System.out::println) // Print the body
                .exceptionally(ex -> {
                    System.err.println("Request failed: " + ex.getMessage());
                    return null;
                });

        // Do other tasks while the response is being fetched...
        System.out.println("Request is sent. Waiting for response...");

        // Wait until the response completes to prevent the program from exiting early
        futureResponse.join();
    }
}

Explanation:

  1. HttpClient.newBuilder():
    Creates a new instance of the HttpClient. You can optionally configure timeouts, proxies, or redirect policies.
  2. HttpRequest.newBuilder():
    Creates an HTTP request. You specify the URI, headers, and HTTP method (e.g., GET, POST, etc.).
  3. sendAsync:
    Sends the request asynchronously. It accepts two arguments:

    • The HttpRequest object.
    • A BodyHandler to determine how the HTTP response body should be handled, such as ofString() for plain text.
  4. CompletableFuture Chain:
    • thenApply: Manipulates the asynchronous result as it becomes available.
    • thenAccept: Consumes the result of the future once it’s ready.
    • exceptionally: Handles any exceptions that occur during execution.
  5. join():
    Blocks the main thread until the asynchronous operation is complete (used here to prevent premature termination of the program).

Example: Sending an Asynchronous POST Request with JSON Body

package org.kodejava.net.http;

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

public class AsyncPostExample {

    public static void main(String[] args) {
        // Create HttpClient
        HttpClient client = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(10))
                .build();

        // Prepare JSON body
        String jsonBody = "{ \"title\": \"foo\", \"body\": \"bar\", \"userId\": 1 }";

        // Create HttpRequest
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://jsonplaceholder.typicode.com/posts"))
                .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
                .header("Content-Type", "application/json")
                .build();

        // Send asynchronous POST request
        CompletableFuture<HttpResponse<String>> futureResponse =
                client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

        // Handle response
        futureResponse.thenApply(HttpResponse::body)
                .thenAccept(System.out::println)
                .exceptionally(ex -> {
                    System.err.println("Error: " + ex.getMessage());
                    return null;
                });

        // Keep the program running to wait for response
        System.out.println("POST request sent. Waiting for response...");
        futureResponse.join();
    }
}

Keynotes:

  • Thread-Safe HttpClient:
    The HttpClient instance is thread-safe and can be reused for multiple requests.
  • Non-blocking Nature:
    Asynchronous requests are non-blocking, so you can perform other tasks while waiting for the response.
  • Error Handling:
    Use the exceptionally method of the CompletableFuture to handle any errors during the request.
  • Keepalive:
    By default, HttpClient connections have keepalive enabled. It’s more efficient for high-performance applications.
  • Timeouts:
    Always configure timeouts to prevent indefinite blockage (connectTimeout or read timeouts).

Using this approach, you can efficiently perform asynchronous HTTP communication with HttpClient.

How do I send a simple GET request using Java 11 HttpClient?

In Java 11, you can use the HttpClient to send HTTP requests easily. Here’s how you can send a simple GET request using HttpClient:

Full Example:

package org.kodejava.net.http;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class SimpleGetRequestExample {

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

         // Create the GET request
         HttpRequest request = HttpRequest.newBuilder()
                 .uri(URI.create("https://jsonplaceholder.typicode.com/posts/1")) // Replace with your URL
                 .GET() // Optional since GET is the default
                 .build();

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

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

      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

Explanation:

  1. HttpClient Instance:
    • Use HttpClient.newHttpClient() to create a new HttpClient. You can also customize the client for specific timeouts, authentication, or proxy configurations.
  2. HttpRequest Object:
    • Use HttpRequest.newBuilder() to create a request.
    • Use .uri(URI.create("URL")) to set the target URI.
    • Specify the HTTP method using .GET() (which is the default for HttpRequest).
  3. Send the HTTP Request:
    • Use the httpClient.send(request, HttpResponse.BodyHandlers.ofString()) method.
    • HttpResponse.BodyHandlers.ofString() specifies how the response body should be handled (in this case, as a string).
  4. Process the Response:
    • Access the HTTP response status code using response.statusCode().
    • Access the body using response.body().

Output for the Example:

If the request is successful, you would see the HTTP status code (e.g., 200) and the response body for the given URL printed in the console.

Status code: 200
Response body: {
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}

Alternative: Using Asynchronous Request

If you want to send the request asynchronously:

package org.kodejava.net.http;

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 AsyncGetRequestExample {

   public static void main(String[] args) {
      HttpClient httpClient = HttpClient.newHttpClient();

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

      CompletableFuture<HttpResponse<String>> responseFuture = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString());

      responseFuture.thenAccept(response -> {
         System.out.println("Status code: " + response.statusCode());
         System.out.println("Response body: " + response.body());
      }).join(); // Waits for the asynchronous computation to complete
   }
}

Benefits of Using HttpClient in Java 11:

  • Built-in support for HTTP/1.1 and HTTP/2.
  • Synchronous (send) and asynchronous (sendAsync) request capabilities.
  • Configurable features like timeouts and proxy settings.

By following these examples, you can handle simple GET requests in Java 11 effectively!