This is a mobile version, full one is here.
Yegor Bugayenko
11 April 2014
Fluent Java HTTP Client
In the world of Java, there are plenty of HTTP clients from which to choose. Nevertheless, I decided to create a new one because none of the other clients satisfied fully all of my requirements. Maybe, I’m too demanding. Still, this is how my jcabi-http client interacts when you make an HTTP request and expect a successful HTML page in return:
String html = new JdkRequest("https://www.google.com")
.uri().path("/users").queryParam("id", 333).back()
.method(Request.GET)
.header("Accept", "text/html")
.fetch()
.as(RestResponse.class)
.assertStatus(HttpURLConnection.HTTP_OK)
.body();
I designed this new client with the following requirements in mind:
Simplicity
For me, this was the most important requirement. The client must be simple and easy to use. In most cases, I need only to make an HTTP request and parse the JSON response to return a value. For example, this is how I use the new client to return a current EUR rate:
String uri = "https://www.getexchangerates.com/api/latest.json";
String rate = new JdkRequest(uri)
.header("Accept", "application/json")
.fetch()
.as(JsonResponse.class)
.json().readArray().getJsonObject(0)
.getString("EUR");
I assume that the above is easy to understand and maintain.
Fluent Interface
The new client has to be fluent, which means that the entire server interaction fits into one Java statement. Why is this important? I think that fluent interface is the most compact and expressive way to perform multiple imperative calls. To my knowledge, none of the existing libraries enable this type of fluency.
Testable and Extensible
I’m a big fan of interfaces, mostly because they make your designs both cleaner and highly extensible at the same time. In jcabi-http, there are five interfaces extended by 20 classes.
Request
is an interface, as well as
Response
,
RequestURI
,
and
RequestBody
exposed by it.
Use of interfaces makes the library highly extensible. For example, we have
JdkRequest
and
ApacheRequest
,
which make actual HTTP calls to the server using two completely
different technologies: (JDK HttpURLConnection
and Apache HTTP Client, respectively).
In the future, it will be possible to introduce new implementations without breaking existing code.
Say, for instance, I want to fetch a page and then do something with it. These two calls perform the task differently, but the end results are the same:
String uri = "https://www.google.com";
Response page;
page = new JdkRequest(uri).fetch();
page = new ApacheRequest(uri).fetch();
XML and JSON Out-of-the-Box
There are two common standards that I wanted the library to support right out of the box. In most cases, the response retrieved from a server is in either XML or JSON format. It has always been a hassle, and extra work, for me to parse the output to take care of formatting issues.
jcabi-http client supports them both out of the box, and it’s possible to add more formats in the future as needed. For example, you can fetch XML and retrieve a string value from its element:
String name = new JdkRequest("http://my-api.example.com")
.header("Accept", "text/xml")
.fetch()
.as(XmlResponse.class)
.xml().xpath("/root/name/text()").get(0);
Basically, the response produced by fetch()
is decorated by XmlResponse
. This then exposes the xml()
method that returns an instance of the XML
interface.
The same can be done with JSON through the Java JSON API (JSR-353).
None of the libraries that I’m aware of or worked with offer this feature.
Immutable
The last requirement, but certainly not the least important, is that I need all interfaces of the
library to be annotated with @Immutable
.
This is important because I need to be able to encapsulate an instance of Request
in other immutable classes.
ps. A short summary of this article was published at JavaLobby