1. Overview

Apache HttpClient provides extensive functionality to work with the HTTP resources. Hence it is one of the most used HTTP clients in the Java ecosystem.

In this tutorial, we'll look at how we can use Apache HttpClient 4 to perform HTTP requests.

2. Maven Dependency

Let's start with adding the httpclient dependency.

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.8</version>
</dependency>

3. Executing GET Requests

Now, we'll first execute a GET request using Apache HttpClient.

public void executeGet() throws Exception {
    try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
        final HttpGet httpGet = new HttpGet(GET_URL);
        
        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            StatusLine statusLine = response.getStatusLine();
            System.out.println(statusLine.getStatusCode() + " " + statusLine.getReasonPhrase());
            String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            System.out.println("Response body: " + responseBody);
        }
    }
}

Here we're creating an instance of CloseableHttpClient. This is the first step for all HTTP operations. Then we're creating an HTTP GET request using the HttpGet class. Notice that we're passing the URL to the constructor of HttpGet. After this step, we're ready to execute our HTTP request. Then we're calling httpClient.execute(httpGet) to get a response back. Now that we have the response, we can apply our business logic. In our case, we're just printing the status code and the response body.

Notice that we've defined CloseableHttpClient within a try-with-resources block. So when the method completes, our HttpClient will be closed automatically. Similarly, we've also defined CloseableHttpResponse within a try-with-resources block. This usage ensures that we release the system resources associated with HttpClient and HttpResponse.

4. Executing POST Requests

Secondly, we'll see how we can execute a POST request with Apache HttpClient.

public void executePost() throws Exception {
    try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
        final HttpPost httpPost = new HttpPost(GET_URL);
        final List<NameValuePair> nameValuePairs = new ArrayList<>();
        nameValuePairs.add(new BasicNameValuePair("name", "John"));
        nameValuePairs.add(new BasicNameValuePair("message", "Hello"));
        httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

        try (final CloseableHttpResponse response = httpClient.execute(httpPost)) {
            StatusLine statusLine = response.getStatusLine();
            System.out.println(statusLine.getStatusCode() + " " + statusLine.getReasonPhrase());
            String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            System.out.println("Response body: " + responseBody);
        }
    }
}

As we can see, the flow is very similar to the execution of GET requests. The changing part is the construction of the POST request. Here, we're first creating a list of NameValuePairs and constructing a UrlEncodedFormEntity instance. Apache HttpClient will send this entity with the request.

Notice that in our case the content type is application/x-www-form-urlencoded, but we can also send requests with other content types.

5. Constructing HttpClient

Now that we've seen the general flow, we'll look at next to the specific steps. Firstly, we'll investigate the construction of HttpClient.

5.1. Default HttpClient

In our previous examples, we've used the default HttpClient. The HttpClients class serves as a static factory for creating the HttpClient instances:

CloseableHttpClient httpClient = HttpClients.createDefault()

Here, we're getting the default instance.

5.2. Constructing HttpClient with Builder

Alternatively, we'll use the HttpClientBuilder class to fine-tune our client instance:

CloseableHttpClient httpClient = HttpClientBuilder
  .create()
  .setDefaultRequestConfig(RequestConfig.custom().setMaxRedirects(1).build())
  .build();

By using the HttpClientBuilder class, we're providing the RequestConfig instance. There are also other options we can configure by using HttpClientBuilder.

6.  Constructing Http Request

So far in our examples, we've created the request object and provided the URL as a single String value. But there are other options we can use to create a request.

6.1. Constructing URI

Let's first look at constructing URIs using the URIBuilder class:

public void constructUri() throws Exception {
    URI uri = new URIBuilder()
      .setScheme("https")
      .setHost(Constants.HOSTNAME)
      .setPath("/get")
      .setParameter("city", "London")
      .setParameter("count", "100")
      .build();
}

Here, the resulting URI will be https://HOSTNAME/get?city=London&count=100. After this construction, we can pass this URI to a request constructor.

6.2. Constructing Http Request

Similar to URIs, we can also build HTTP requests using the RequestBuilder class:

public void constructRequest() {
    HttpUriRequest getRequest = RequestBuilder.get()
      .setUri(GET_URL)
      .addParameter("city", "London")
      .addParameter("count", "100")
      .build();
}

Here, we're generating the same URI as the previous example. Additionally, we're creating a GET request that can be executed by a HttpClient instance.

7. Consuming Http Response

Now, let's look at in detail how we can consume a response.

7.1. Checking Response Status Code

Firstly, we'll look at checking the response status codes.

As we saw previously, we get CloseableHttpResponse as the response after executing the request. Additionally, CloseableHttpResponse returns the StatusLine object which we can use for checking the response codes:

public void handleStatusCodes() throws Exception {
    try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
        final HttpGet httpGet = new HttpGet(GET_URL);
        httpGet.addHeader("HttpClient-Header", "test");
        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            StatusLine statusLine = response.getStatusLine();
            if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
                System.out.println("Response is not OK");
                EntityUtils.consumeQuietly(response.getEntity());
            }
            String responseBody = EntityUtils.toString(response.getEntity());
            System.out.println("Response body: " + responseBody);
        }
    }
}

Here, we're getting the StatusLine object from the response and invoking the getStatusCode() method. This method returns the raw status code - 200, 400, 404.

7.2. Reading Response Body

Secondly, let's see how we can read the response body.

For this purpose, the EntityUtils class provides us helpful methods:

try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
    StatusLine statusLine = response.getStatusLine();
    if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
        System.out.println("Response is not OK");
        EntityUtils.consumeQuietly(response.getEntity());
    }
    String responseBody = EntityUtils.toString(response.getEntity());
    System.out.println("Response body: " + responseBody);
}

Here, we're using two different EntityUtils methods to consume the response body. If the response is OK, we're converting the response body to String using EntityUtils.toString(). However, if the response is not OK, we still need to operate on the response. In our case, we're calling EntityUtils.consumeQuitely() to release the connection resources.

Beware that we should always consume the response, even if it is not OK - 200, 201, etc.

8. Summary

In this tutorial, we've provided a quick guide for the Apache HttpClient 4.

Finally, check out the source code for all examples over on Github.