W3docs

Java HttpClient

HTTP-Anfragen in modernem Java mit java.net.http.HttpClient, einschließlich synchroner, asynchroner Aufrufe und HTTP/2-Unterstützung.

java.net.http.HttpClient, in Java 11 standardisiert, ist die moderne HTTP-API und die erste Wahl für neuen Code. Sie ersetzt das umständliche HttpURLConnection durch ein unveränderliches, Builder-basiertes Design: standardmäßig HTTP/2, native synchrone und asynchrone Aufrufe sowie austauschbare Behandlung von Anfrage- und Antwortkörpern. Drei Typen erledigen die Arbeit — HttpClient, HttpRequest und HttpResponse.

Dieses Kapitel behandelt den Aufbau und die Wiederverwendung eines Clients, die Konstruktion von Anfragen mit Körpern und Headern, die synchronen und asynchronen Sendemodi, wie BodyHandlers eine Antwort in den gewünschten Typ umwandeln, sowie die Fallstricke, die Erstanwender treffen. Wenn Sie neu im Bereich Java-Networking sind, beginnen Sie mit der Networking-Einführung; für den hier verwendeten asynchronen Baustein siehe CompletableFuture.

Die drei Typen

HttpClient client = HttpClient.newBuilder()
        .version(HttpClient.Version.HTTP_2)       // HTTP/2, falling back to 1.1
        .connectTimeout(Duration.ofSeconds(10))
        .followRedirects(HttpClient.Redirect.NORMAL)
        .build();

Ein einzelner HttpClient ist thread-sicher und wiederverwendbar — erstellen Sie einen und teilen Sie ihn in der gesamten Anwendung; erstellen Sie keinen pro Anfrage. Von ihm aus senden Sie HttpRequest-Objekte und erhalten HttpResponse-Objekte zurück.

Eine Anfrage erstellen

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://example.com/api"))
        .header("Accept", "application/json")
        .timeout(Duration.ofSeconds(5))
        .POST(HttpRequest.BodyPublishers.ofString("{\"x\":1}"))
        .build();

Die Verbmethode (GET(), POST(...), PUT(...), DELETE()) wird im Builder gewählt. Ein BodyPublisher liefert den Anfragekörper — ofString, ofByteArray, ofFile oder noBody(). Anfragen sind nach dem Erstellen unveränderlich und können wiederverwendet werden.

Senden: synchron und asynchron

// Blocking
HttpResponse<String> resp =
        client.send(request, HttpResponse.BodyHandlers.ofString());

// Non-blocking — returns a CompletableFuture
CompletableFuture<HttpResponse<String>> future =
        client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

Ein BodyHandler entscheidet, wie der Antwortkörper materialisiert wird: ofString(), ofByteArray(), ofFile(path), ofLines() (ein Stream<String>) oder discarding(). sendAsync gibt ein CompletableFuture zurück, sodass Sie .thenApply, .thenAccept und .exceptionally verketten können, ohne einen Thread zu blockieren.

Die Antwort lesen

HttpResponse<T> ist ein einfaches typisiertes Wertobjekt. Die am häufigsten verwendeten Methoden:

  • statusCode() — der HTTP-Status als int (z.B. 200, 404). Es gibt kein isSuccessful(); prüfen Sie den Code selbst.
  • body() — der Körper, bereits durch den übergebenen BodyHandler in T konvertiert.
  • headers() — ein HttpHeaders. Verwenden Sie firstValue("Content-Type") (gibt ein Optional<String> zurück) oder allValues("Set-Cookie") für wiederholte Header.
  • uri() — die finale URI, die nach einer Umleitung von der Anfrage-URI abweichen kann.

Header-Namen werden ohne Berücksichtigung der Groß-/Kleinschreibung abgeglichen, daher liefern firstValue("content-type") und firstValue("Content-Type") denselben Wert.

Ein durchgearbeitetes Beispiel: synchrones GET, synchrones POST und asynchron

Dieses Programm stellt einen Loopback-Endpunkt bereit, der die empfangene HTTP-Methode meldet, und steuert ihn dann auf drei Arten mit einem gemeinsam genutzten HttpClient: ein synchrones GET, ein synchrones POST mit einem Körper und ein asynchrones GET über ein CompletableFuture.

java— editable, runs on the server

