Examples and Recipes
The following are a number of examples and recipes that can be followed to perform common tasks using the JDK HTTP Client. See here for and introduction to the JDK HTTP Client.
Synchronous Get
Response body as a String
public void get(String uri) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.build();
HttpResponse<String> response =
client.send(request, BodyHandler.asString());
System.out.println(response.body());
}
The above example uses the asString
BodyHandler
to convert the response body bytes into a
String
. A BodyHandler
must be supplied
for each HttpRequest
sent. The
BodyHandler
determines how to handle the response
body, if any.
The BodyHandler
is invoked once the response status
code and headers are available, but before the response body bytes
are received. The BodyHandler
is responsible for
creating the BodySubscriber
which is a reactive-stream
subscriber that receives streams of data with non-blocking back
pressure. The BodySubscriber
is responsible for,
possibly, converting the response body bytes into a higher-level
Java type.
The HttpResponse
class provides a number of
convenience static factory methods for creating a
BodyHandler
. A number of these accumulate the response
bytes in memory until it is completely received, after which it is
converted into the higher-level Java type, for example,
asString
, and asByteArray
. Others stream
the response data as it arrives; asFile
,
asByteArrayConsumer
, and asInputStream
.
Alternatively, a custom written subscriber implementation can be
provided.
Response body as a File
public void get(String uri) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.build();
HttpResponse<Path> response =
client.send(request, BodyHandler.asFile(Paths.get("body.txt")));
System.out.println("Response in file:" + response.body());
}
Asynchronous Get
The asynchronous API returns immediately with a
CompletableFuture
that completes with the
HttpResponse
when it becomes available.
CompletableFuture
was added in Java 8 and supports
composable asynchronous programming.
Response body as a String
public CompletableFuture<String> get(String uri) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.build();
return client.sendAsync(request, BodyHandler.asString())
.thenApply(HttpResponse::body);
}
The CompletableFuture.thenApply(Function)
method
can be used to map the HttpResponse
to its body type,
status code, etc.
Response body as a File
public CompletableFuture<Path> get(String uri) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.build();
return client.sendAsync(request, BodyHandler.asFile(Paths.get("body.txt")))
.thenApply(HttpResponse::body);
}
Post
A request body can be supplied by an
HttpRequest.BodyPublisher
.
public void post(String uri, String data) throws Exception {
HttpClient client = HttpClient.newBuilder().build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.POST(BodyPublisher.fromString(data))
.build();
HttpResponse<?> response = client.send(request, BodyHandler.discard(""));
System.out.println(response.statusCode());
}
The above example uses the fromString
BodyPublisher
to convert the given String
into request body bytes.
The BodyPublisher
is a reactive-stream publisher
that publishes streams of request body on-demand.
HttpRequest.Builder
has a number of methods that allow
setting a BodyPublisher
; Builder::POST
,
Builder::PUT
, and Builder::method
. The
HttpRequest
class has a number of convenience static
factory methods that create a BodyPublisher
for common
types of data; fromString
, fromByteArray
,
fromFile
.
The discard
BodyHandler
can be used to
receive and discard the response body when it is not of
interest.
Concurrent Requests
It's easy to combine Java Streams and the CompletableFuture API to issue a number of requests and await their responses. The following example sends a GET request for each of the URIs in the list and stores all the responses as Strings.
public void getURIs(List<URI> uris) {
HttpClient client = HttpClient.newHttpClient();
List<HttpRequest> requests = uris.stream()
.map(HttpRequest::newBuilder)
.map(reqBuilder -> reqBuilder.build())
.collect(toList());
CompletableFuture.allOf(requests.stream()
.map(request -> client.sendAsync(request, asString()))
.toArray(CompletableFuture<?>[]::new))
.join();
}
Get JSON
In many cases the response body will be in some higher-level format. The convenience response body handlers can be used, along with a third-party library to convert the response body into that format.
The following example demonstrates how to use the Jackson
library, in combination with BodyHandler::asString
to
convert a JSON response into a Map
of String key/value
pairs.
public CompletableFuture<Map<String,String>> JSONBodyAsMap(URI uri) {
UncheckedObjectMapper objectMapper = new UncheckedObjectMapper();
HttpRequest request = HttpRequest.newBuilder(uri)
.header("Accept", "application/json")
.build();
return HttpClient.newHttpClient()
.sendAsync(request, asString())
.thenApply(HttpResponse::body)
.thenApply(objectMapper::readValue);
}
class UncheckedObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper {
/** Parses the given JSON string into a Map. */
Map<String,String> readValue(String content) {
try {
return this.readValue(content, new TypeReference<>(){});
} catch (IOException ioe) {
throw new CompletionException(ioe);
}
}
The above example uses asString
which accumulates
the response body bytes in memory. Alternatively, a streaming
subscriber, like asInputStream
could be used.
Post JSON
In many cases the request body will be in some higher-level format. The convenience request body handlers can be used, along with a third-party library to convert the request body into that format.
The following example demonstrates how to use the Jackson
library, in combination with the
BodyPublisher::fromString
to convert a
Map
of String key/value pairs into JSON.
public CompletableFuture<Void> postJSON(URI uri,
Map<String,String> map)
throws IOException
{
ObjectMapper objectMapper = new ObjectMapper();
String requestBody = objectMapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(map);
HttpRequest request = HttpRequest.newBuilder(uri)
.header("Content-Type", "application/json")
.POST(BodyPublisher.fromString(requestBody))
.build();
return HttpClient.newHttpClient()
.sendAsync(request, BodyHandler.asString())
.thenApply(HttpResponse::statusCode)
.thenAccept(System.out::println);
}
Setting a Proxy
A ProxySelector
can be configured on the
HttpClient
through the client's
Builder::proxy
method. The ProxySelector
API returns a specific proxy for a given URI. In many cases a
single static proxy is sufficient. The
ProxySelector::of
static factory method can be used to
create such a selector.
Response body as a String with a specified proxy
public CompletableFuture<String> get(String uri) {
HttpClient client = HttpClient.newBuilder()
.proxy(ProxySelector.of(new InetSocketAddress("www-proxy.com", 8080)))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.build();
return client.sendAsync(request, asString())
.thenApply(HttpResponse::body);
}
Alternatively, the system-wide default proxy selector can be used, which is the default on macOS.
HttpClient.newBuilder()
.proxy(ProxySelector.getDefault())
.build();