How to make requests with Java HTTP Client (vanilla Java)

Felipe Videira
3 min readMar 27, 2023

--

The HTTP Client was introduced in Java 11 (September 2018) to offer a simpler way to perform HTTP requests. The previously available HttpURLConnection API was verbose, complex, and outdated.

The newer API has many useful features, supports synchronous and asynchronous programming, and is compatible with HTTP/1.1 and HTTP/2.

Performing a basic request

An example of performing a GET request:

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com/"))
.build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println("response body: " + response.body());

As the example shows, the HTTP Client is based on the classes HttpClient, HttpRequest, and HttpResponse.

HttpClient creation

To perform a request, a HttpClient instance is necessary. There are two static methods to get it.

The HttpClient.newHttpClient() creates a default HttpClient, which suits most cases.

The HttpClient.newBuilder() returns a builder for more specific settings like HTTP version, redirection policy, a proxy, CookieHandler, HTTP authentication, and more.

HttpClient client = HttpClient.newBuilder()
.followRedirects(Redirect.NORMAL)
.build();

HttpRequest creation

A request to be performed is represented by the HttpRequest class. It is created by the request builder, and with that we define the URI, request method, body, headers, request timeout, and HTTP version.

The HttpRequest.newBuilder() method creates a builder.

Alternatively, there is the HttpRequest.newBuilder(URI uri) which already takes the URI for a shorter code.

Setting a request header

A header can be set with the header(String name, String value) method of the request builder.

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com/api/products"))
.header("Authorization", "Bearer 6f0e91f1-b237-45dc-a462-5b6bd1bf9747")
.build();

Setting the HTTP method and body of a request

To set an HTTP method for a request, the builder has methods with the equivalent name: GET, POST, PUT, and DELETE.

The request body is passed as a parameter in a BodyPublisher. To create a BodyPublisher instance, the BodyPublishers class has static methods like ofString(String body), ofByteArray(byte[] buf), and others. And there is the static method noBody() to perform the request without a body.

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com/api/products"))
.header("Content-Type", "application/json")
.POST(BodyPublishers.ofString("{\"message\": \"Test.\"}"))
.build();

The builder also has the method(String method, BodyPublisher bodyPublisher), which takes the HTTP method name as a String.

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com/api/products/1"))
.header("Content-Type", "application/json")
.method("POST", BodyPublishers.ofString("{\"message\": \"Test.\"}"))
.build();

Request timeout

The HttpRequest builder has the timeout method, which takes the time as a java.time.Duration. The time can be obtained with methods like Duration.ofMinutes(2) and Duration.ofSeconds(45).

The HttpTimeoutException is used when the timeout is reached before the response is received.

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com/api/products"))
.timeout(Duration.ofMinutes(2))
.build();

Without setting a timeout, the client will wait infinitely for a response.

BodyHandler implementations

BodyHandler is an interface that defines how the response body of the request will be managed.

The HttpClient methods that perform the requests take a BodyHandler.

The BodyHandlers class has static methods that offer BodyHandler implementations like ofString(), ofByteArray(), ofInputStream(), and others.

HttpResponse<String> response1 = client.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<byte[]> response2 = client.send(request, HttpResponse.BodyHandlers.ofByteArray());

Asynchronous request

The HttpClient has the sendAsync(HttpRequest request, BodyHandler<T> responseBodyHandler) method, which returns a CompletableFuture and performs the request asynchronously.

CompletableFuture is a class designed for asynchronous programming. It represents the result of an operation performed asynchronously and offers useful methods to handle successful results and errors.

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder(URI.create("https://example.com/api/products")).build();
CompletableFuture<HttpResponse<String>> future = client.sendAsync(request, BodyHandlers.ofString());
future.thenAccept(response -> System.out.println("response body: " + response.body()));

The thenAccept(Consumer<? super T> action) method will execute the received Consumer once the request is finished successfully.

HttpResponse

The HttpResponse is returned when sending the request and has methods to get the response status code, body, and headers, as well as the request data.

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder(URI.create("https://example.com/")).build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println("status code: " + response.statusCode());
System.out.println("headers: " + response.headers());
System.out.println("response body: " + response.body());

Briefer code with static imports

The HTTP Client is much simpler than the old classes and methods, but actually it is still verbose. And used frequently, such as in endpoint test code, the problem gets worse.

The code can become more succinct by using static imports.

var response = newHttpClient().send(newBuilder(URI.create("https://example.com/api/products")).build(),
BodyHandlers.ofString());
System.out.println("response body: " + response.body());

Conclusion

In this guide, we learned how to use the Java HTTP Client to perform requests and its main features.

--

--