Invocación API REST mediante un cliente Spring OpenFeign

Vamos a tratar de detallar hoy el modo de implementar la llamada a un servicio REST haciendo uso del framework de Spring. En concreto, la idea es analizar cómo se puede realizar esta invocación mediante la utilización del cliente OpenFeign de Spring Cloud. Aunque es cierto que disponemos de varias opciones a la hora de realizar la invocación de un servicio REST, la realidad es que Feign actualmente es el cliente recomendado para implementaciones de microservicios mediante Spring Cloud.

 

 

Invocación de Servicio REST mediante Feign

 

En primer lugar, ¿qué es un cliente Feign? Pues Spring Cloud OpenFeign, más conocido como Feign Client, es un cliente REST declarativo proporcionado por el ecosistema Spring Cloud. Este cliente permite implementar fácilmente llamadas API HTTP entre microservicios mediante la declaración de interfaces y el uso de anotaciones.

 

Originalmente fue desarrollado por Netflix (Netflix Feign), pero posteriormente fue adoptado e integrado en Spring Cloud. En el framework Spring se añadió compatibilidad con la configuración automática de Spring Boot y también se incorporaron funciones adicionales tales como las siguientes:

  • Integración con Spring Cloud Load Balancer, para permitir balanceo automático entre todas las instancias disponibles del servicio destino.
  • Integración con Eureka Discovery Service.
  • Circuit Breaker (mediante Hystrix o Resilience4j).

 

Hay que puntualizar que la creación del cliente Feign se realiza utilizando únicamente programación declarativa. La implementación posterior del cliente la realiza el framework de Spring a partir de las declaraciones configuradas. En una aplicación Spring Boot se pueden implementar diferentes clientes Feign, cada uno de ellos diseñado para invocar a las API de diferentes microservicios.

 

Servicio API REST destino

 

En primer lugar, debemos disponer una API REST destino a la que podamos invocar con nuestro cliente OpenFeign. Para ello, vamos a suponer que tenemos una API sencilla que nos devuelve el detalle de un producto. Esta API se resuelve invocando con un endpoint del siguiente modo:

http://localhost:8501/productodetalle/producto/{elemento}

 

Por ejemplo, si resolvemos el endpoint con el elemento "avellanas", la API nos devuelve un JSON del tipo siguiente:

{
    "solicitud""avellanas",
    "productoDetalle""Producto solicitado - avellanas"
}


Para que lo tengas más claro, el controlador de la API REST destino que vamos a invocar podría ser una clase similar a lo siguiente:

@RestController
@RequestMapping("/productodetalle")
public class ProductoDetalleApiController {
   
    @GetMapping("/producto/{elemento}")
    public ResponseEntity<ProductoDetalleDto> RestApi (@PathVariable("elemento") String elemento) {
       
        ResponseEntity<ProductoDetalleDto> response;
       
        String str = "Producto solicitado - " + elemento;
       
        ProductoDetalleDto producto = new ProductoDetalleDto();
        producto.setSolicitud(elemento);
        producto.setProductoDetalle(str);
       
        response = ResponseEntity.status(HttpStatus.OK).body(producto);
       
        return response;
       
    }
   
}

 

Como vemos, se devuelve un ResponseEntity cuyo body contiene una clase DTO (Data Transfer Object) del tipo siguiente "ProductoDetalleDto.java":

public class ProductoDetalleDto {

    private String solicitud;
    private String ProductoDetalle;
   
}


Teniendo en cuenta todo esto, ahora en nuestra aplicación Spring Boot el objetivo va a consistir en implementar un cliente Feign que recoja esa información y haga el tratamiento de la misma.


Creación del cliente API REST Feign Client

 

Dicho lo anterior, vamos a entrar en detalle y a ir revisando los pasos precisados para completar la configuración de un cliente OpenFeign en nuestra aplicación Spring Boot.


1º) En primer lugar, debemos incluir en el "pom.xml" de la aplicación las dependencias necesarias para poder hacer uso del cliente OpenFeign.

spring-cloud-starter-openfeign

 

En el apartado <dependencies> incluimos lo siguiente:

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>

 En el apartado <dependencyManagement> incluimos lo siguiente:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2024.0.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

 

En el "application.properties" únicamente vamos a establecer los parámetros "name" y "port" de la aplicación Spring Boot. 

