Course – LS (cat=HTTP Client-Side)

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

Unirest is a lightweight HTTP client library from Mashape. Along with Java, it’s also available for Node.js, .Net, Python, Ruby, etc.

Before we jump in, note that we’ll use mocky.io for all our HTTP requests here.

2. Maven Setup

To get started, let’s add the necessary dependencies first:

<dependency>
    <groupId>com.mashape.unirest</groupId>
    <artifactId>unirest-java</artifactId>
    <version>1.4.9</version>
</dependency>

Check out the latest version here.

3. Simple Requests

Let’s send a simple HTTP request, to understand the semantics of the framework:

@Test
public void shouldReturnStatusOkay() {
    HttpResponse<JsonNode> jsonResponse 
      = Unirest.get("http://www.mocky.io/v2/5a9ce37b3100004f00ab5154")
      .header("accept", "application/json").queryString("apiKey", "123")
      .asJson();

    assertNotNull(jsonResponse.getBody());
    assertEquals(200, jsonResponse.getStatus());
}

Notice that the API is fluent, efficient and quite easy to read.

We’re passing headers and parameters with the header() and fields() APIs.

And the request gets invoked on the asJson() method call; we also have other options here, such as asBinary(), asString() and asObject().

To pass multiple headers or fields, we can create a map and pass them to .headers(Map<String, Object> headers) and .fields(Map<String, String> fields) respectively:

@Test
public void shouldReturnStatusAccepted() {
    Map<String, String> headers = new HashMap<>();
    headers.put("accept", "application/json");
    headers.put("Authorization", "Bearer 5a9ce37b3100004f00ab5154");

    Map<String, Object> fields = new HashMap<>();
    fields.put("name", "Sam Baeldung");
    fields.put("id", "PSP123");

    HttpResponse<JsonNode> jsonResponse 
      = Unirest.put("http://www.mocky.io/v2/5a9ce7853100002a00ab515e")
      .headers(headers).fields(fields)
      .asJson();
 
    assertNotNull(jsonResponse.getBody());
    assertEquals(202, jsonResponse.getStatus());
}

3.1. Passing Query Params

To pass data as a query String, we’ll use the queryString() method:

HttpResponse<JsonNode> jsonResponse 
  = Unirest.get("http://www.mocky.io/v2/5a9ce37b3100004f00ab5154")
  .queryString("apiKey", "123")

3.2. Using Path Params

For passing any URL parameters, we can use the routeParam() method:

HttpResponse<JsonNode> jsonResponse 
  = Unirest.get("http://www.mocky.io/v2/5a9ce37b3100004f00ab5154/{userId}")
  .routeParam("userId", "123")

The parameter placeholder name must be same as the first argument to the method.

3.3. Requests With Body

If our request requires a string/JSON body, we pass it using the body() method:

@Test
public void givenRequestBodyWhenCreatedThenCorrect() {

    HttpResponse<JsonNode> jsonResponse 
      = Unirest.post("http://www.mocky.io/v2/5a9ce7663100006800ab515d")
      .body("{\"name\":\"Sam Baeldung\", \"city\":\"viena\"}")
      .asJson();
 
    assertEquals(201, jsonResponse.getStatus());
}

3.4. Object Mapper

In order to use the asObject() or body() in the request, we need to define our object mapper. For simplicity, we’ll use the Jackson object mapper.

Let’s first add the following dependencies to pom.xml:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.4</version>
</dependency>

Always use the latest version over on Maven Central.

Now let’s configure our mapper:

Unirest.setObjectMapper(new ObjectMapper() {
    com.fasterxml.jackson.databind.ObjectMapper mapper 
      = new com.fasterxml.jackson.databind.ObjectMapper();

    public String writeValue(Object value) {
        return mapper.writeValueAsString(value);
    }

    public <T> T readValue(String value, Class<T> valueType) {
        return mapper.readValue(value, valueType);
    }
});

Note that setObjectMapper() should only be called once, for setting the mapper; once the mapper instance is set, it will be used for all request and responses.

Let’s now test the new functionality using a custom Article object:

@Test
public void givenArticleWhenCreatedThenCorrect() {
    Article article 
      = new Article("ID1213", "Guide to Rest", "baeldung");
    HttpResponse<JsonNode> jsonResponse 
      = Unirest.post("http://www.mocky.io/v2/5a9ce7663100006800ab515d")
      .body(article)
      .asJson();
 
    assertEquals(201, jsonResponse.getStatus());
}

4. Request Methods

Similar to any HTTP client, the framework provides separate methods for each HTTP verb:

POST:

Unirest.post("http://www.mocky.io/v2/5a9ce7663100006800ab515d")

PUT:

Unirest.put("http://www.mocky.io/v2/5a9ce7663100006800ab515d")

GET:

