The HTTP Client in Java Networking

The URLConnection class was designed before HTTP was the universal protocol of the Web. It provides support for a number of protocols, but its HTTP support is somewhat cumbersome. When the decision was made to support HTTP/2, it became clear that it would be best to provide a modern client interface instead of reworking the existing API. The HttpCtient provides a more convenient API and HTTP/2 support. In Java 9 and 10, the API classes are located in the jdk.incubator.http package, to give the API a chance to evolve as a result of user feedback. As of Java 11, the HttpCtient is in the java.net.http package.

The HTTP client API provides a simpler mechanism for connecting to a web server than the URLConnection class with its rather fussy set of stages.

An HttpClient can issue requests and receive responses. You get a client by calling

HttpClient client = HttpClient.newHttpCUent()

Alternatively, if you need to configure the client, you use a builder API, like this:

HttpClient client = HttpClient.newBuilder()

.followRedirects(HttpClient.Redirect.ALWAYS)

.build();

That is, you get a builder, call methods to customize the item that is going to be built, and then call the build method to finalize the building process. This is a common pattern for constructing immutable objects.

You also follow the builder pattern for formulating requests. Here is a GET request:

HttpRequest request = HttpRequest.newBuilder()

.uri(new URI(“http://horstmann.com”))

.GET()

.build();

The URI is the “uniform resource identifier” which is, when using HTTP, the same as a URL. However, in Java, the URL class has methods for actually opening a connection to a URL, whereas the URI class is only concerned with the syntax (scheme, host, port, path, query, fragment, and so on).

With a POST request, you need a “body publisher” that turns the request data into the data that is being posted. There are body publishers for strings, byte arrays, and files. For example, if your request is in JSON, you just provide the JSON string to a string body publisher.

HttpRequest request = HttpRequest.newBuitder()

.uri(new URI(urt))

.header(“Content-Type”, “application/json”)

.POST(HttpRequest.BodyPubtishers.ofString(jsonString))

.buitd();

It is unfortunate that the API does not support the required formatting for common content types. The sample program in Listing 4.8 provides body publishers for form data and file uploads.

When sending the request, you have to tell the client how to handle the response. If you just want the body as a string, send the request with a HttpResponse.BodyHandters.ofString(), like this:

HttpResponse<String> response = ctient.send(request,

HttpResponse.BodyHandters.ofString());

The HttpResponse class is a generic type whose type parameter denotes the type of the body. You get the response body string simply as

String bodyString = response.body();

There are other response body handlers that get the response as a byte array or input stream. BodyHandters.ofFite(fitePath) yields a handler that saves the re­sponse to the given file, and BodyHandters.ofFiteDowntoad(directoryPath) saves the response in the given directory, using the file name from the Content-Disposition header. Finally, the handler obtained from BodyHandters.discarding() simply discards the response.

Processing the contents of the response is not considered part of the API. For example, if you receive JSON data, you need a JSON library to parse the contents.

The HttpResponse object also yields the status code and the response headers.

int status = response.statusCode();

HttpHeaders responseHeaders = response.headers();

You can turn the HttpHeaders object into a map:

Map<String, List<String>> headerMap = responseHeaders.map();

The map values are lists since in HTTP, each key can have multiple values.

If you just want the value of a particular key, and you know that there won’t be multiple values, call the firstVatue method:

Optionat<String> tastModified = headerMap.firstVatue(“Last-Modified”);

You get the response value or an empty optional if none was supplied.

You can process the response asynchronously. When building the client, provide an executor:

ExecutorService executor = Executors.newCachedThreadPoolO;

HttpClient client = HttpCtient.newBuitder().executor(executor).buitd();

Build a request and then invoke the sendAsync method on the client. You receive a ComptetabteFuture<HttpResponse<T>>, where T is the type of the body handler. Use the CompletableFuture API as described in Chapter 12 of Volume I:

HttpRequest request = HttpRequest.newBuitder().uri(uri).GET().buitd();

client.sendAsync(request, HttpResponse.BodyHandlers.ofString())

thenAccept(response -> . . .);

Source: Horstmann Cay S. (2019), Core Java. Volume II – Advanced Features, Pearson; 11th edition.

Leave a Reply

Your email address will not be published. Required fields are marked *