spring.application.name=pedido-rapido-app

server.port=8510


Ten en cuenta que puedes elegir los nombres de paquetes y clases que prefieras, pero la estructura final de nuestro proyecto debería ser algo similar a lo siguiente:


 

2º) A continuación, para habilitar este cliente en nuestra aplicación, debemos añadir la anotación @EnableFeignClients en la clase principal de nuestro Spring Boot "PedidoRapidoAppApplication".

 

package com.tambolsa.pedido_rapido_app;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients(basePackages="com.tambolsa.pedido_rapido_app.feignclient")
public class PedidoRapidoAppApplication {

    public static void main(String[] args) {
        SpringApplication.run(PedidoRapidoAppApplication.class, args);
    }

}

 

3º) Llegamos ahora al punto clave del proceso. Debemos crearnos una interface que será el esqueleto del cliente REST. Aquí deberán configurarse los datos necesarios para poder realizar la comunicación con la API destino. Personalmente voy a denominar esta interface como "ProductoClient.java".

Esta interface debe estar anotada con el tag @FeignClient, que debe ser configurado especificando algunos parámetros

  • name: Nombre lógico que se le quiere asociar al cliente Feign.
  • url: URL de la API destino.
  • path: Endpoint con el que se debe invocar al controlador del servicio destino.

 

Dentro de la interface hay que definir los métodos precisados para realizar la invocación de la funcionalidad requerida. En nuestro ejemplo, creamos un método con un @GetMapping apuntando al controlador con endpoint "/producto/{elemento}". Este método enviará a la API destino un parámetro "elemento" de tipo String y recibirá una respuesta conteniendo un body de tipo "ProductoDetalleDto" (clase cuya estructura ya vimos al principio del post).


package com.tambolsa.pedido_rapido_app.feignclient;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import com.tambolsa.pedido_rapido_app.dto.ProductoDetalleDto;

@FeignClient(name="producto-detalle-api",url="http://localhost:8501",path="/productodetalle")
public interface ProductoClient {

    @GetMapping("/producto/{elemento}")
    public ResponseEntity<ProductoDetalleDto> getProductoRestApi (@PathVariable("elemento") String elemento);
   
}


Por si no te queda del todo claro, el cliente ProductoClient anterior está construyendo invocaciones con el parámetro de entrada "elemento" hacia el siguiente endpoint de la API destino:

http://localhost:8501/productodetalle/producto/{elemento}

 

API con URL dinámica: Hay que puntualizar que, en realidad, no es necesario hardcodear la URL del servicio destino. En este ejemplo lo estamos realizando simplemente porque se trata de un primer contacto con OpenFeign y por simplificar la configuración del mismo. En una aplicación real lo normal es que se utilicen APIs con URL dinámicas. En esos casos, para no hardcodear la URL del microservicio destino, tendremos que usar el nombre (en el campo "name") con el que dicho microservicio esté registrado en el Service Discovery que estemos usando (Eureka, Kubernetes o cualquier otro).


4º) Una vez configurado el cliente Feign, tocaría hacer uso del mismo en las clases correspondientes de nuestra capa de Servicio de la aplicación. 

En nuestro ejemplo vamos a definir una clase "PedidoRapidoService.java", donde invocaremos al cliente Feign para recuperar el ProductoDetalleDto devuelto por la API destino.


@Service
public class PedidoRapidoService {

    @Autowired
    private ProductoClient productoClient;
   
    public PedidoRapidoDto obtenerPedidoRapido(String elemento) {
       
        PedidoRapidoDto responsePedido = new PedidoRapidoDto();
       
        // Invocacion a la API destino mediante cliente Feign
        ResponseEntity<ProductoDetalleDto> responseProducto = productoClient.getProductoRestApi(elemento);
       
        // Extraccion de la respuesta devuelta por la API destino
        String producto = responseProducto.getBody().getProductoDetalle();
       
        String pedidoRapido = "Pedido rapido: " + producto;
       
        // Construccion de la respuesta con estructura PedidoRapidoDto
        responsePedido.setPedidoRapido(pedidoRapido);
        responsePedido.setProductoDetalle(producto);
   
        return responsePedido;
       
    }
   
}


Como vemos, en dicha clase hay que inyectar el cliente Feign que vamos a utilizar.

@Autowired

private ProductoClient productoClient;


