REST
restapi5HttpClientConsume
- Path
- pkg12restapi/restapi5HttpClientConsume.java
- Package
- pkg12restapi
- Study order
- 5
- Run
- Single-file source launch
- Command
- java pkg12restapi/restapi5HttpClientConsume.java
- Dependencies
- com.sun.net.httpserver.HttpServer
There is no in-browser runner. This is the file from the curriculum, unchanged.
1package pkg12restapi;2 3import com.sun.net.httpserver.HttpServer;4import java.net.InetSocketAddress;5import java.net.URI;6import java.net.http.HttpClient;7import java.net.http.HttpRequest;8import java.net.http.HttpResponse;9import java.nio.charset.StandardCharsets;10import java.time.Duration;11import java.util.List;12import java.util.concurrent.CompletableFuture;13 14/*15 * restapi5HttpClientConsume.java16 * ------------------------------17 * Consuming a REST API like a real client: headers, timeouts, status checks,18 * error handling, and concurrent (async) requests.19 *20 * DEFINITION:21 * The flip side of restapi3: how a client app talks to a REST service. We host22 * a tiny server, then call it the way production code would.23 *24 * KEY POINTS:25 * - Reuse one HttpClient; set connectTimeout + per-request timeout.26 * - Check statusCode() and branch on 2xx vs 4xx/5xx.27 * - sendAsync returns CompletableFuture — fan out N calls and join them.28 * - Always send Accept/Content-Type and (when needed) Authorization headers.29 */30public class restapi5HttpClientConsume {31 32 public static void main(String[] args) throws Exception {33 // Stand up a server with a couple of endpoints34 HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);35 server.createContext("/ok", ex -> write(ex, 200, "{\"status\":\"ok\"}"));36 server.createContext("/secure", ex -> {37 String auth = ex.getRequestHeaders().getFirst("Authorization");38 if ("Bearer token123".equals(auth)) write(ex, 200, "{\"data\":\"secret\"}");39 else write(ex, 401, "{\"error\":\"unauthorized\"}");40 });41 server.start();42 String base = "http://127.0.0.1:" + server.getAddress().getPort();43 44 HttpClient client = HttpClient.newBuilder()45 .connectTimeout(Duration.ofSeconds(2))46 .followRedirects(HttpClient.Redirect.NORMAL)47 .build();48 49 // 1) Simple GET with status handling50 HttpResponse<String> ok = client.send(51 HttpRequest.newBuilder(URI.create(base + "/ok"))52 .timeout(Duration.ofSeconds(2))53 .header("Accept", "application/json")54 .GET().build(),55 HttpResponse.BodyHandlers.ofString());56 System.out.println("GET /ok -> " + ok.statusCode());57 System.out.println(ok.statusCode() / 100 == 2 ? " success: " + ok.body()58 : " failure: " + ok.body());59 60 // 2) Auth: without token (401) then with token (200)61 for (String token : new String[]{null, "Bearer token123"}) {62 HttpRequest.Builder b = HttpRequest.newBuilder(URI.create(base + "/secure")).GET();63 if (token != null) b.header("Authorization", token);64 HttpResponse<String> r = client.send(b.build(), HttpResponse.BodyHandlers.ofString());65 System.out.printf("GET /secure (token=%s) -> %d %s%n",66 token == null ? "none" : "yes", r.statusCode(), r.body());67 }68 69 // 3) Concurrent async calls, joined together70 System.out.println("\nFiring 4 async requests:");71 List<CompletableFuture<String>> futures = List.of(0, 1, 2, 3).stream()72 .map(i -> client.sendAsync(73 HttpRequest.newBuilder(URI.create(base + "/ok")).GET().build(),74 HttpResponse.BodyHandlers.ofString())75 .thenApply(resp -> " req#" + i + " -> " + resp.statusCode()))76 .toList();77 CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();78 futures.forEach(f -> System.out.println(f.join()));79 80 server.stop(0);81 }82 83 static void write(com.sun.net.httpserver.HttpExchange ex, int status, String body) throws java.io.IOException {84 byte[] bytes = body.getBytes(StandardCharsets.UTF_8);85 ex.getResponseHeaders().add("Content-Type", "application/json");86 ex.sendResponseHeaders(status, bytes.length);87 ex.getResponseBody().write(bytes);88 ex.close();89 }90}