Java

Tutorial Java: HttpClient moderno

Aprende a usar la API HttpClient moderna de Java 11 para realizar peticiones GET y POST de forma eficiente y asíncrona con ejemplos prácticos.

Aprende Java y certifícate

API HTTP Cliente moderno (Java 11+)

Java 11 introdujo una nueva API de cliente HTTP que representa un cambio significativo en la forma de realizar peticiones web desde aplicaciones Java. Esta API, ubicada en el paquete java.net.http, reemplaza las antiguas implementaciones como HttpURLConnection con un enfoque más moderno, fluido y adaptado a los patrones de programación actuales.

El nuevo cliente HTTP se diseñó con varios objetivos clave:

  • Proporcionar una API más intuitiva y fácil de usar
  • Soportar tanto operaciones síncronas como asíncronas
  • Implementar HTTP/2 de forma nativa
  • Mejorar el rendimiento general de las comunicaciones HTTP

Componentes principales

La API se estructura alrededor de tres clases fundamentales:

  • HttpClient: El punto central para enviar solicitudes HTTP y recibir respuestas
  • HttpRequest: Representa una solicitud HTTP con todos sus componentes
  • HttpResponse: Encapsula la respuesta recibida del servidor

Creación del cliente HTTP

Para comenzar a utilizar esta API, primero necesitamos crear una instancia de HttpClient:

HttpClient client = HttpClient.newHttpClient();

Este método de fábrica crea un cliente con la configuración predeterminada. Para casos más específicos, podemos usar un builder para personalizar el comportamiento:

HttpClient client = HttpClient.newBuilder()
    .version(HttpClient.Version.HTTP_2)    // Preferir HTTP/2
    .connectTimeout(Duration.ofSeconds(10)) // Tiempo máximo de conexión
    .followRedirects(HttpClient.Redirect.NORMAL) // Seguir redirecciones
    .build();

Construcción de solicitudes

Las solicitudes HTTP se crean mediante el patrón builder a través de la clase HttpRequest.Builder:

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.ejemplo.com/datos"))
    .header("Content-Type", "application/json")
    .timeout(Duration.ofMinutes(1))
    .GET()  // Método HTTP a utilizar
    .build();

La API permite configurar todos los aspectos de una solicitud HTTP de manera fluida:

  • URI: Destino de la solicitud
  • Encabezados: Metadatos de la solicitud
  • Método HTTP: GET, POST, PUT, DELETE, etc.
  • Cuerpo de la solicitud: Para métodos como POST o PUT
  • Timeout: Tiempo máximo de espera

Envío de solicitudes y procesamiento de respuestas

Una vez configurados el cliente y la solicitud, podemos enviarla y procesar la respuesta:

// Envío síncrono
HttpResponse<String> response = client.send(request, 
    HttpResponse.BodyHandlers.ofString());

// Acceso a los componentes de la respuesta
int statusCode = response.statusCode();
String body = response.body();
HttpHeaders headers = response.headers();

La API utiliza procesadores de cuerpo (body handlers) para convertir el flujo de bytes de la respuesta al formato deseado. Algunos handlers comunes incluyen:

  • BodyHandlers.ofString(): Convierte el cuerpo en un String
  • BodyHandlers.ofInputStream(): Proporciona un InputStream para procesar los datos
  • BodyHandlers.ofFile(Path): Guarda la respuesta directamente en un archivo
  • BodyHandlers.ofByteArray(): Convierte el cuerpo en un array de bytes

Peticiones asíncronas

Una de las características más potentes de la nueva API es el soporte nativo para operaciones asíncronas mediante CompletableFuture:

CompletableFuture<HttpResponse<String>> futureResponse = 
    client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

// Procesamiento asíncrono de la respuesta
futureResponse.thenApply(HttpResponse::body)
    .thenAccept(System.out::println)
    .join(); // Esperar a que se complete (opcional)

Este enfoque asíncrono permite realizar múltiples solicitudes en paralelo sin bloquear el hilo principal, lo que resulta especialmente útil para aplicaciones que necesitan alta concurrencia.