Posteriormente, en la funcionalidad deseada ya podremos proceder a realizar la invocación de los métodos que hayamos configurado en nuestro cliente Feign (recordemos que dicha configuración la hicimos en la interface OpenFeign algunos pasos más arriba).

 

// Invocacion a la API destino mediante cliente Feign

ResponseEntity<ProductoDetalleDto> responseProducto = productoClient.getProductoRestApi(elemento);

 

Finalmente, nuestro servicio construye una respuesta haciendo uso de la estructura definida en la clase "PedidoRapidoDto.java". La estructura sería algo como lo siguiente:

 

public class PedidoRapidoDto {

    private String PedidoRapido;
    private String ProductoDetalle;
   
}

 

🔔 Balanceo de carga: Hay que tener en cuenta que OpenFeign tiene integración con Spring Cloud Load Balancer, por lo que en la interface no será necesario configurar ningún balanceo adicional (entre las instancias activas de la API destino). Lo que sí se podrá configurar es el tipo de LoadBalancer utilizado.

  • RoundRobinLoadBalancer: el balanceo se realiza con la técnica Round-Robin, esto es, rotación secuencial de una instancia a otra del servicio destino.
  • RandomLoadBalancer

 

5º) Finalmente, tendremos que implementar el Controller que haga uso de las transformaciones realizadas en los servicios y que devuelva el resultado al servicio invocante. En mi caso voy a denominar la clase como "PedidoRapidoController.java". Como siempre, esta clase deberá estar anotada con el tag @RestController.

 

@RestController
@RequestMapping("/pedidorapido")
public class PedidoRapidoController {
   
    @Autowired
    PedidoRapidoService pedidoRapidoService;
   
    @GetMapping("/obtener/{producto}")
    public ResponseEntity<PedidoRapidoDto> obtenerPedidoRapido(@PathVariable("producto") String producto) {
       
        // Recibimos el producto de entrada
        String elemento = producto;
        PedidoRapidoDto str = new PedidoRapidoDto();
       
        ResponseEntity<PedidoRapidoDto> response;
       
        // Llamamos al Servicio para obtener el Pedido para el elemento enviado
        str = pedidoRapidoService.obtenerPedidoRapido(elemento);
       
        // Construimos la respuesta con el Pedido obtenido
        response = ResponseEntity.status(HttpStatus.OK).body(str);
       
        return response;
       
    }
   
}

 

Como se aprecia en la clase y en el método, aquí estamos construyendo una lógica que deberá ser invocada a través de un endpoint del tipo:

http://localhost:8510/pedidorapido/obtener/{producto}

 

Prueba del cliente OpenFeign


5º) Una vez implementado todo lo anterior, ya podemos realizar una prueba para ver si nuestro cliente OpenFeign funciona correctamente. Para ello, nos vamos a Postman y nos creamos un request para realizar invocaciones al endpoint.

http://localhost:8510/pedidorapido/obtener/{producto}

En concreto, vamos a utilizar el endpoint con el producto "avellanas".

http://localhost:8510/pedidorapido/obtener/avellanas

 

La respuesta obtenida debería ser similar a lo siguiente:

{
    "productoDetalle""Producto solicitado - avellanas",
    "pedidoRapido""Pedido rapido: Producto solicitado - avellanas"
}

 

En el Postman nos deberá aparecer esta ventana:


 

Y si no ha habido ningún problema, con los pasos anteriores ya debería quedar configurada correctamente una invocación básica a una REST API mediante un cliente OpenFeign. A partir de aquí la cosa se puede ir complicando de manera casi indefinida, pero por lo menos aquí ya has dado tu primer paso.

 

Conclusión

 

En líneas generales, el cliente OpenFeign debe ser usado siempre que tengas que trabajar con microservicios que se comuniquen a través de API REST, ya que esta herramienta permite implementar un código más claro y legible. Por otra parte, como ya hemos comentado, Spring Cloud OpenFeign permite integración casi automática con otros componentes de Spring Cloud relacionados con Load Balancing, Resilience o Tracing. De hecho, es la solución más recomendada para la comunicación REST entre servicios del framework Spring Cloud.


¡Nos vemos en el siguiente post!

Saludos.


Comentarios

Entradas populares de este blog

Componentes y Ventanas de Java Swing

Creación de Webservice SOAP mediante Anotaciones

Configurar Apache Tomcat en Eclipse