Versiones comparadas

Clave

  • Se ha añadido esta línea.
  • Se ha eliminado esta línea.
  • El formato se ha cambiado.

...

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

Tendremos que añadir la siguiente dependencia a nuestro pom.xml:


Info

Con la configuración actual de las APIs, con el parent fundewebjs-api-parent (ver Creación y estructura de proyecto SpringBootMigración de APIs a Parent FundeWebJS).

No es necesario realizar ninguna configuración de librerías en pom.xml.

Bloque de código
languagexml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>

Crear un WebClient

Para creaar crear un WebClient, utilizaremos WebClient.builder(), pasándole a continuación la url base la que queremos llamar:

...

languagejava

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:

Bloque de código
@Configuration
public class WebClientConfig {

 	@Value( "${app.server.path}" )
	private String apiPath;

	@Bean
	public WebClient webClient(WebClient.Builder webClientBuilder) {
		return webClientBuilder
				.baseUrl(apiPath.concat("/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í:

Bloque de código
languagejava
WebClient webClient = WebClient.builder()return webClientBuilder
        .baseUrl(apiPath.concat("https://unaapi.com/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:

...

¡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


Para obtener las respuestas, disponemos de dos clases Mono y Flux, vamos a ver para qué usamos cada una.

Recibir un solo elemento: Mono

Como hemos visto en el ejemplo anterior, con .bodyToMono indicamos que se recibe un elemento, y la respuesta se mapeará a la clase que pongamos. De este modo, se podrá mapear directamente a un DTO propio si la estructura de la respuesta coincide con éste.

En el caso de que queramos obtener la respuesta como un Map con las claves y valores que hemos recibido, haremos uso de ParameterizedTypeReference, así:

Bloque de código
languagejava
.bodyToMono( new ParameterizedTypeReference<Map<String, Object>>() {} ).block();

Recibir una lista de elementos: Flux

En el caso de que vayamos a recibir una lista de elementos, podemos usar Flux. La forma de usarlo es muy parecida a Mono, pero utilizando .bodyToFlux en lugar de .bodyToMono, e indicando .collectList después para obtener la lista. Por ejemplo, para recibir una lista de Strings sería:

Bloque de código
languagejava
.bodyToFlux(String.class).collectList().block();

Y para recibir una lista de Maps, al igual que hemos visto en Mono:

Bloque de código
.bodyToFlux( new ParameterizedTypeReference<Map<String, Object>>() {} ).collectList().block();

Enviar JSON en el body de un POST

Para enviar un JSON como body de una llamada POST, lo haremos como hemos visto, pasándole un Map:

Bloque de código
languagejava
Map<String, String> bodyJson = new HashMap<>();
bodyJson.put("unaPropiedad", "Hola");
bodyJson.put("otraPropiedad", "Mundo");

String response = webClientSello.post().uri("/noseque")
        .body(Mono.just(bodyForm), Map.class).retrieve()
        .bodyToMono(String.class).block();

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:

Bloque de código
languagejava
        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:

...

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.

Documentación relacionada