Micro Java frameworks, like Spark, often lack a built-in HTTP client, so if your code need to access and consume an external API - bring your own client, like Apache, Okhttp or Unirest. This goes same way for [rare] non-web Java apps. Of course, there is HttpURLConnection - available since Java 1.0 - old, low-level and not feature-rich, but...
Java 9 introduced - in an incubator mode - and Java 11 introduced finally - new built-in HTTP client. Is it worth to use?
In this post we would see how to add HTTP Client to your app and how to proceed HTTP requests synchronously/asynchronously.
Overview of Java 11 HTTP Client
The new Java HTTP client is available as a part of java.net.http package and has three core components:
- java.net.HttpClient - a client itself, used to send requests and retrieve response. Created via builder
- java.net.HttpRequest - an HTTP request, created via builder
- java.net.HttpResponse - a response. Not created directly, but is a result of sending an HttpRequest.
It is worth to mention that now Java supports out-of-the-box both synchronous and asynchronous modes, has HTTP/2 + legacy HTTP/1 support and has WebSocket client as well. By the way, this is a good and simple way to test web servers in Java.
HTTP requests
Before we could do requests, we should obtain HttpClient instance:
/* create with default settings */
var defaultClient = HttpClient.newHttpClient();
/* create a custom client */
var customClient = HttpClient.newBuilder()
.followRedirects(Redirect.SAME_PROTOCOL)
.authenticator(Authenticator.getDefault())
.connectTimeout(Duration.ofSeconds(30))
.priority(1)
.version(HttpClient.Version.HTTP_2)
.build();
There are two ways to create HttpClient - with default settings or with custom parameters. The complete list of parameters lives in java docs, but here are some most common parameters:
- version = default value is HTTP/2, but if you need it, you can explicitly set HTTP/1.1
- connectTimeout = determines how long the client waits until a connection can be established
- followRedirects = a default value is NEVER.
Next step is to create requests. Let see how to work with them in both sync and async ways:
Send GET in sync way
Send requests synchronously is a simple and straightforward process. We create HttpRequest and then send it via HttpClient and obtain HttpResponse as a result. Take a look on a code snippet, it is pretty self-explanatory:
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder().GET().uri(URI.create("https://jsonplaceholder.typicode.com/posts/1")).build();
We use JSONPlaceholder - fake online REST API for testing. Note, that both client and request are immutable and we can reuse them for executing multiple times. First, let see how to get data in synchronous manner:
//sync
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String body = response.body();
System.out.println(body);
Send GET in async way
A possibility to execute requests in asynchronous way - is one of best innovations of new API. Actually, such operations should be performed in a non-blocking manner. Let see the code first:
//async
var future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
String body2 = future.thenApply(HttpResponse::body).get();
System.out.println(body2);
API uses CompletableFuture - that may be explicitly completed - in order to send request asynchronously. After execution we can retrieve a data or use lambda expression to do something with response, for instance:
future.thenAccept(this::processResponse);
//...
void processResponse(HttpResponse<String> response){
//do something
}
Send POST in sync way
Now, let try to send POST request. Differences are that we need to specify body and set POST method:
//...client
//body
String body = "{}"
//create a request
var request = HttpRequest.newBuilder()
.POST(HttpRequest.BodyPublishers.ofString(body))
.uri(URI.create("https://jsonplaceholder.typicode.com/posts")).build();
Now we simple send POST request and retrieve its status code:
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
int code1 = response.statusCode();
System.out.printf("SYNC Status code: %d",code1);
Send POST in async way
Much better idea is to send HTTP requests in asynchronous way:
var future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
int code2 = future.get().statusCode();
System.out.printf("ASYNC Status code: %d",code2);
As you can note, the principle is same as with GET (or any other) method. We create CompletableFuture and then get a data from obtained HttpResponse result.
Conclusion
In this short post we observed what Java 11 HttpClient is and how to use it: create, send requests in sync/async ways. It is a quite powerful tool, you could use in your applications, when you need to work with API.
By the way, you can also read my other posts on JVM-related topics in my blog :)
Top comments (2)
Hello,
Thanks for the small introduction, it is really fast and straight to the point.
I noticed a mistake and wanted to share it, so that you can fix it.
Section "Send GET in async way" second code example, line 1 should be:
The change is 'thenApply' -> 'thenAccept' as the referenced method "processResponse" is a Consumer, not a Function.
Cheers,
Genadi
Hi, Genadi! Thanks for your correction! I really appreciate feedback