Previamente utilizábamos RestTemplate para hacer un cliente REST en Spring Boot, pero ésta se va a quedar obsoleta, por lo que a partir de ahora tendremos que usar WebClient.
Dependencias
IMPORTANTE
Para utilizar WebClient la versión del parent de nuestra aplicación (está en el pom.xml) debe ser igual o superior a 2.6.0, si no pueden surgir conflictos con otras librerías.
Tendremos que añadir la siguiente dependencia a nuestro pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Crear un WebClient
Para crear un WebClient, tendremos que crearlo como un bean en un archivo de configuración. Nos crearemos una clase para ello, WebClientConfig, donde crearemos cada webClient que necesitemos de esta manera, con la url correspondiente:
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient(WebClient.Builder webClientBuilder) {
return webClientBuilder
.baseUrl("https://apidesa.um.es/acade/expedienteacademico-api/private/v1.0").build();
}
}
Si tenemos un solo WebClient, simplemente lo usaremos con @Autowired, si tenemos varios, además de @Autowired tendremos que añadir @Qualifier("nombre del bean").
Añadir headers por defecto
Si queremos añadir headers por defecto, como por ejemplo que el tipo del body es JSON, y que acepta también una respuesta en JSON, lo haremos así:
return webClientBuilder
.baseUrl("https://apidesa.um.es/acade/expedienteacademico-api/private/v1.0")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.build();
Hacer una petición y recibir una respuesta
Una vez creado el WebClient, podremos hacer peticiones indicando el método REST, la uri a la que se llama, los headers que queramos añadirle (si queremos añadirle alguno), el body que le pasamos si es necesario (con Mono.just(..) para pasarle el objeto que sea, y su clase), y obtener la respuesta:
String response = webClient.post()
.uri("/recurso")
.header("nombreHeader", "Valor")
.body(Mono.just(Objeto), TipoObjeto.class)
.retrieve()
.bodyToMono(String.class)
.block();
También podemos indicar un timeout para nuestra llamada:
String response = webClient.post()
.uri("/recurso")
.header("nombreHeader", "Valor")
.body(Mono.just(Objeto), TipoObjeto.class)
.retrieve()
.bodyToMono(String.class)
.timeout(Duration.ofSeconds(3))
.block();
¡Ojo! Por defecto, las llamadas con WebClient son asíncronas, no bloqueantes. La instrucción block() del final sirve para hacerlas bloqueantes, para esperar a la respuesta antes de seguir.
Body con Content-Type: application/x-www-form-urlencoded
Para este caso, hay que meter el body utilizando BodyInserters.fromFormData, pasándole un MultiValueMap. Por ejemplo:
MultiValueMap<String, String> bodyForm = new LinkedMultiValueMap<>();
bodyForm.add("login", "correo@prueba.com");
bodyForm.add("clave", "AAERRasdfasdf1asd2asdfasdf:34adsad");
bodyForm.add("nomape", "MESSI");
bodyForm.add("tipo", "externa");
bodyForm.add("validar", "no");
String response = webClient.post()
.uri(metodo).body(BodyInserters.fromFormData(bodyForm))
.retrieve()
.bodyToMono(String.class)
.block();
Gestionar código de respuesta
Para gestionar llamadas fallidas, o según el código que se obtenga, utilizamos onStatus:
String response = webClient.post()
.uri("/recurso")
.header("nombreHeader", "Valor")
.body(Mono.just(Objeto), TipoObjeto.class)
.retrieve()
.onStatus(HttpStatus::is5xxServerError, clientResponse -> {
log.error("Error, código {}", clientResponse.statusCode());
throw new UnaExcepcion("HTTP Status 500 error");
})
.bodyToMono(String.class)
.timeout(Duration.ofSeconds(3))
.block();
A destacar es que no acepta los códigos de error con las constantes tipo HttpStatus.FORBIDDEN, pide un predicado de tipo HttpStatus::metodo. Normalmente se utilizarán métodos como el mostrado ahí para error 500 (5xx más bien), y también da opción para 4xx (is4xxClientError), para 3xx (is3xxRedirection), o para 2xx (is2xxSuccessful). Si autocompletamos podremos ver todas las opciones.