Any HTTP Client library in Java is a hell to me. Comparing to other technologies like Groovy which have built-in APIs, Java sucks. Projects use Apache HttpClient API for trivial tasks like testing, which I consider as a brain rape because of the usage difficulties. When the HTTP client was introduced and incubated in Java 9 I was like:
Finally, we have our HTTP client, at least for testing!.
Now it’s officially moved to
java.net.http package. I had to try it on my own in the home laboratory.
HTTP client API was de-incubated in Java 11 and guess what? It looks nice.
Recently, I wrote an example newsletter service - which is, by the way, used in this blog. In order to test it, Apache http-commons library (which is an actual better version of Apache’s HttpClient API in every sense) was doing the HTTP work for me. Here is the test code:
This is a pretty simple usage of Apache http-commons API. That’s what I really like - an object of request and a result returned. Wait… Not really - there is a mutable
PostMethod object. It’s not even an actual method but a call, for God’s sake! I hate it a little but it’s still better than normal HttpClient API.
How does the same thing look like in Java 11’s
java.net.http library? Here it is:
Advantages? Fluent API, an actual result (not a mutable request object).
Disadvantages? Lack of urlencoded handling on its own. I had to write an actual
= in the body.
How the new HTTP Client Java API is different from other HTTP clients?
- HTTP/2 support
- WebSocket calls
- async and sync API
The root class of the entire client. You can set up here a connection like proxy, SSL setup, following redirects (or not) and authentication. Because of the good object-oriented design, the HttpClient’s constructor is protected. All configuration parameters and
HttpClient instantiation have to be provided by
HttpClient is also immutable.
The HTTP request is created by
newBuilder methods. The request is also immutable and created through the builder pattern. The main advantage of this approach is using a
copy() method for creating more than one instance of request (however the one request can be used many times) - for example:
Two different methods don’t have to declare the specific header twice. The example is trivial, however more complex expressions like passing through the HTTP builder for some dynamic operations seem legitimate to me.
The other important subclass here is
HttpRequest.BodyPublisher. It feeds body content with some value using (primarily) a
ByteBuffer. The entire concept of subscriber/publisher used in the API is worth exploring and I recommend you to see the documentation about Publishers from
java.util.concurrent package. Software engineers or architects might consider using this concept in their own API instead of homemade sub-pub layers.
Anyway, if one is looking for ready-to-use publishers (no one wants to write one), a few are located in HttpRequest.BodyPublishers class. In my case above, I used
BodyPublishers.ofString(urlEncodedBody). I found this difficult to send form-alike data. It applies especially to url-encoded ones. Providing a key-value map as a body doesn’t work here (by default). The good part of
BodyPublishers methods is a possibility to reuse existing
Flow.Publisher object with
It is the only generic class here. Generic class depends on
HttpResponse.BodyHandler instance provided in
HttpClient::sendAsync. This guy depends on
BodySubscriber class which transforms
List<ByteBuffer> of response body into whatever class is needed.
BodyHandler is the class you need especially when handling asynchronous request - it puts
HttpResponse.ResponseInfo object through this function interface. The method can process or discard the response (because of code fault of whatever other reason software engineer meant). We will look closer to the
BodyHandler in the next blog post.
Should I migrate to the
Tldr; no. Let’s consider two examples:
HTTP Client is used as integration testing tool. Fluent API is the only profit of migration - not sure is your priceless time worth to migrate things around. I don’t think migration to Java 11 is now beneficial though.
Are you using the HTTP client that doesn’t support HTTP/2? Consider the migration in your future plans. The Web is going to use HTTP/2 more and if your code is based on calling multiple unknown websites, the benefits can be significant and worth investing. Most of software engineers don’t have such project, therefore the heuristic is “no, you don’t need to migrate”.
Regardless to the API is not a good idea for an ongoing project. However, for greenfield projects, it might be tempting. The migration can be considered as a good move. I recommend using the
java.net.http library in that case.
Important observation though: all API’s methods are not
getXxx format anymore! It looks like the Java community is already tired of
getRequest, etc. People are already telling me about the
get keyword redundancy. I know it’s bad. The
set method format is a different story though. Aesthetic is not a case in this situation.
There is one thing that I have missed here: asynchronous calls. I am going to publish an async crawler for websites based on Java 11 HTTP Client API in the next Thursday. I published already the Crawler Github repository. Stay tuned!