Desde Java 11 hay un cliente HTTP en la biblioteca estándar, con HTTP/2 y peticiones asíncronas. Ya no hace falta añadir ninguna dependencia para hablar con una API.
También te puede interesar
El cliente
HttpClient cliente = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
Se crea una vez y se reutiliza: mantiene el grupo de conexiones y la configuración de HTTP/2. Crear uno por petición es el error de rendimiento más común con esta API.
GET
HttpRequest get = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/get?lenguaje=java&version=25"))
.header("Accept", "application/json")
.header("User-Agent", "decodigo-ejemplo/1.0")
.timeout(Duration.ofSeconds(20))
.GET()
.build();
HttpResponse<String> r = cliente.send(get, HttpResponse.BodyHandlers.ofString());
salidaestado : 200 versión : HTTP_2 tipo : application/json cuerpo : "lenguaje": "java",
Negoció HTTP/2 sin que se lo pidiéramos. Hay dos tiempos de espera distintos y conviene poner los dos: el connectTimeout del cliente, para establecer la conexión, y el timeout de la petición, para la respuesta completa.
POST
String json = """
{"nombre": "decodigo", "lenguaje": "java"}""";
HttpRequest post = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/post"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
salidaeco : "data": "{\"nombre\": \"decodigo\", \"lenguaje\": \"java\"}",
Hay un BodyPublisher para cada caso: ofString, ofFile, ofByteArray y noBody. Y al otro lado, un BodyHandler: ofString, ofLines, ofInputStream y ofFile, que descarga directamente a disco sin cargar nada en memoria.
Un 404 no es una excepción
salidaestado : 404 ¿hay excepción? no
send solo lanza si la petición no llegó a completarse —no hay red, no resuelve el nombre, venció el plazo—. Un 404, un 500 o un 403 son respuestas perfectamente válidas desde el punto de vista del cliente. Hay que mirar statusCode() siempre, o acabarás analizando una página de error como si fuera JSON.Asíncrono
var futuro = cliente.sendAsync(get, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::statusCode);
IO.println("mientras tanto el programa sigue…");
IO.println("llegó: " + futuro.get());
salidamientras tanto el programa sigue… llegó: 200
sendAsync devuelve un CompletableFuture y no bloquea. Para lanzar muchas peticiones a la vez hay dos caminos: encadenar futuros, o —más legible desde Java 21— usar send normal dentro de hilos virtuales, que bloquean sin coste.
Resumen
| Para… | Se usa |
|---|---|
| Crear el cliente | HttpClient.newBuilder()...build(), una sola vez |
| Mandar un cuerpo | BodyPublishers.ofString / ofFile |
| Recibir texto | BodyHandlers.ofString() |
| Bajar un archivo grande | BodyHandlers.ofFile(ruta) |
| Saber si salió bien | r.statusCode(), siempre |
| Una cabecera de la respuesta | r.headers().firstValue("...") |
| No bloquear | sendAsync, o send en un hilo virtual |
Todo el código se compiló y se ejecutó con Java 25 LTS en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.