How to make requests with Java HTTP Client (vanilla Java)
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.