Everything Java

← Back to blog

Published on Thu Feb 20 2025 14:30:00 GMT+0000 (Coordinated Universal Time) by Purusothaman Ramanujam

Working with HTTP Requests Using Apache HttpClient

Introduction

In modern Java applications, you often need to make HTTP requests to external APIs, web services, or other applications. While Java provides built-in HTTP client capabilities, Apache HttpClient offers a more feature-rich and flexible solution for handling HTTP communications.

Apache HttpClient is a robust, feature-complete HTTP client library that supports HTTP/1.1 and HTTP/2, connection pooling, authentication, and many other advanced features.

What is Apache HttpClient?

Apache HttpClient is a powerful HTTP client library that provides:

Adding Apache HttpClient to Your Project

Maven Dependencies

<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.1</version>
</dependency>

Gradle Dependencies

implementation 'org.apache.httpcomponents.client5:httpclient5:5.2.1'

Basic HTTP Requests

Simple GET Request

Let’s start with a simple GET request:

import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.io.entity.EntityUtils;
public class BasicHttpClientExample {
public static void main(String[] args) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// Create HTTP GET request
HttpGet httpGet = new HttpGet("https://jsonplaceholder.typicode.com/posts/1");
// Execute the request
String response = httpClient.execute(httpGet, response -> {
return EntityUtils.toString(response.getEntity());
});
System.out.println("Response: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}

POST Request with JSON

Here’s how to make a POST request with JSON data:

import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.io.entity.StringEntity;
public class PostRequestExample {
public static void main(String[] args) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
// Create HTTP POST request
HttpPost httpPost = new HttpPost("https://jsonplaceholder.typicode.com/posts");
// Create JSON payload
String jsonPayload = "{\"title\":\"Test Post\",\"body\":\"This is a test post\",\"userId\":1}";
StringEntity entity = new StringEntity(jsonPayload, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// Execute the request
String response = httpClient.execute(httpPost, response -> {
return EntityUtils.toString(response.getEntity());
});
System.out.println("Response: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}

Advanced Configuration

Connection Pooling

HttpClient supports connection pooling for better performance:

import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.core5.util.Timeout;
public class ConnectionPoolingExample {
public static void main(String[] args) {
// Create connection manager with pooling
PoolingHttpClientConnectionManager connectionManager =
PoolingHttpClientConnectionManagerBuilder.create()
.setMaxTotal(100) // Maximum total connections
.setDefaultMaxPerRoute(20) // Maximum connections per route
.build();
// Create HTTP client with connection manager
try (CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.build()) {
HttpGet httpGet = new HttpGet("https://api.example.com/data");
String response = httpClient.execute(httpGet, response -> {
return EntityUtils.toString(response.getEntity());
});
System.out.println("Response: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}

Request Configuration

You can configure various request parameters:

import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.core5.util.Timeout;
public class RequestConfigExample {
public static void main(String[] args) {
// Create request configuration
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(Timeout.ofSeconds(10))
.setResponseTimeout(Timeout.ofSeconds(30))
.build();
try (CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
.build()) {
HttpGet httpGet = new HttpGet("https://api.example.com/data");
String response = httpClient.execute(httpGet, response -> {
return EntityUtils.toString(response.getEntity());
});
System.out.println("Response: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}

Authentication

Basic Authentication

import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.core5.http.HttpHost;
public class BasicAuthExample {
public static void main(String[] args) {
// Create credentials provider
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
new AuthScope("api.example.com", 443),
new UsernamePasswordCredentials("username", "password".toCharArray())
);
try (CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultCredentialsProvider(credentialsProvider)
.build()) {
HttpGet httpGet = new HttpGet("https://api.example.com/protected");
String response = httpClient.execute(httpGet, response -> {
return EntityUtils.toString(response.getEntity());
});
System.out.println("Response: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}

Working with Different Content Types

Form Data

import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.core5.http.io.entity.UrlEncodedFormEntity;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import java.util.Arrays;
public class FormDataExample {
public static void main(String[] args) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost("https://httpbin.org/post");
// Create form data
List<BasicNameValuePair> formData = Arrays.asList(
new BasicNameValuePair("name", "John Doe"),
new BasicNameValuePair("email", "john@example.com"),
new BasicNameValuePair("message", "Hello World")
);
httpPost.setEntity(new UrlEncodedFormEntity(formData));
String response = httpClient.execute(httpPost, response -> {
return EntityUtils.toString(response.getEntity());
});
System.out.println("Response: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}

File Upload

import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.io.entity.FileEntity;
import java.io.File;
public class FileUploadExample {
public static void main(String[] args) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost("https://httpbin.org/post");
// Upload a file
File file = new File("example.txt");
FileEntity fileEntity = new FileEntity(file, ContentType.TEXT_PLAIN);
httpPost.setEntity(fileEntity);
String response = httpClient.execute(httpPost, response -> {
return EntityUtils.toString(response.getEntity());
});
System.out.println("Response: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}

Error Handling

Custom Response Handler

import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpStatus;
public class CustomResponseHandlerExample {
public static void main(String[] args) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet httpGet = new HttpGet("https://api.example.com/data");
String response = httpClient.execute(httpGet, (ClassicHttpResponse httpResponse) -> {
int statusCode = httpResponse.getCode();
if (statusCode == HttpStatus.SC_OK) {
return EntityUtils.toString(httpResponse.getEntity());
} else if (statusCode == HttpStatus.SC_NOT_FOUND) {
throw new RuntimeException("Resource not found");
} else if (statusCode >= 500) {
throw new RuntimeException("Server error: " + statusCode);
} else {
throw new RuntimeException("Unexpected response: " + statusCode);
}
});
System.out.println("Response: " + response);
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
}
}
}

SSL/TLS Configuration

Custom SSL Context

import org.apache.hc.core5.ssl.SSLContextBuilder;
import javax.net.ssl.SSLContext;
public class SSLExample {
public static void main(String[] args) {
try {
// Create custom SSL context
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial(null, (chain, authType) -> true) // Trust all certificates (for testing)
.build();
try (CloseableHttpClient httpClient = HttpClients.custom()
.setSSLContext(sslContext)
.build()) {
HttpGet httpGet = new HttpGet("https://api.example.com/data");
String response = httpClient.execute(httpGet, response -> {
return EntityUtils.toString(response.getEntity());
});
System.out.println("Response: " + response);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

Proxy Support

import org.apache.hc.core5.http.HttpHost;
public class ProxyExample {
public static void main(String[] args) {
// Create proxy configuration
HttpHost proxy = new HttpHost("proxy.example.com", 8080);
try (CloseableHttpClient httpClient = HttpClients.custom()
.setProxy(proxy)
.build()) {
HttpGet httpGet = new HttpGet("https://api.example.com/data");
String response = httpClient.execute(httpGet, response -> {
return EntityUtils.toString(response.getEntity());
});
System.out.println("Response: " + response);
} catch (Exception e) {
e.printStackTrace();
}
}
}

Best Practices

  1. Use Connection Pooling: Always use connection pooling for better performance in production applications.

  2. Handle Resources Properly: Use try-with-resources to ensure HttpClient is properly closed.

  3. Set Timeouts: Always configure appropriate connection and response timeouts.

  4. Handle Errors Gracefully: Implement proper error handling for different HTTP status codes.

  5. Use HTTPS: Always use HTTPS for sensitive data transmission.

  6. Validate Responses: Always validate response status codes and content.

  7. Reuse HttpClient: Create one HttpClient instance and reuse it for multiple requests.

Common Pitfalls

  1. Not Closing HttpClient: Always close HttpClient to free up resources.

  2. Ignoring Status Codes: Don’t assume all responses are successful; check status codes.

  3. Not Setting Timeouts: Without timeouts, requests can hang indefinitely.

  4. Memory Leaks: Be careful with large response entities; always consume them.

  5. Thread Safety: HttpClient is thread-safe, but individual HttpRequest objects are not.

Conclusion

Apache HttpClient is a powerful and flexible HTTP client library that provides everything you need for HTTP communication in Java applications. From simple GET requests to complex scenarios with authentication, SSL, and connection pooling, HttpClient has you covered.

Start with basic requests and gradually explore advanced features like connection pooling, authentication, and custom configurations as your needs grow. Remember to follow best practices for resource management and error handling.

Resources

Written by Purusothaman Ramanujam

← Back to blog