Was aus dem Durchlauf zu entnehmen ist:

  • Ein HttpClient bediente alle drei Anfragen. Der Client ist unveränderlich und thread-sicher, daher lautet das richtige Muster: einmal erstellen und überall teilen; einen Client pro Aufruf zu instanziieren, verschwendet Connection-Pools und HTTP/2-Sitzungen. Beachten Sie, dass nirgendwo ein disconnect() vorkommt — der Client verwaltet Verbindungen für Sie.
  • Das Anfrageverb lag im Builder: .GET() für den Lesevorgang und .POST(BodyPublishers.ofString("payload")) für den Schreibvorgang. Der Server hat die empfangene Methode zurückgespiegelt (handled GET, handled POST) und damit bestätigt, dass der Publisher sowohl den Körper übertrug als auch das Verb setzte.
  • HttpResponse ist ein typisiertes Objekt mit statusCode() und body(). Da BodyHandlers.ofString() übergeben wurde, kam der Körper bereits als String decodiert zurück — tauschen Sie ofByteArray, ofFile oder ofLines ein, und derselbe Aufruf liefert Bytes, eine gespeicherte Datei oder einen Strom von Zeilen.
  • Der asynchrone Aufruf gab ein CompletableFuture zurück und verkettete mit .thenApply, ohne einen Thread zu blockieren, bis future.get() aufgerufen wurde. Das ist der strukturelle Vorteil gegenüber HttpURLConnection: Nebenläufigkeit ist eingebaut, sodass Hunderte gleichzeitig laufende Anfragen nicht Hunderte blockierte Threads bedeuten müssen.
  • Der gesamte Ablauf — Client, Anfrage, synchrone und asynchrone Sendevorgänge, typisierte Antworten — verwendete keine manuellen Streams und keine Fehler-Stream-Falle. Im Vergleich zu HttpURLConnection ist HttpClient kürzer, sicherer und leistungsfähiger, weshalb er die Standardwahl auf Java 11+ ist.

Häufige Fallstricke

  • Ein 4xx oder 5xx ist keine Ausnahme. Anders als bei manchen Bibliotheken gibt HttpClient die Antwort für jeden empfangenen Status normal zurück; nur Transportfehler (Verbindung verweigert, Timeout, DNS) werfen eine IOException. Prüfen Sie immer statusCode() — der Körper einer Fehlerantwort ist weiterhin lesbar.
  • Einen Client erstellen und teilen. HttpClient ist unveränderlich und thread-sicher und besitzt seinen Connection-Pool. Das Erstellen eines Clients pro Anfrage verwirft die Verbindungswiederverwendung und HTTP/2-Sitzungen. Vor Java 21 gibt es kein close() (und ab Java 21+ ist er AutoCloseable, aber ein langlebiger gemeinsam genutzter Client muss selten geschlossen werden).
  • connectTimeout und timeout sind unterschiedlich. HttpClient.connectTimeout(...) begrenzt, wie lange das Aufbauen der TCP-Verbindung dauern darf; HttpRequest.timeout(...) begrenzt die gesamte Anfrage/Antwort. Eine Anfrage, die ein Timeout überschreitet, schließt das Future mit einer HttpTimeoutException ab.
  • Ein GET kann keinen Körper tragen. Das Aufrufen von GET() mit einem BodyPublisher entspricht nicht der Funktionsweise der API — verwenden Sie POST, PUT oder das generische method(name, publisher) für Verben, die Daten senden.
  • URI.create muss eine absolute, wohlgeformte URI erhalten. Leerzeichen und andere unsichere Zeichen werden nicht für Sie codiert; codieren Sie Abfrageparameter, bevor Sie die URI erstellen.

Übungen

Übung
In einem stark frequentierten Dienst schreibt ein Entwickler 'HttpClient.newHttpClient()' innerhalb der Methode, die jede eingehende Anfrage behandelt, und erstellt so einen neuen Client pro Aufruf. Reviewer beanstanden dies. Warum?
In einem stark frequentierten Dienst schreibt ein Entwickler 'HttpClient.newHttpClient()' innerhalb der Methode, die jede eingehende Anfrage behandelt, und erstellt so einen neuen Client pro Aufruf. Reviewer beanstanden dies. Warum?
Was this page helpful?