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.

pkg12restapi/restapi5HttpClientConsume.java
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}