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 creaar un WebClient, utilizaremos WebClient.builder(), pasándole a continuación la url base la que queremos llamar:
WebClient webClient = WebClient.builder()
.baseUrl("https://unaapi.com")
.build();
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í:
WebClient webClient = WebClient.builder()
.baseUrl("https://unaapi.com")
.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.