El cliente HTTP RestTemplate legacy
RestTemplate es el cliente HTTP tradicional de Spring que ha sido la herramienta estándar para realizar peticiones HTTP desde aplicaciones Spring durante muchos años. Aunque actualmente está marcado como deprecated en favor de RestClient y WebClient, sigue siendo ampliamente utilizado en proyectos existentes y es importante conocer su funcionamiento.
Este cliente proporciona una API síncrona para realizar operaciones HTTP comunes como GET, POST, PUT y DELETE de manera directa y sencilla. Su diseño se basa en el patrón Template Method, ofreciendo métodos de alto nivel que abstraen la complejidad de las comunicaciones HTTP.
Configuración básica de RestTemplate
Para utilizar RestTemplate en una aplicación Spring Boot, necesitamos crear un bean de configuración. La forma más común es definirlo en una clase de configuración:
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Una vez configurado, podemos inyectarlo en cualquier componente de Spring donde necesitemos realizar peticiones HTTP:
@Service
public class ApiService {
private final RestTemplate restTemplate;
public ApiService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
}
Operaciones GET básicas
Las operaciones GET son las más comunes al consumir APIs REST. RestTemplate ofrece varios métodos para realizar estas peticiones:
Obtener un objeto directamente:
@Service
public class UsuarioService {
private final RestTemplate restTemplate;
private static final String API_URL = "https://jsonplaceholder.typicode.com";
public UsuarioService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public Usuario obtenerUsuario(Long id) {
String url = API_URL + "/users/" + id;
return restTemplate.getForObject(url, Usuario.class);
}
}
Obtener una respuesta completa con metadatos:
public ResponseEntity<Usuario> obtenerUsuarioCompleto(Long id) {
String url = API_URL + "/users/" + id;
return restTemplate.getForEntity(url, Usuario.class);
}
Trabajar con arrays y listas:
public List<Usuario> obtenerTodosLosUsuarios() {
String url = API_URL + "/users";
Usuario[] usuarios = restTemplate.getForObject(url, Usuario[].class);
return Arrays.asList(usuarios);
}
Operaciones POST para crear recursos
Las operaciones POST se utilizan para crear nuevos recursos en el servidor. RestTemplate proporciona métodos específicos para estas operaciones:
public Usuario crearUsuario(Usuario nuevoUsuario) {
String url = API_URL + "/users";
return restTemplate.postForObject(url, nuevoUsuario, Usuario.class);
}
public ResponseEntity<Usuario> crearUsuarioCompleto(Usuario nuevoUsuario) {
String url = API_URL + "/users";
return restTemplate.postForEntity(url, nuevoUsuario, Usuario.class);
}
Obtener la ubicación del recurso creado:
public URI crearUsuarioYObtenerUbicacion(Usuario nuevoUsuario) {
String url = API_URL + "/users";
return restTemplate.postForLocation(url, nuevoUsuario);
}
Operaciones PUT y DELETE
Para actualizar recursos existentes utilizamos PUT, mientras que DELETE nos permite eliminar recursos:
public void actualizarUsuario(Long id, Usuario usuario) {
String url = API_URL + "/users/" + id;
restTemplate.put(url, usuario);
}
public void eliminarUsuario(Long id) {
String url = API_URL + "/users/" + id;
restTemplate.delete(url);
}
Manejo de parámetros de URL
RestTemplate facilita el manejo de parámetros en las URLs de varias formas:
Usando variables de plantilla:
public Usuario obtenerUsuarioPorId(Long id) {
String url = API_URL + "/users/{id}";
return restTemplate.getForObject(url, Usuario.class, id);
}
public List<Post> obtenerPostsDeUsuario(Long userId) {
String url = API_URL + "/users/{userId}/posts";
return Arrays.asList(
restTemplate.getForObject(url, Post[].class, userId)
);
}
Usando un mapa de parámetros:
public Usuario buscarUsuario(Long id, String formato) {
String url = API_URL + "/users/{id}?format={formato}";
Map<String, Object> parametros = new HashMap<>();
parametros.put("id", id);
parametros.put("formato", formato);
return restTemplate.getForObject(url, Usuario.class, parametros);
}
Configuración de headers HTTP
Para personalizar las cabeceras de nuestras peticiones, utilizamos HttpEntity junto con HttpHeaders:
public Usuario obtenerUsuarioConHeaders(Long id) {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer mi-token");
headers.set("Content-Type", "application/json");
HttpEntity<String> entity = new HttpEntity<>(headers);
String url = API_URL + "/users/" + id;
ResponseEntity<Usuario> response = restTemplate.exchange(
url, HttpMethod.GET, entity, Usuario.class
);
return response.getBody();
}
Para operaciones POST con headers personalizados:
public Usuario crearUsuarioConAutenticacion(Usuario usuario, String token) {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + token);
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Usuario> entity = new HttpEntity<>(usuario, headers);
return restTemplate.postForObject(API_URL + "/users", entity, Usuario.class);
}
Manejo básico de errores
RestTemplate lanza excepciones específicas para diferentes tipos de errores HTTP que podemos capturar y manejar:
public Usuario obtenerUsuarioSeguro(Long id) {
try {
String url = API_URL + "/users/" + id;
return restTemplate.getForObject(url, Usuario.class);
} catch (HttpClientErrorException e) {
// Errores 4xx (cliente)
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
throw new UsuarioNoEncontradoException("Usuario no encontrado: " + id);
}
throw new ErrorClienteException("Error del cliente: " + e.getMessage());
} catch (HttpServerErrorException e) {
// Errores 5xx (servidor)
throw new ErrorServidorException("Error del servidor: " + e.getMessage());
} catch (ResourceAccessException e) {
// Problemas de conectividad
throw new ErrorConexionException("No se pudo conectar al servidor");
}
}
El método exchange para mayor control
El método exchange es el más versátil de RestTemplate, permitiendo un control completo sobre la petición HTTP:
public ResponseEntity<String> realizarPeticionPersonalizada(
String metodo, String url, Object cuerpo) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Object> entity = new HttpEntity<>(cuerpo, headers);
return restTemplate.exchange(
url,
HttpMethod.valueOf(metodo.toUpperCase()),
entity,
String.class
);
}
Este enfoque es especialmente útil cuando necesitamos flexibilidad total en nuestras peticiones HTTP o cuando trabajamos con APIs que requieren configuraciones específicas de headers o métodos HTTP menos comunes.
¿Te está gustando esta lección?
Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.
Más de 25.000 desarrolladores ya confían en CertiDevs
El nuevo RestClient de Spring 6
RestClient es la nueva alternativa moderna introducida en Spring 6 que viene a reemplazar a RestTemplate con una API más fluida y expresiva. Este cliente HTTP mantiene la naturaleza síncrona de RestTemplate pero ofrece una interfaz más intuitiva basada en el patrón builder, similar a la que encontramos en WebClient pero sin la complejidad reactiva.
La principal ventaja de RestClient radica en su API declarativa que permite construir peticiones HTTP de manera más legible y mantenible. Además, incorpora mejores prácticas de configuración y manejo de errores desde su diseño inicial.
Configuración de RestClient
Para configurar RestClient en nuestra aplicación Spring Boot, creamos un bean utilizando el builder proporcionado por el framework:
@Configuration
public class RestClientConfig {
@Bean
public RestClient restClient() {
return RestClient.builder()
.baseUrl("https://jsonplaceholder.typicode.com")
.build();
}
}
Configuración con personalización avanzada:
@Bean
public RestClient restClientPersonalizado() {
return RestClient.builder()
.baseUrl("https://api.ejemplo.com")
.defaultHeader("User-Agent", "MiApp/1.0")
.defaultHeader("Accept", "application/json")
.build();
}
Una vez configurado, lo inyectamos en nuestros servicios de la misma manera que con RestTemplate:
@Service
public class ApiClientService {
private final RestClient restClient;
public ApiClientService(RestClient restClient) {
this.restClient = restClient;
}
}
Sintaxis fluida para operaciones GET
RestClient introduce una sintaxis más expresiva para realizar peticiones GET que resulta más legible que su predecesor:
@Service
public class UsuarioClientService {
private final RestClient restClient;
public UsuarioClientService(RestClient restClient) {
this.restClient = restClient;
}
public Usuario obtenerUsuario(Long id) {
return restClient.get()
.uri("/users/{id}", id)
.retrieve()
.body(Usuario.class);
}
}
Obtener respuesta completa con metadatos:
public ResponseEntity<Usuario> obtenerUsuarioConMetadatos(Long id) {
return restClient.get()
.uri("/users/{id}", id)
.retrieve()
.toEntity(Usuario.class);
}
Trabajar con listas de objetos:
public List<Usuario> obtenerTodosLosUsuarios() {
return restClient.get()
.uri("/users")
.retrieve()
.body(new ParameterizedTypeReference<List<Usuario>>() {});
}
Operaciones POST con sintaxis moderna
Las operaciones POST en RestClient siguen el mismo patrón fluido, proporcionando una experiencia más consistente:
public Usuario crearUsuario(Usuario nuevoUsuario) {
return restClient.post()
.uri("/users")
.contentType(MediaType.APPLICATION_JSON)
.body(nuevoUsuario)
.retrieve()
.body(Usuario.class);
}
Crear recurso y obtener respuesta completa:
public ResponseEntity<Usuario> crearUsuarioCompleto(Usuario nuevoUsuario) {
return restClient.post()
.uri("/users")
.contentType(MediaType.APPLICATION_JSON)
.body(nuevoUsuario)
.retrieve()
.toEntity(Usuario.class);
}
Operaciones PUT y DELETE simplificadas
RestClient simplifica las operaciones de actualización y eliminación con una sintaxis más clara:
public void actualizarUsuario(Long id, Usuario usuario) {
restClient.put()
.uri("/users/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.body(usuario)
.retrieve()
.toBodilessEntity();
}
public void eliminarUsuario(Long id) {
restClient.delete()
.uri("/users/{id}", id)
.retrieve()
.toBodilessEntity();
}
Actualización con respuesta:
public Usuario actualizarYObtenerUsuario(Long id, Usuario usuario) {
return restClient.put()
.uri("/users/{id}", id)
.contentType(MediaType.APPLICATION_JSON)
.body(usuario)
.retrieve()
.body(Usuario.class);
}
Configuración de headers por petición
RestClient facilita la configuración de headers específicos para cada petición de manera más intuitiva:
public Usuario obtenerUsuarioAutenticado(Long id, String token) {
return restClient.get()
.uri("/users/{id}", id)
.header("Authorization", "Bearer " + token)
.header("X-Request-ID", UUID.randomUUID().toString())
.retrieve()
.body(Usuario.class);
}
Headers múltiples con Consumer:
public Usuario obtenerUsuarioConHeadersPersonalizados(Long id) {
return restClient.get()
.uri("/users/{id}", id)
.headers(headers -> {
headers.set("Authorization", "Bearer mi-token");
headers.set("Accept-Language", "es-ES");
headers.set("X-Client-Version", "2.0");
})
.retrieve()
.body(Usuario.class);
}
Manejo avanzado de errores
Una de las mejoras más significativas de RestClient es su sistema de manejo de errores más sofisticado y expresivo:
public Usuario obtenerUsuarioConManejoDeErrores(Long id) {
return restClient.get()
.uri("/users/{id}", id)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, (request, response) -> {
if (response.getStatusCode() == HttpStatus.NOT_FOUND) {
throw new UsuarioNoEncontradoException("Usuario no encontrado: " + id);
}
throw new ErrorClienteException("Error del cliente: " + response.getStatusCode());
})
.onStatus(HttpStatusCode::is5xxServerError, (request, response) -> {
throw new ErrorServidorException("Error del servidor: " + response.getStatusCode());
})
.body(Usuario.class);
}
Manejo específico por código de estado:
public Optional<Usuario> buscarUsuarioSeguro(Long id) {
try {
Usuario usuario = restClient.get()
.uri("/users/{id}", id)
.retrieve()
.onStatus(status -> status.value() == 404, (request, response) -> {
// No lanzar excepción para 404, devolver Optional vacío
})
.body(Usuario.class);
return Optional.ofNullable(usuario);
} catch (Exception e) {
return Optional.empty();
}
}
Configuración de timeouts y reintentos
RestClient permite configurar timeouts y políticas de reintentos de manera más granular durante la construcción del cliente:
@Bean
public RestClient restClientConTimeouts() {
return RestClient.builder()
.baseUrl("https://api.ejemplo.com")
.requestFactory(clientHttpRequestFactory())
.build();
}
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(5000);
factory.setConnectionRequestTimeout(5000);
return factory;
}
Interceptores para funcionalidad transversal
RestClient soporta interceptores que permiten añadir funcionalidad transversal como logging, autenticación automática o métricas:
@Bean
public RestClient restClientConInterceptores() {
return RestClient.builder()
.baseUrl("https://api.ejemplo.com")
.requestInterceptor((request, body, execution) -> {
// Logging de peticiones
System.out.println("Realizando petición a: " + request.getURI());
// Añadir header automáticamente
request.getHeaders().add("X-Request-Time",
Instant.now().toString());
return execution.execute(request, body);
})
.build();
}
Interceptor para autenticación automática:
public ClientHttpRequestInterceptor authInterceptor() {
return (request, body, execution) -> {
String token = obtenerTokenActual();
request.getHeaders().setBearerAuth(token);
return execution.execute(request, body);
};
}
Comparación práctica con RestTemplate
Para ilustrar las diferencias, veamos el mismo caso de uso implementado con ambos clientes:
Con RestTemplate:
public Usuario crearUsuarioConValidacion(Usuario usuario, String token) {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + token);
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Usuario> entity = new HttpEntity<>(usuario, headers);
try {
return restTemplate.postForObject("/users", entity, Usuario.class);
} catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.BAD_REQUEST) {
throw new DatosInvalidosException("Datos de usuario inválidos");
}
throw e;
}
}
Con RestClient:
public Usuario crearUsuarioConValidacion(Usuario usuario, String token) {
return restClient.post()
.uri("/users")
.header("Authorization", "Bearer " + token)
.contentType(MediaType.APPLICATION_JSON)
.body(usuario)
.retrieve()
.onStatus(status -> status.value() == 400, (request, response) -> {
throw new DatosInvalidosException("Datos de usuario inválidos");
})
.body(Usuario.class);
}
La sintaxis de RestClient resulta más legible y expresiva, eliminando la necesidad de crear objetos HttpEntity manualmente y proporcionando un flujo de configuración más natural que facilita el mantenimiento del código.
Aprendizajes de esta lección
- Comprender la configuración y uso básico de RestTemplate para operaciones HTTP comunes.
- Aprender a realizar peticiones GET, POST, PUT y DELETE con RestTemplate, incluyendo manejo de parámetros y headers.
- Conocer la nueva API de RestClient en Spring 6 y su sintaxis fluida para realizar peticiones HTTP.
- Entender el manejo avanzado de errores y configuración de headers en RestClient.
- Comparar las ventajas y diferencias entre RestTemplate y RestClient para elegir la herramienta adecuada.
Completa SpringBoot y certifícate
Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.
Asistente IA
Resuelve dudas al instante
Ejercicios
Practica con proyectos reales
Certificados
Valida tus conocimientos
Más de 25.000 desarrolladores ya se han certificado con CertiDevs