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 alsint(z.B.200,404). Es gibt keinisSuccessful(); prüfen Sie den Code selbst.body()— der Körper, bereits durch den übergebenenBodyHandlerinTkonvertiert.headers()— einHttpHeaders. Verwenden SiefirstValue("Content-Type")(gibt einOptional<String>zurück) oderallValues("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.
Was aus dem Durchlauf zu entnehmen ist:
- Ein
HttpClientbediente 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 eindisconnect()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. HttpResponseist ein typisiertes Objekt mitstatusCode()undbody(). DaBodyHandlers.ofString()übergeben wurde, kam der Körper bereits alsStringdecodiert zurück — tauschen SieofByteArray,ofFileoderofLinesein, und derselbe Aufruf liefert Bytes, eine gespeicherte Datei oder einen Strom von Zeilen.- Der asynchrone Aufruf gab ein
CompletableFuturezurück und verkettete mit.thenApply, ohne einen Thread zu blockieren, bisfuture.get()aufgerufen wurde. Das ist der strukturelle Vorteil gegenüberHttpURLConnection: 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
HttpURLConnectionistHttpClientkü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
HttpClientdie Antwort für jeden empfangenen Status normal zurück; nur Transportfehler (Verbindung verweigert, Timeout, DNS) werfen eineIOException. Prüfen Sie immerstatusCode()— der Körper einer Fehlerantwort ist weiterhin lesbar. - Einen Client erstellen und teilen.
HttpClientist 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 keinclose()(und ab Java 21+ ist erAutoCloseable, aber ein langlebiger gemeinsam genutzter Client muss selten geschlossen werden). connectTimeoutundtimeoutsind 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 einerHttpTimeoutExceptionab.- Ein
GETkann keinen Körper tragen. Das Aufrufen vonGET()mit einemBodyPublisherentspricht nicht der Funktionsweise der API — verwenden SiePOST,PUToder das generischemethod(name, publisher)für Verben, die Daten senden. URI.createmuss eine absolute, wohlgeformte URI erhalten. Leerzeichen und andere unsichere Zeichen werden nicht für Sie codiert; codieren Sie Abfrageparameter, bevor Sie die URI erstellen.