Manejo de autenticación

El cliente HTTP moderno también facilita la implementación de mecanismos de autenticación:

HttpClient client = HttpClient.newBuilder()
    .authenticator(new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(
                "usuario", "contraseña".toCharArray());
        }
    })
    .build();

Soporte para WebSockets

Otra característica importante es el soporte nativo para WebSockets, permitiendo comunicaciones bidireccionales en tiempo real:

WebSocket.Builder wsBuilder = client.newWebSocketBuilder();

WebSocket webSocket = wsBuilder.buildAsync(
    URI.create("ws://servidor.ejemplo.com/socket"), 
    new WebSocket.Listener() {
        @Override
        public CompletionStage<?> onText(WebSocket webSocket, 
                                         CharSequence data, 
                                         boolean last) {
            System.out.println("Mensaje recibido: " + data);
            return WebSocket.Listener.super.onText(webSocket, data, last);
        }
    }).join();

// Enviar un mensaje
webSocket.sendText("Hola, servidor", true);

Ventajas sobre las APIs anteriores

El cliente HTTP moderno ofrece varias mejoras significativas sobre HttpURLConnection:

  • API fluida con patrón builder que facilita la configuración
  • Soporte nativo para operaciones asíncronas
  • Implementación de HTTP/2 con multiplexación de conexiones
  • Mejor manejo de errores y excepciones más informativas
  • Procesadores de cuerpo flexibles para diferentes tipos de respuestas
  • Rendimiento mejorado gracias a la implementación moderna

Esta nueva API representa un gran avance en la forma de realizar comunicaciones HTTP en Java, alineándose con las prácticas modernas de desarrollo y proporcionando herramientas potentes para crear aplicaciones conectadas eficientes.

Realizar peticiones GET simples

Las peticiones GET son el tipo más común de solicitud HTTP, utilizadas para recuperar información de un servidor sin modificar ningún dato. Con el cliente HTTP moderno de Java, realizar estas peticiones se ha vuelto mucho más intuitivo y directo.

Estructura básica de una petición GET

Para realizar una petición GET simple, necesitamos seguir tres pasos fundamentales:

  1. Crear un cliente HTTP
  2. Construir una solicitud GET
  3. Enviar la solicitud y procesar la respuesta

Veamos un ejemplo completo:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class SimpleGetExample {
    public static void main(String[] args) throws Exception {
        // 1. Crear el cliente HTTP
        HttpClient client = HttpClient.newHttpClient();
        
        // 2. Construir la solicitud GET
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://jsonplaceholder.typicode.com/posts/1"))
            .timeout(Duration.ofSeconds(5))
            .GET()  // Este método es opcional, ya que GET es el predeterminado
            .build();
        
        // 3. Enviar la solicitud y recibir la respuesta como String
        HttpResponse<String> response = client.send(request, 
            HttpResponse.BodyHandlers.ofString());
        
        // 4. Procesar la respuesta
        System.out.println("Código de estado: " + response.statusCode());
        System.out.println("Cuerpo de la respuesta: " + response.body());
    }
}

Este ejemplo muestra la forma más directa de realizar una petición GET. Observa que el método .GET() es técnicamente opcional, ya que es el método predeterminado cuando construimos una solicitud HTTP.

Configuración de parámetros de consulta (query parameters)

Muchas APIs requieren parámetros de consulta para filtrar o personalizar los resultados. Estos parámetros se añaden a la URI:

// Construir una URI con parámetros de consulta
URI uri = URI.create("https://api.openweathermap.org/data/2.5/weather?q=Madrid&appid=tu_api_key&units=metric");

HttpRequest request = HttpRequest.newBuilder()
    .uri(uri)
    .build();

Para una construcción más dinámica de parámetros, podemos utilizar la clase URLBuilder o simplemente concatenar los parámetros de forma segura:

String ciudad = "Madrid";
String apiKey = "tu_api_key";
String unidades = "metric";

String endpoint = "https://api.openweathermap.org/data/2.5/weather";
String queryParams = String.format("?q=%s&appid=%s&units=%s", 
    ciudad, apiKey, unidades);