Unirest.get("http://www.mocky.io/v2/5a9ce7663100006800ab515d")

DELETE:

Unirest.delete("http://www.mocky.io/v2/5a9ce7663100006800ab515d")

PATCH:

Unirest.patch("http://www.mocky.io/v2/5a9ce7663100006800ab515d")

OPTIONS:

Unirest.options("http://www.mocky.io/v2/5a9ce7663100006800ab515d")

5. Response Methods

Once we get the response, let check the status code and status message:

//...
jsonResponse.getStatus()

//...

Extract the headers:

//...
jsonResponse.getHeaders();
//...

Get the response body:

//...
jsonResponse.getBody();
jsonResponse.getRawBody();
//...

Notice that, the getRawBody(), returns a stream of the unparsed response body, whereas the getBody() returns the parsed body, using the object mapper defined in the earlier section.

6. Handling Asynchronous Requests

Unirest also has the capability to handle asynchronous requests – using java.util.concurrent.Future and callback methods:

@Test
public void whenAysncRequestShouldReturnOk() {
    Future<HttpResponse<JsonNode>> future = Unirest.post(
      "http://www.mocky.io/v2/5a9ce37b3100004f00ab5154?mocky-delay=10000ms")
      .header("accept", "application/json")
      .asJsonAsync(new Callback<JsonNode>() {

        public void failed(UnirestException e) {
            // Do something if the request failed
        }

        public void completed(HttpResponse<JsonNode> response) {
            // Do something if the request is successful
        }

        public void cancelled() {
            // Do something if the request is cancelled
        }
        });
 
    assertEquals(200, future.get().getStatus());
}

The com.mashape.unirest.http.async.Callback<T> interface provides three methods, failed(), cancelled() and completed().

Override the methods to perform the necessary operations depending on the response.

7. File Uploads

To upload or send a file as a part of the request, pass a java.io.File object as a field with name file:

@Test
public void givenFileWhenUploadedThenCorrect() {

    HttpResponse<JsonNode> jsonResponse = Unirest.post(
      "http://www.mocky.io/v2/5a9ce7663100006800ab515d")
      .field("file", new File("/path/to/file"))
      .asJson();
 
    assertEquals(201, jsonResponse.getStatus());
}

We can also use ByteStream:

@Test
public void givenByteStreamWhenUploadedThenCorrect() {
    try (InputStream inputStream = new FileInputStream(
      new File("/path/to/file/artcile.txt"))) {
        byte[] bytes = new byte[inputStream.available()];
        inputStream.read(bytes);
        HttpResponse<JsonNode> jsonResponse = Unirest.post(
          "http://www.mocky.io/v2/5a9ce7663100006800ab515d")
          .field("file", bytes, "article.txt")
          .asJson();
 
        assertEquals(201, jsonResponse.getStatus());
    }
}

Or use the input stream directly, adding the ContentType.APPLICATION_OCTET_STREAM as the second argument in the fields() method:

@Test
public void givenInputStreamWhenUploadedThenCorrect() {
    try (InputStream inputStream = new FileInputStream(
      new File("/path/to/file/artcile.txt"))) {

        HttpResponse<JsonNode> jsonResponse = Unirest.post(
          "http://www.mocky.io/v2/5a9ce7663100006800ab515d")
          .field("file", inputStream, ContentType.APPLICATION_OCTET_STREAM, "article.txt").asJson();
 
        assertEquals(201, jsonResponse.getStatus());
    }
}

8. Unirest Configurations

The framework also supports typical configurations of an HTTP client like connection pooling, timeouts, global headers etc.

Let’s set the number of connections and number maximum connections per route:

Unirest.setConcurrency(20, 5);

Configure connection and socket timeouts :

Unirest.setTimeouts(20000, 15000);

Note that the time values are in milliseconds.

Now let’s set HTTP headers for all our requests:

Unirest.setDefaultHeader("X-app-name", "baeldung-unirest");
Unirest.setDefaultHeader("X-request-id", "100004f00ab5");

We can clear the global headers anytime:

Unirest.clearDefaultHeaders();

At some point, we might need to make requests through a proxy server:

Unirest.setProxy(new HttpHost("localhost", 8080));

One important aspect to be aware of is closing or exiting the application gracefully. Unirest spawns a background event loop to handle the operations, we need to shut down that loop before exiting our application:

Unirest.shutdown();

9. Conclusion

In this tutorial, we focused on the lightweight HTTP client framework – Unirest. We worked with some simple examples, both in a synchronous but also async modes.

Finally, we also used several advanced configurations – such as connection pooling, proxy settings etc.

As usual, the source code is available over on GitHub.

Course – LS (cat=HTTP Client-Side)

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – HTTP Client (eBook) (cat=Http Client-Side)
Comments are closed on this article!