Overview
The new HttpClient API was introduced in Java 11. It is a replacement for the old HttpURLConnection API which was not suitable for HTTP protocol. This new API uses builder pattern and fluent API to create the required objects to communicate over the Network. It also provides the below features:
- Support for HTTP2 protocol.
- SSL encription.
- Synchronous and Asynchronous communication models.
- Support for HTTP methods.
- Authentication mechanism (Basic).
- Cookies.
The API contains three main classes:
HttClient is used to send multiple requests and receive the responses over the newtwork.
HttpRequest is an ummutable class representing an http request to be sent. It can be configured for a specific HTTP method and append the body if any.
HttpResponse depicts a response comming from the web server. It is returned by the HttpClient when a request is submitted. If the call is asynchronous it returns a CompletableFuture.
The steps are straightforward. First, an instance of HttpClient is created, then the HTTP request to be dispatched. Finally, the request is passed to the HttpClient send methods and a response object is returned (or CompletableFuture if the call is asynchronous).
Use Cases In Action
Without any further delay, let's take a look at some examples:
For this demo, a SpringBoot REST application will be exposing an endpoint (located in http://localhost:8080/api/v1/customers)
that allows to list/add/update/delete customers. Customer is just an ummutable POJO class with a few members. With the help of HttpClient API, we will perform CRUD operations while interacting with the service.
1. Get List of customers
The first scenario is to get a list of all customers. This is just a GET request to the customers resource URL.
HttpClient client = HttpClient
.newBuilder()
.connectTimeout(Duration.ofMillis(500))
.build();
Note that connection will time out if it is not established in half a second. Next the http request object.
HttpRequests request = HttpRequest
.newBuilder()
.uri(URI.create("http://localhost:8080/api/v1/customers"))
.header("Content-Type", "application/json")
.GET()
.build();
Now the communication can be done synchronous, that is, execution is blocked until the response is received.
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.printf("Status %s \n", response.statusCode());
System.out.printf("Response %s \n", response.body());
The BodyHandlers class contains convenient methods to convert the response body data into java objects like a String.
Program Output
Status 200
Response [
{"id":1,"name":"Joe Smith","email":"joe.smith@gmail.com","dateOfBirth":"2008-01-01"},
{"id":2,"name":"Robert Moody","email":"robert.moody@gmail.com","dateOfBirth":"1985-06-21"},
{"id":3,"name":"Jennifer Dolan","email":"jennifer.dolan@gmail.com","dateOfBirth":"1966-11-11"},
{"id":4,"name":"Christopher Farrel","email":"christopher.farrel@gmail.com","dateOfBirth":"1970-04-15"},
{"id":5,"name":"Caroline Red","email":"caroline.red@gmail.com","dateOfBirth":"1992-03-05"}
]
We could send the same request asynchronously invoking the sendAsynch method. This call is non-blocking and it will
return immediately a CompletableFuture.
CompletableFuture<HttpResponse<String>> responseFuture = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
responseFuture
.thenApply(HttpResponse::body)
.thenApply(String::toUpperCase)
.thenAccept(System.out::println)
.join();
In the above pipeline, the body is extracted from the response, uppercased and printed.
Program Output
[
{"ID":1,"NAME":"JOE SMITH","EMAIL":"JOE.SMITH@GMAIL.COM","DATEOFBIRTH":"2008-01-01"},
{"ID":2,"NAME":"ROBERT MOODY","EMAIL":"ROBERT.MOODY@GMAIL.COM","DATEOFBIRTH":"1985-06-21"},
{"ID":3,"NAME":"JENNIFER DOLAN","EMAIL":"JENNIFER.DOLAN@GMAIL.COM","DATEOFBIRTH":"1966-11-11"},
{"ID":4,"NAME":"CHRISTOPHER FARREL","EMAIL":"CHRISTOPHER.FARREL@GMAIL.COM","DATEOFBIRTH":"1970-04-15"},
{"ID":5,"NAME":"CAROLINE RED","EMAIL":"CAROLINE.RED@GMAIL.COM","DATEOFBIRTH":"1992-03-05"}
]
2. Create a new Customer
POST method will be used to create a new customer. The body must be populated with the customer data in JSON format. The BodyPublishers class provides handy methods to convert from java objects into a flow of data for sending as a request body.
HttpRequest request = HttpRequest
.newBuilder()
.uri(URI.create("http://localhost:8080/api/v1/customers"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"Sonia Lamar\",\"email\":\"sonia.lamar@mail.com\",\"dateOfBirth\":\"1998-07-29\"}"))
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.printf("Status %s \n", response.statusCode());
System.out.printf("Location %s \n", response.headers().map().get("location"));
Program Output
Status 201
Location [http://localhost:8080/api/v1/customers/6]
3. Update a new Customer
PUT method will be used to replace entirely an existing customer. That means all fields will be changed except the id. For partial updates, like updating only the email, the PATCH method is more appropiate.
HttpRequest request = HttpRequest
.newBuilder()
.uri(URI.create("http://localhost:8080/api/v1/customers/4"))
.header("Content-Type", "application/json")
.PUT(HttpRequest.BodyPublishers.ofString("{\"name\":\"Victor Martin\",\"email\":\"victor.martin@mail.com\",\"dateOfBirth\":\"1977-04-15\"}"))
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.printf("Status %s \n", response.statusCode());
System.out.printf("Body %s \n", response.body());
Program Output
Status 200
Body {"id":4,"name":"Victor Martin","email":"victor.martin@mail.com","dateOfBirth":"1977-04-15"}
4. Delete a new Customer
Final scenario is to delete the customer which id is 3.
var request = HttpRequest
.newBuilder()
.uri(URI.create("http://localhost:8080/api/v1/customers/3"))
.header("Content-Type", "application/json")
.DELETE()
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.printf("Status %s \n", response.statusCode());
Program Output
Status 204
Conclusion
We learned how to use the Java HttpClient API to consume a REST web service. We performed CRUD operations using the appropiate HTTP methods and inspect the response to verify status, headers and body.
Follow up on the second part of this series and check out how to authenticate to access secured resources. Click here to learn more!
Check the code on GitHub
Top comments (0)