URI uri = URI.create(endpoint + queryParams);

Configuración de encabezados (headers)

Los encabezados HTTP proporcionan metadatos adicionales sobre la solicitud. Son esenciales para muchas APIs que requieren autenticación o especificación del formato de datos:

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.github.com/users/octocat"))
    .header("Accept", "application/json")
    .header("User-Agent", "Java HttpClient Bot")
    .build();

Para añadir múltiples valores al mismo encabezado, podemos usar el método headers():

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.ejemplo.com/recursos"))
    .headers("Accept", "application/json", 
             "Authorization", "Bearer " + token,
             "User-Agent", "Java 11 HttpClient")
    .build();

Manejo de respuestas

El cliente HTTP moderno ofrece diferentes procesadores de cuerpo (body handlers) para convertir la respuesta al formato deseado:

// Obtener la respuesta como String
HttpResponse<String> textResponse = client.send(request, 
    HttpResponse.BodyHandlers.ofString());

// Obtener la respuesta como array de bytes
HttpResponse<byte[]> bytesResponse = client.send(request, 
    HttpResponse.BodyHandlers.ofByteArray());

// Guardar la respuesta directamente en un archivo
HttpResponse<Path> fileResponse = client.send(request, 
    HttpResponse.BodyHandlers.ofFile(Paths.get("respuesta.json")));

Manejo de códigos de estado

Es importante verificar el código de estado de la respuesta para asegurarse de que la solicitud se procesó correctamente:

HttpResponse<String> response = client.send(request, 
    HttpResponse.BodyHandlers.ofString());

int statusCode = response.statusCode();

if (statusCode >= 200 && statusCode < 300) {
    // Éxito - procesar la respuesta
    System.out.println("Datos recibidos: " + response.body());
} else if (statusCode == 404) {
    // Recurso no encontrado
    System.out.println("El recurso solicitado no existe");
} else {
    // Otro error
    System.out.println("Error en la solicitud: " + statusCode);
    System.out.println("Mensaje: " + response.body());
}

Configuración de timeouts

Para evitar que las solicitudes se bloqueen indefinidamente, es recomendable configurar tiempos de espera:

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.ejemplo.com/datos"))
    .timeout(Duration.ofSeconds(10))  // La solicitud fallará después de 10 segundos
    .build();

Ejemplo práctico: Consulta a una API pública

