Há algum tempo ajudando uma amiga com um teste para um processo seletivo, me deparei com o desafio de consumir apis externas. Desafio dado, porque não buscar algo novo e aprender algo diferente? Foi assim que escolhemos o webclient do Spring Webflux para a tarefa.
Segundo a documentação do Spring, o Webflux é a stack reativa do Spring que foi adicionado a partir da versão 5 do framework que está presente na versão 2.0 ou superiores do Springboot. Com ele é possível fazer chamadas síncronas e assíncronas e tem sido utilizado em muitos projetos no mercado, inclusive no que eu trabalho atualmente! :)
Neste projeto vamos consumir apis do star wars a ideia é criar uma api de leilão para os veículos da franquia. Num primeiro momento vamos acessar os endpoints que retornam todos os veículos e os que devolvem os veículos individualmente.
Partiu?
Vamos começar entendendo como disponibilizar essa ferramenta na nossa api. Eu prefiro utilizar o gradle como ferramenta de build e ele foi o escolhido para esse projeto, outro ponto importante, foi que utilizei o spring initializr para criar o projeto, sendo assim foi preciso apenas adicionar a dependência do Spring Reactive Web. Caso prefira criar o projeto de outra maneira, pode adicionar a seguinte linha:
implementation 'org.springframework.boot:spring-boot-starter-webflux'
ao arquivo build.gradle.
Com o webflux disponível, podemos criar nosso webclient
que é quem vai orquestrar as nossas requisições a star wars api. Como se trata de uma biblioteca externa precisamos que o Spring possa tomar conta do seu ciclo de vida pra nós, e para isso a transformamos em um bean
. Isso pode ser feito na classe que está anotada com o @SpringBootApplication
como abaixo:
@SpringBootApplication
public class MyApplication {
@Bean
public WebClient webClient(WebClient.Builder builder) {
return builder.baseUrl("https://swapi.dev/api/")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Ou ainda criar uma classe de onde possamos configurar todos os beans da nossa aplicação, mantendo assim a classe com uma única responsabilidade, o que eu prefiro fazer. A minha ficou assim:
@Configuration
public class BeanConfiguration {
private String starWarsUrl;
public BeanConfiguration(StarWarsConfiguration starWarsConfiguration){
this.starWarsUrl = starWarsConfiguration.starWarsBaseUrl;
}
@Bean
public WebClient webClient(WebClient.Builder builder) {
return builder.baseUrl(starWarsUrl)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
}
}
Outra boa prática que gosto de seguir, é não manter informações que são dinâmicas 'chumbadas' no código, como é o caso da url base da api que queremos acessar. Para manter essas informações isoladas em um único local preferi guardar os dados no application.properties
do meu projeto:
starwars.base-url = https://swapi.dev/api/
starwars.vehicles = vehicles/
starwars.vehicle = vehicles/{id}/
E para acessar esses dados, usei mais uma classe de configuração. Aqui, a única função dela é disponibilizar as informações do application.properties
em variáveis.
@Configuration
@PropertySource("classpath:application.properties")
public class StarWarsConfiguration {
@Value("${starwars.base-url}")
public String starWarsBaseUrl;
@Value("${starwars.vehicles}")
public String starWarsVehicles;
@Value("${starwars.vehicle}")
public String starWarsVehicle;
}
Com todas as classes de configurações apresentadas, deixo uma imagem para que fique um pouco mais claro como elas se conversam.
Partindo para as nossas requisições, a ideia foi criar um component
cuja função seja apenas isso, acessar a api do star wars e retornar as informações que precisamos. Primeiro, disponibilizo através do construtor o bean do webclient
para utilizar nas chamadas e as variáveis que contem as urls que serão buscadas.
@Component
public class StarWarsWebClient {
private final String allVehiclesResource;
private final String vehicleResource;
private WebClient webClient;
StarWarsWebClient(WebClient webClient, StarWarsConfiguration starWarsConfiguration) {
this.webClient = webClient;
this.allVehiclesResource = starWarsConfiguration.starWarsVehicles;
this.vehicleResource = starWarsConfiguration.starWarsVehicle;
}
Feito isso é só criar o método que efetivamente vai fazer a requisição. Neste caso buscaremos primeiro todos os veículos e aqui é preciso fazer uma ressalva, se a api externa retornasse apenas uma lista com todos veículos disponíveis [{},{}]
, poderíamos utilizar um flux e o método ficaria mais ou menos assim:
public Flux<VehicleResponse> getAllVehicles() {
Flux<VehicleResponse> response = webClient
.get() // metodo http utilizado na requisicao
.uri(allVehiclesResource) //recurso a ser atingido encapsulado na variavel
.retrieve() //executa a chamada
.bodyToFlux(VehicleResponse.class); //transforma a resposta em um Flux do tipo VehicleResponse
return response;
}
No entanto a nossa api retorna um único objeto contendo em uma de suas propriedades uma lista de veículos {[]}
, nesse caso o método utilizado foi o método a seguir, e por enquanto focaremos só no Mono:
public AllVehicleResponse getAllVehicles() {
return webClient
.get()
.uri(allVehiclesResource)
.retrieve()
.bodyToMono(AllVehicleResponse.class)
.block();
}
E para atingir os veículos individualmente, o processo é quase o mesmo:
public VehicleResponse getVehicle(Integer id) {
return webClient
.method(HttpMethod.GET)
.uri(vehicleResource, id)
.retrieve()
.bodyToMono(VehicleResponse.class)
.block();
}
A diferença está na chamada da uri, a variável vehicleResource
contem o recurso seguido do id
que está sendo buscado vehicles/{id}/
e na frente dele, uma variável que de fato armazena esse id.
A partir daí qualquer classe service no nosso projeto pode acessar os caras que fazem essas requisições e trabalhar com seus dados. Como no exemplo abaixo:
@Service
public class VehiclesService {
private final VehicleRepository vehicleRepository;
private final StarWarsWebClient starWarsWebClient;
public VehiclesService(VehicleRepository vehicleRepository, StarWarsWebClient starWarsWebClient) {
this.vehicleRepository = vehicleRepository;
this.starWarsWebClient = starWarsWebClient;
}
public AllVehicleResponse getVehicles() {
return starWarsWebClient.getAllVehicles();
}
public VehicleResponse getVehicle(Integer id) {
return starWarsWebClient.getVehicle(id);
}
}
Fácil né? Nos próximos capítulos vamos explorar mais esses dados e montar nossa api de leilões! Quer ver o projeto no git? O link é esse aqui. Espero que tenham gostado.
Até!
Top comments (4)
Muito bom o artigo.
Parabéns ficou show
uaaaaau que demais Gle <3
Ficou incrível gleice