Introduction
Based on the 2020 Java Ecosystem survey from Jetbrains, 75% of Java developers are still using JDK 8. JDK 11 introduces HttpClient
, which replaces the legacy HttpURLConnection
API. For this tutorial, we will build a HttpClient
that consumes the free Cat Facts API.
Goals
At the end of this tutorial, you would have learned:
- How to use the
HttpClient
to consume REST APIs.
Prerequisite Knowledge
- Basic Java.
- Good understanding of HTTP protocols.
Tools Required
- Any Java IDE that supports JDK 11 and above.
The Application Entry Point
Our App does not need any special build tool. Follow the steps below to create the main method:
- Create an empty Java project with support for JDK 11+.
- Create a package called
com.example.client
. - Inside the
client
package, create a class calledEntry
. - Add the main method inside
Entry
.
Your Entry.java
file should now look like this:
package com.example.client;
public class Entry {
public static void main(String[] args){
}
}
The CatFactsService Class
Next, we need to create a class for grouping all of the logics for our HttpClient
.
- Inside the same
Entry.java
file, create another top level class calledCatFactsService
withpackage
visibility (empty modifier). - Add a
private final
instance variable with typeHttpClient
(fromjava.net.http
) and identifierhttpClient
. - Add an empty constructor and initialize
httpClient
using the static methodnewHttpClient()
from theHttpClient
class. This will initialize aHttpClient
with default settings, which is sufficient for this tutorial. If you want to obtain an instance ofHttpClient
with custom settings, you can use the static methodnewBuilder()
fromHttpClient
instead.
The CatFactsService
class should now look like this:
class CatFactsService {
private final HttpClient httpClient;
CatFactsService() {
this.httpClient = HttpClient.newHttpClient();
}
}
Preparing for Requests
There are a couple of steps that we will have to follow before sending a simple request:
- Build the request: setting headers, parameters, body content, uri, etc. We will use
HttpRequest.Builder
for this. - Decides whether we want an async or synchronous request, so we can handle the response properly.
a. If the request is synchronous, we would use thesend()
method and assign the response toHttpResponse
.
b. If the request is async, we would use thesendAsync()
method and assign the response toCompletableFuture
. - Whether we want to use a default
HttpResponse.BodyHandler
implementation or a custom implementation.
Retrieving a Random Cat Fact
The first request that we will build is a synchronous GET
request.
Add a private static final String randomFactURI
variable into CatFactsService
. Initialize it with “https://catfact.ninja/fact?max_length=%d”
. This variable serves as a constant for other methods in the class. Hard-coded URIs can change and is not a good practice, so loading String constants from an external source such as the .properties
file is preferred. I am only using hard-coded String
objects to keep the tutorial simple to understand.
private static final String randomFactURI = "https://catfact.ninja/fact?max_length=%d";
Add the getRandomCatFact
method to the CatFactsService
.
public String getRandomCatFact(int maxLength) throws IOException, InterruptedException {
//Creates a new request for sending
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create(String.format(randomFactURI, maxLength)))
.build();
//Gets response with the body as String
HttpResponse<String> res = this.httpClient.send(req, HttpResponse.BodyHandlers.ofString());
return res.body();
}
Here are the explanation for the method above:
- The cat fact API has an endpoint to get a random cat fact, and the only customization we can make is the max length of the cat fact
String
. The max length is set as a request parameter. Our method takes a parameter ofint
, which is the max length that callers can set. - In the
String
constant that you added in the previous step, there is a placeholder%d
String. Based on JavaFormatter
rules, this allows us to insert an integral type into the placeholder String. Theint
parameter would replace%d
. - The first statement that declares
req
and initializes anHttpRequest
object is where we build our request. Since we only need to modify the URI parameters, we would call theuri()
method to set our custom URI. - The second statement is where we send a synchronous request and assigns the response to the
HttpResponse
object. - Because the API returns the cat fact as a json object in the body of the response, we need to return the body of the response to our caller.
Use the CatFactsService
In the main
method, we need to initialize a CatFactsService
instance, and then use the getRandomCatFact
method to get random cat fact. Here is the content of the main method:
var service = new CatFactsService();
String jsonCatFact1 = service.getRandomCatFact(70);
String jsonCatFact2 = service.getRandomCatFact(140);
System.out.println(jsonCatFact1);
System.out.println(jsonCatFact2);
After running main
, you will receive two json objects (as String
). Here are what I received:
{"fact":"Neutering a cat extends its life span by two or three years.","length":60}
{"fact":"Many cats love having their forehead gently stroked.","length":52}
Solution Code
package com.example.client;
import java.io.IOException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.URI;
import java.net.http.HttpResponse;
public class Entry {
public static void main(String[] args) throws IOException, InterruptedException {
var service = new CatFactsService();
String jsonCatFact1 = service.getRandomCatFact(70);
String jsonCatFact2 = service.getRandomCatFact(140);
System.out.println(jsonCatFact1);
System.out.println(jsonCatFact2);
}
}
class CatFactsService {
private final HttpClient httpClient;
private static final String randomFactURI = "https://catfact.ninja/fact?max_length=%d";
CatFactsService() {
this.httpClient = HttpClient.newHttpClient();
}
public String getRandomCatFact(int maxLength) throws IOException, InterruptedException {
//Creates a new request for sending
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create(String.format(randomFactURI, maxLength)))
.build();
//Gets response with the body as String
HttpResponse<String> res = this.httpClient.send(req, HttpResponse.BodyHandlers.ofString());
return res.body();
}
}
Summary
The project that we created is a good starting point if you want to create an HTTP client to consume REST APIs. As for why HttpClient
was introduced in JDK 11, you can check out JEP 110 and JEP 321.
The project source code can be found here: https://github.com/dmitrilc/DaniWebJavaHttpClient