Lesson Completion
Back to course

HTTP Client API: Modern HTTP Communication

Beginner
12 minutes4.6Java

1. The Hook (The "Byte-Sized" Intro)

  • In a Nutshell: HTTP Client API (Java 11+) replaces legacy HttpURLConnection—modern, async-first, HTTP/2 support.
  • Three key classes: HttpClient (reusable client), HttpRequest (request builder), HttpResponse (response).
  • Two modes: Synchronous (send()) blocks, Asynchronous (sendAsync()) returns CompletableFuture.
  • BodyHandlers: ofString(), ofFile(), ofInputStream().
  • Benefits: Fluent builder API, async by default, HTTP/2 multiplexing, WebSocket support.
  • Replace: Apache HttpClient, OkHttp (for simple use cases)!

Think of ordering food. Old HttpURLConnection = phone call (wait on hold, blocking). New HTTP Client = food app (order, do other things, notification when ready). Async-first design!


2. Conceptual Clarity (The "Simple" Tier)

💡 The Analogy

  • Synchronous send(): Waiting in line (blocks until served)
  • Asynchronous sendAsync(): Take number, sit down, called when ready
  • HTTP/2: Multiple orders at once (multiplexing)

3. Technical Mastery (The "Deep Dive")

HTTP Client Components

ComponentPurpose
HttpClientReusable client (configure once, use many times)
HttpRequestRequest configuration (URL, headers, body)
HttpResponseResponse (status, headers, body)
BodyHandlerHow to process response body
BodyPublisherHow to send request body

4. Interactive & Applied Code

java
import java.net.*; import java.net.http.*; import java.net.http.HttpResponse.BodyHandlers; import java.time.Duration; import java.util.concurrent.*; public class HTTPClientDemo { public static void main(String[] args) throws Exception { demonstrateGET(); demonstratePOST(); demonstrateAsync(); demonstrateAdvanced(); } // Basic GET request (synchronous) static void demonstrateGET() throws Exception { System.out.println("=== GET REQUEST ==="); // Create client (reusable) HttpClient client = HttpClient.newHttpClient(); // Build request HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://httpbin.org/get")) .GET() // Default, can omit .build(); // Send synchronously HttpResponse<String> response = client.send( request, BodyHandlers.ofString() ); System.out.println("Status: " + response.statusCode()); System.out.println("Body: " + response.body()); } // POST request with JSON static void demonstratePOST() throws Exception { System.out.println("\n=== POST REQUEST ==="); HttpClient client = HttpClient.newHttpClient(); String jsonBody = """ { "name": "Alice", "age": 30 } """; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://httpbin.org/post")) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); HttpResponse<String> response = client.send( request, BodyHandlers.ofString() ); System.out.println("Status: " + response.statusCode()); System.out.println("Response: " + response.body().substring(0, 100) + "..."); } // Asynchronous request static void demonstrateAsync() throws Exception { System.out.println("\n=== ASYNC REQUEST ==="); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://httpbin.org/delay/2")) .build(); // Send asynchronously (non-blocking!) CompletableFuture<HttpResponse<String>> futureResponse = client.sendAsync(request, BodyHandlers.ofString()); System.out.println("Request sent... doing other work..."); // Chain operations futureResponse .thenApply(HttpResponse::body) .thenAccept(body -> System.out.println("Received: " + body.substring(0, 50))) .join(); // Wait for completion } // Advanced features static void demonstrateAdvanced() throws Exception { System.out.println("\n=== ADVANCED FEATURES ==="); // Custom client configuration HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) // Prefer HTTP/2 .connectTimeout(Duration.ofSeconds(10)) .followRedirects(HttpClient.Redirect.NORMAL) .build(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://httpbin.org/headers")) .timeout(Duration.ofSeconds(5)) .header("User-Agent", "Java HTTP Client") .build(); HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); // Access response details System.out.println("HTTP Version: " + response.version()); System.out.println("Status: " + response.statusCode()); response.headers().map().forEach((k, v) -> System.out.println("Header: " + k + " = " + v)); } } // Real-world examples class RealWorldHTTPExamples { static HttpClient client = HttpClient.newHttpClient(); // REST API call static String fetchUser(int userId) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://jsonplaceholder.typicode.com/users/" + userId)) .build(); HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); return response.body(); } // Multiple async requests (parallel) static void fetchMultiple() { List<CompletableFuture<String>> futures = IntStream.rangeClosed(1, 5) .mapToObj(i -> HttpRequest.newBuilder() .uri(URI.create("https://jsonplaceholder.typicode.com/users/" + i)) .build()) .map(req -> client.sendAsync(req, BodyHandlers.ofString()) .thenApply(HttpResponse::body)) .toList(); // Wait for all CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) .thenRun(() -> System.out.println("All requests complete!")) .join(); } // POST JSON static void createUser(String name, String email) throws Exception { String json = String.format(""" { "name": "%s", "email": "%s" } """, name, email); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://jsonplaceholder.typicode.com/users")) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(json)) .build(); HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); System.out.println("Created: " + response.body()); } }

5. The Comparison & Decision Layer

Old (HttpURLConnection)New (HTTP Client)
Verbose, low-levelFluent builder API
HTTP/1.1 onlyHTTP/2 support
Synchronous onlyAsync-first
Manual configurationSensible defaults
No timeout supportBuilt-in timeouts

6. The "Interview Corner" (The Edge)

The "Killer" Interview Question: "send() vs sendAsync()?" Answer:

  • send(): Blocks current thread until response
  • sendAsync(): Returns CompletableFuture (non-blocking)
java
// send(): Blocks here! HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); System.out.println(response.body()); // Only runs after response // sendAsync(): Doesn't block! CompletableFuture<HttpResponse<String>> future = client.sendAsync(request, BodyHandlers.ofString()); System.out.println("This runs immediately!"); // Doesn't wait future.thenApply(HttpResponse::body) .thenAccept(System.out::println);

Pro-Tip: HTTP/2 multiplexing:

java
// HTTP/2 allows multiple requests over single connection! HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) .build(); // These can share connection (efficient!) client.sendAsync(request1, BodyHandlers.ofString()); client.sendAsync(request2, BodyHandlers.ofString()); client.sendAsync(request3, BodyHandlers.ofString());

Topics Covered

Java Fundamentals

Tags

#java#programming#beginner-friendly

Last Updated

2025-02-01