Veamos un ejemplo completo que consulta la API pública de OpenWeatherMap para obtener el clima actual:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class WeatherApiExample {
    public static void main(String[] args) {
        try {
            // Parámetros de la consulta
            String ciudad = "Barcelona";
            String apiKey = "tu_api_key"; // Reemplaza con tu clave API real
            
            // Construir la URI
            String url = String.format(
                "https://api.openweathermap.org/data/2.5/weather?q=%s&appid=%s&units=metric",
                ciudad, apiKey);
            
            // Crear el cliente y la solicitud
            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .timeout(Duration.ofSeconds(5))
                .header("Accept", "application/json")
                .build();
            
            // Enviar la solicitud
            HttpResponse<String> response = client.send(request, 
                HttpResponse.BodyHandlers.ofString());
            
            // Verificar y procesar la respuesta
            if (response.statusCode() == 200) {
                String jsonResponse = response.body();
                System.out.println("Datos del clima para " + ciudad + ":");
                System.out.println(jsonResponse);
                
                // Aquí podrías usar una biblioteca JSON para parsear la respuesta
                // y extraer información específica como temperatura, humedad, etc.
            } else {
                System.out.println("Error al obtener datos: " + response.statusCode());
                System.out.println(response.body());
            }
            
        } catch (Exception e) {
            System.out.println("Error en la solicitud: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

Manejo de redirecciones

Por defecto, el cliente HTTP sigue las redirecciones automáticamente. Sin embargo, podemos personalizar este comportamiento:

HttpClient client = HttpClient.newBuilder()
    .followRedirects(HttpClient.Redirect.NORMAL) // Seguir redirecciones normales
    .build();

// Otras opciones:
// HttpClient.Redirect.ALWAYS - Seguir todas las redirecciones
// HttpClient.Redirect.NEVER - No seguir ninguna redirección

Buenas prácticas para peticiones GET

  1. Siempre configura timeouts para evitar bloqueos indefinidos
  2. Verifica los códigos de estado antes de procesar la respuesta
  3. Maneja las excepciones adecuadamente para proporcionar mensajes de error útiles
  4. Reutiliza las instancias de HttpClient cuando sea posible, ya que son thread-safe y mantienen un pool de conexiones
  5. Incluye encabezados User-Agent para identificar tu aplicación ante los servidores

Con estas técnicas, puedes realizar peticiones GET de forma eficiente y robusta utilizando el cliente HTTP moderno de Java, aprovechando todas las ventajas que ofrece esta API sobre las implementaciones anteriores.

Realizar peticiones POST simples

A diferencia de las peticiones GET que solicitan información, las peticiones POST se utilizan para enviar datos al servidor para crear o actualizar recursos. El cliente HTTP moderno de Java facilita enormemente la realización de estas operaciones, proporcionando una API fluida y potente para construir y enviar solicitudes POST.

Anatomía de una petición POST

Una petición POST típicamente contiene:

  • Una URI de destino
  • Encabezados HTTP (incluyendo Content-Type)
  • Un cuerpo (body) con los datos a enviar
  • Opcionalmente, parámetros de configuración como timeouts

La principal diferencia con las peticiones GET es la inclusión del cuerpo de la solicitud, que puede contener datos en diversos formatos como JSON, XML, formularios o datos binarios.

Envío de datos JSON

El formato JSON es ampliamente utilizado en APIs modernas. Para enviar datos JSON, necesitamos configurar el encabezado Content-Type adecuadamente y proporcionar el cuerpo de la solicitud:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class JsonPostExample {
    public static void main(String[] args) throws Exception {
        // Crear el cliente HTTP
        HttpClient client = HttpClient.newHttpClient();
        
        // Datos a enviar en formato JSON
        String jsonData = """
            {
                "title": "Nuevo artículo",
                "body": "Contenido del artículo",
                "userId": 1
            }
            """;
        
        // Construir la solicitud POST
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://jsonplaceholder.typicode.com/posts"))
            .timeout(Duration.ofSeconds(10))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(jsonData))
            .build();
        
        // Enviar la solicitud y recibir la respuesta
        HttpResponse<String> response = client.send(request, 
            HttpResponse.BodyHandlers.ofString());
        
        // Procesar la respuesta
        System.out.println("Código de estado: " + response.statusCode());
        System.out.println("Respuesta: " + response.body());
    }
}

El elemento clave aquí es el método POST() que recibe un BodyPublisher. Este componente se encarga de publicar el contenido del cuerpo de la solicitud.

Publicadores de cuerpo (BodyPublishers)

Java proporciona varios publicadores predefinidos para diferentes tipos de datos:

  • ofString(): Para enviar texto plano o JSON como String
  • ofByteArray(): Para enviar datos binarios
  • ofFile(): Para enviar el contenido de un archivo
  • ofInputStream(): Para enviar datos desde un InputStream
  • noBody(): Para solicitudes sin cuerpo
// Enviar datos desde un archivo
HttpRequest.BodyPublishers.ofFile(Path.of("datos.json"))

// Enviar datos binarios
byte[] datos = obtenerDatosBinarios();
HttpRequest.BodyPublishers.ofByteArray(datos)

// Enviar desde un InputStream
InputStream stream = obtenerStream();
HttpRequest.BodyPublishers.ofInputStream(() -> stream)

Envío de formularios

Para enviar datos de formulario (formato application/x-www-form-urlencoded), necesitamos formatear los datos correctamente:

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;

public class FormPostExample {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        
        // Datos del formulario
        Map<String, String> formData = new HashMap<>();
        formData.put("username", "usuario123");
        formData.put("password", "clave456");
        formData.put("action", "login");
        
        // Codificar los datos del formulario
        StringJoiner sj = new StringJoiner("&");
        for (Map.Entry<String, String> entry : formData.entrySet()) {
            sj.add(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8) + "=" +
                   URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
        }
        String formBody = sj.toString();
        
        // Construir la solicitud POST
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://httpbin.org/post"))
            .timeout(Duration.ofSeconds(10))
            .header("Content-Type", "application/x-www-form-urlencoded")
            .POST(HttpRequest.BodyPublishers.ofString(formBody))
            .build();
        
        // Enviar la solicitud
        HttpResponse<String> response = client.send(request, 
            HttpResponse.BodyHandlers.ofString());
        
        System.out.println("Código de estado: " + response.statusCode());
        System.out.println("Respuesta: " + response.body());
    }
}

Envío de datos multipart (archivos y formularios)

Para enviar datos multipart (como formularios con archivos adjuntos), necesitamos crear manualmente el cuerpo multipart con los límites (boundaries) adecuados:

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.UUID;

public class MultipartPostExample {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        
        // Generar un boundary único para separar las partes
        String boundary = UUID.randomUUID().toString();
        
        // Archivo a subir
        Path filePath = Path.of("imagen.jpg");
        String filename = filePath.getFileName().toString();
        String mimeType = Files.probeContentType(filePath);
        byte[] fileData = Files.readAllBytes(filePath);
        
        // Construir el cuerpo multipart
        String separator = "--" + boundary + "\r\nContent-Disposition: form-data; ";
        String end = "\r\n--" + boundary + "--\r\n";
        
        // Parte 1: campo de texto
        StringBuilder builder = new StringBuilder();
        builder.append(separator)
               .append("name=\"descripcion\"\r\n\r\n")
               .append("Mi imagen de perfil")
               .append("\r\n");
        
        // Parte 2: archivo
        builder.append(separator)
               .append("name=\"archivo\"; filename=\"")
               .append(filename)
               .append("\"\r\nContent-Type: ")
               .append(mimeType)
               .append("\r\n\r\n");
        
        // Convertir el inicio y el final a bytes
        byte[] start = builder.toString().getBytes(StandardCharsets.UTF_8);
        byte[] newline = "\r\n".getBytes(StandardCharsets.UTF_8);
        byte[] endBytes = end.getBytes(StandardCharsets.UTF_8);
        
        // Combinar todas las partes en un solo array
        byte[] requestBody = new byte[start.length + fileData.length + newline.length + endBytes.length];
        System.arraycopy(start, 0, requestBody, 0, start.length);
        System.arraycopy(fileData, 0, requestBody, start.length, fileData.length);
        System.arraycopy(newline, 0, requestBody, start.length + fileData.length, newline.length);
        System.arraycopy(endBytes, 0, requestBody, start.length + fileData.length + newline.length, endBytes.length);
        
        // Construir la solicitud POST
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://httpbin.org/post"))
            .header("Content-Type", "multipart/form-data; boundary=" + boundary)
            .POST(HttpRequest.BodyPublishers.ofByteArray(requestBody))
            .build();
        
        // Enviar la solicitud
        HttpResponse<String> response = client.send(request, 
            HttpResponse.BodyHandlers.ofString());
        
        System.out.println("Código de estado: " + response.statusCode());
        System.out.println("Respuesta: " + response.body());
    }
}

Aunque este enfoque funciona, para aplicaciones reales se recomienda utilizar bibliotecas como Apache HttpComponents o OkHttp que simplifican considerablemente el manejo de solicitudes multipart.

Peticiones POST asíncronas

Al igual que con las peticiones GET, podemos realizar solicitudes POST de forma asíncrona:

import java.util.concurrent.CompletableFuture;

public class AsyncPostExample {
    public static void main(String[] args) {
        HttpClient client = HttpClient.newHttpClient();
        
        String jsonData = "{\"nombre\":\"Juan\",\"edad\":30}";
        
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://httpbin.org/post"))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(jsonData))
            .build();
        
        // Enviar la solicitud de forma asíncrona
        CompletableFuture<HttpResponse<String>> futureResponse = 
            client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
        
        // Procesar la respuesta cuando esté disponible
        futureResponse
            .thenApply(response -> {
                System.out.println("Código de estado: " + response.statusCode());
                return response.body();
            })
            .thenAccept(body -> System.out.println("Respuesta: " + body))
            .exceptionally(ex -> {
                System.err.println("Error en la solicitud: " + ex.getMessage());
                return null;
            });
        
        // Hacer otras tareas mientras se procesa la solicitud
        System.out.println("Solicitud enviada, continuando con otras tareas...");
        
        // Esperar a que se complete la solicitud (solo para este ejemplo)
        futureResponse.join();
    }
}

Este enfoque es especialmente útil cuando necesitas realizar múltiples solicitudes en paralelo o no quieres bloquear el hilo principal.

Manejo de respuestas en peticiones POST

El procesamiento de respuestas para peticiones POST es similar al de las peticiones GET, pero con algunas consideraciones adicionales:

HttpResponse<String> response = client.send(request, 
    HttpResponse.BodyHandlers.ofString());

int statusCode = response.statusCode();

switch (statusCode) {
    case 201: // Created
        System.out.println("Recurso creado exitosamente");
        System.out.println("URI del nuevo recurso: " + 
            response.headers().firstValue("Location").orElse("No disponible"));
        break;
    case 200: // OK
        System.out.println("Operación completada con éxito");
        break;
    case 400: // Bad Request
        System.out.println("Error en los datos enviados: " + response.body());
        break;
    case 401: // Unauthorized
    case 403: // Forbidden
        System.out.println("Error de autenticación o autorización");
        break;
    case 422: // Unprocessable Entity
        System.out.println("Datos válidos pero inaceptables: " + response.body());
        break;
    default:
        System.out.println("Respuesta inesperada: " + statusCode);
        System.out.println("Cuerpo: " + response.body());
}

Ejemplo práctico: Creación de un recurso en una API REST

Veamos un ejemplo completo que crea un nuevo usuario en una API REST:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class CreateUserExample {
    public static void main(String[] args) {
        try {
            // Crear el cliente HTTP
            HttpClient client = HttpClient.newHttpClient();
            
            // Datos del nuevo usuario en formato JSON
            String userData = """
                {
                    "name": "Ana García",
                    "email": "ana.garcia@ejemplo.com",
                    "gender": "female",
                    "status": "active"
                }
                """;
            
            // Construir la solicitud POST
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://gorest.co.in/public/v2/users"))
                .timeout(Duration.ofSeconds(15))
                .header("Content-Type", "application/json")
                .header("Authorization", "Bearer TU_TOKEN_DE_ACCESO") // Reemplazar con tu token real
                .POST(HttpRequest.BodyPublishers.ofString(userData))
                .build();
            
            // Enviar la solicitud
            HttpResponse<String> response = client.send(request, 
                HttpResponse.BodyHandlers.ofString());
            
            // Procesar la respuesta
            if (response.statusCode() == 201) {
                System.out.println("Usuario creado exitosamente:");
                System.out.println(response.body());
            } else {
                System.out.println("Error al crear usuario. Código: " + response.statusCode());
                System.out.println("Respuesta: " + response.body());
            }
            
        } catch (Exception e) {
            System.out.println("Error en la solicitud: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

Buenas prácticas para peticiones POST

  • Siempre especifica el encabezado Content-Type adecuado para el formato de datos que estás enviando
  • Valida los datos antes de enviarlos para evitar errores 400 (Bad Request)
  • Maneja adecuadamente los diferentes códigos de estado que pueden devolver las APIs
  • Implementa reintentos para errores transitorios (como 429 Too Many Requests o 503 Service Unavailable)
  • Considera la seguridad utilizando HTTPS y tokens de autenticación cuando sea necesario
  • Limita el tamaño de los datos enviados para evitar problemas de rendimiento

Con estas técnicas, puedes realizar peticiones POST de manera efectiva utilizando el cliente HTTP moderno de Java, aprovechando su API fluida y su soporte para operaciones tanto síncronas como asíncronas.

CONSTRUYE TU CARRERA EN IA Y PROGRAMACIÓN SOFTWARE

Accede a +1000 lecciones y cursos con certificado. Mejora tu portfolio con certificados de superación para tu CV.

30 % DE DESCUENTO

Plan mensual

19.00 /mes

13.30 € /mes

Precio normal mensual: 19 €
63 % DE DESCUENTO

Plan anual

10.00 /mes

7.00 € /mes

Ahorras 144 € al año
Precio normal anual: 120 €
Aprende Java online

Ejercicios de esta lección HttpClient moderno

Evalúa tus conocimientos de esta lección HttpClient moderno con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Streams: match

Test

Gestión de errores y excepciones

Código

CRUD en Java de modelo Customer sobre un ArrayList

Proyecto

Clases abstractas

Test

Listas

Código

Métodos de la clase String

Código

Streams: reduce()

Test

API java.nio 2

Puzzle

Polimorfismo

Código

Pattern Matching

Código

Streams: flatMap()

Test

Llamada y sobrecarga de funciones

Puzzle

Métodos referenciados

Test

Métodos de la clase String

Código

Representación de Fecha

Puzzle

Operadores lógicos

Test

Inferencia de tipos con var

Código

Tipos de datos

Código

Estructuras de iteración

Puzzle

Streams: forEach()

Test

Objetos

Puzzle

Funciones lambda

Test

Uso de Scanner

Puzzle

Tipos de variables

Puzzle

Streams: collect()

Puzzle

Operadores aritméticos

Puzzle

Arrays y matrices

Código

Clases y objetos

Código

Interfaz funcional Consumer

Test

CRUD en Java de modelo Customer sobre un HashMap

Proyecto

Interfaces

Código

Enumeraciones Enums

Código

API Optional

Test

Interfaz funcional Function

Test

Encapsulación

Test

Interfaces

Código

Uso de API Optional

Puzzle

Representación de Hora

Test

Herencia básica

Test

Clases y objetos

Código

Interfaz funcional Supplier

Puzzle

HashMap

Puzzle

Sobrecarga de métodos

Test

Polimorfismo de tiempo de ejecución

Puzzle

OOP en Java

Proyecto

Sobrecarga de métodos

Código

CRUD de productos en Java

Proyecto

Clases sealed

Código

Creación de Streams

Test

Records

Código

Encapsulación

Código

Streams: min max

Puzzle

Herencia

Código

Métodos avanzados de la clase String

Puzzle

Funciones

Código

Polimorfismo de tiempo de compilación

Test

Reto sintaxis Java

Proyecto

Conjuntos

Código

Estructuras de control

Código

Recursión

Código

Excepciones

Puzzle

Herencia avanzada

Puzzle

Estructuras de selección

Test

Uso de interfaces

Test

Operadores

Código

Variables

Código

HashSet

Test

Objeto Scanner

Test

Streams: filter()

Puzzle

Operaciones de Streams

Puzzle

Interfaz funcional Predicate

Puzzle

Streams: sorted()

Test

Configuración de entorno

Test

Uso de variables

Test

Clases

Test

Streams: distinct()

Puzzle

Streams: count()

Test

ArrayList

Test

Mapas

Código

Datos de referencia

Test

Interfaces funcionales

Puzzle

Métodos básicos de la clase String

Test

Tipos de datos

Código

Clases abstractas

Código

Instalación

Test

Funciones

Código

Excepciones

Código

Estructuras de control

Código

Herencia de clases

Código

La clase Scanner

Código

Generics

Código

Streams: map()

Puzzle

Funciones y encapsulamiento

Test

Todas las lecciones de Java

Accede a todas las lecciones de Java y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Instalación De Java

Introducción Y Entorno

Configuración De Entorno Java

Introducción Y Entorno

Tipos De Datos

Sintaxis

Variables

Sintaxis

Operadores

Sintaxis

Estructuras De Control

Sintaxis

Funciones

Sintaxis

Recursión

Sintaxis

Arrays Y Matrices

Sintaxis

Excepciones

Programación Orientada A Objetos

Clases Y Objetos

Programación Orientada A Objetos

Encapsulación

Programación Orientada A Objetos

Herencia

Programación Orientada A Objetos

Clases Abstractas

Programación Orientada A Objetos

Interfaces

Programación Orientada A Objetos

Sobrecarga De Métodos

Programación Orientada A Objetos

Polimorfismo

Programación Orientada A Objetos

La Clase Scanner

Programación Orientada A Objetos

Métodos De La Clase String

Programación Orientada A Objetos

Excepciones

Programación Orientada A Objetos

Records

Programación Orientada A Objetos

Pattern Matching

Programación Orientada A Objetos

Inferencia De Tipos Con Var

Programación Orientada A Objetos

Enumeraciones Enums

Programación Orientada A Objetos

Generics

Programación Orientada A Objetos

Clases Sealed

Programación Orientada A Objetos

Listas

Framework Collections

Conjuntos

Framework Collections

Mapas

Framework Collections

Funciones Lambda

Programación Funcional

Interfaz Funcional Consumer

Programación Funcional

Interfaz Funcional Predicate

Programación Funcional

Interfaz Funcional Supplier

Programación Funcional

Interfaz Funcional Function

Programación Funcional

Métodos Referenciados

Programación Funcional

Creación De Streams

Programación Funcional

Operaciones Intermedias Con Streams: Map()

Programación Funcional

Operaciones Intermedias Con Streams: Filter()

Programación Funcional

Operaciones Intermedias Con Streams: Distinct()

Programación Funcional

Operaciones Finales Con Streams: Collect()

Programación Funcional

Operaciones Finales Con Streams: Min Max

Programación Funcional

Operaciones Intermedias Con Streams: Flatmap()

Programación Funcional

Operaciones Intermedias Con Streams: Sorted()

Programación Funcional

Operaciones Finales Con Streams: Reduce()

Programación Funcional

Operaciones Finales Con Streams: Foreach()

Programación Funcional

Operaciones Finales Con Streams: Count()

Programación Funcional

Operaciones Finales Con Streams: Match

Programación Funcional

Api Optional

Programación Funcional

Transformación

Programación Funcional

Reducción Y Acumulación

Programación Funcional

Mapeo

Programación Funcional

Streams Paralelos

Programación Funcional

Agrupación Y Partición

Programación Funcional

Filtrado Y Búsqueda

Programación Funcional

Api Java.nio 2

Entrada Y Salida Io

Fundamentos De Io

Entrada Y Salida Io

Leer Y Escribir Archivos

Entrada Y Salida Io

Httpclient Moderno

Entrada Y Salida Io

Clases De Nio2

Entrada Y Salida Io

Api Java.time

Api Java.time

Localtime

Api Java.time

Localdatetime

Api Java.time

Localdate

Api Java.time

Executorservice

Concurrencia

Virtual Threads (Project Loom)

Concurrencia

Future Y Completablefuture

Concurrencia

Spring Framework

Frameworks Para Java

Micronaut

Frameworks Para Java

Maven

Frameworks Para Java

Gradle

Frameworks Para Java

Lombok Para Java

Frameworks Para Java

Quarkus

Frameworks Para Java

Ecosistema Jakarta Ee De Java

Frameworks Para Java

Introducción A Junit 5

Testing

Accede GRATIS a Java y certifícate

Certificados de superación de Java

Supera todos los ejercicios de programación del curso de Java y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender la estructura y componentes principales de la API HttpClient moderna en Java 11+.
  • Realizar peticiones HTTP GET y POST utilizando la API fluida y builder pattern.
  • Configurar y manejar encabezados, parámetros, timeouts y códigos de estado en las solicitudes HTTP.
  • Implementar operaciones asíncronas con CompletableFuture para mejorar la concurrencia.
  • Aplicar buenas prácticas en el manejo de autenticación, multipart/form-data y procesamiento de respuestas HTTP.