Consumindo uma API — Parte I

iundarigun
Feb 23, 2017 · 5 min read

Atualmente, a moda das arquiteturas de sistemas é focada para microservice. Isso comporta uma série de vantagens, mas ao mesmo tempo que desaparecem certos cenários, outros menos frequentes antigamente, aparecem mais. No caso que nos ocupa é o consumo (e criação de, claro) de API. Se não está muito claro para você, da uma lida na entrada sobre O que é um microservice.

Escolhi a palavra cenário para não escrever problema, pois de fato não é um problema, só um cenário diferente inerente à mudança de paradigma. O objetivo desse post é mostrar sem aprofundar uma ferramenta interessante para o consumo de API e uma possível configuração para ambientes de desenvolvimento. Numa segunda parte, mostraremos uma possibilidade para melhorar a performance de nossa aplicação cliente.

O exemplo

Como sempre, os exemplos estão publicados no github. Então, já sabe como fazer checkout:

git clone https://github.com/iundarigun/ws-cache.git
> cd ws-cache
> git checkout feign

Na pasta, vai encontrar dois projetos, spring-boot of course. O server-api é só uma aplicação rest com uns serviços expostos para consulta de pessoas, por identificador e por nome. Coloquei um swagger para fazer a interface mais amigável (se não conhece swagger, comenta aqui e tentamos fazer um post sobre).

Na pasta client-web, tem um projeto com um formulário de consulta de usuários, que verifica o usuário por id ou por email consumindo a API.

A continuação, segue a parte do consumo do webservice no controller da aplicação cliente:

@RequestMapping(value = "/person/search", method = RequestMethod.GET)
public ModelAndView getPerson(PersonForm form){
ModelAndView modelAndView = new ModelAndView("person");
if (form.getId()!=null){
RestTemplate restTemplate = new RestTemplate();
PersonVO personVO = restTemplate.getForObject("http://localhost:1980/person/id/" + form.getId(), PersonVO.class);
modelAndView.addObject("person", personVO);
}
else if (StringUtils.isNotBlank(form.getEmail())){
RestTemplate restTemplate = new RestTemplate();
PersonVO personVO = restTemplate.getForObject("http://localhost:1980/person/email/" + form.getEmail() + "/", PersonVO.class);
modelAndView.addObject("person", personVO);
}
else{
return new ModelAndView("index");
}
return modelAndView;
}

Cara, o RestTemplate é maravilhoso. Adoro. Antes disso a gente usava aquele HttpClient e HttpMethod que era um inferno. Mas, assim, mesmo enxugando o código, não sei, como que não é tão fácil de ler. Claro, ainda da para fazer mil coisas para melhorar o código apresentado, mas talvez por minha idade, algumas mudanças são mais difíceis. Até hoje usamos aquelas interfaces injetadas onde só invocamos o método desejado. Na época que aprendi isso, a promessa sempre foi : “Faz por interface, pois a sua implementação pode mudar” (ler com voz grossa de professor de 50 anos que não programou uma linha na vida).

Aqui um caso disso! Vamos usar uma interface então. Vou mostrar como fica nosso código depois da mudança e depois vemos como fazer:

@Autowired
private PersonService personService;
@RequestMapping(value = "/person/search", method = RequestMethod.GET)
public ModelAndView getPerson(PersonForm form){
ModelAndView modelAndView = new ModelAndView("person");
if (form.getId()!=null){
PersonVO personVO = personService.getById(form.getId());
modelAndView.addObject("person", personVO);
}
else if (StringUtils.isNotBlank(form.getEmail())){
PersonVO personVO = personService.getByEmail(form.getEmail());
modelAndView.addObject("person", personVO);
}
else{
return new ModelAndView("index");
}
return modelAndView;
}

Não melhorou? Claro que ainda não vimos a implementação, onde poderíamos estar fazendo o mesmo que fazia o código anterior, mas se você não acredita na mágica, pode parar de ler. E de assistir Netflix, pois o cara que usaremos foi criado por eles, e depois evoluído pela comunidade.

Apresentando o Feign

Passo 1: Incluir as dependências do feign-core e do feign-gson:

compile group: 'io.github.openfeign', name: 'feign-core', version: feignVersion
compile group: 'io.github.openfeign', name: 'feign-gson', version: feignVersion

Passo 2: Alterar a interface para especificar o caminho relativo:

@RequestLine("GET /person/id/{id}")
PersonVO getById(@Param("id") Long id);
@RequestLine("GET /person/email/{email}/")
PersonVO getByEmail(@Param("email") String email);

Não parece um pouco o spring-data?

Passo 3: Criar um bean com a configuração:

@Configuration
public class ClientConfig {
@Bean
public PersonService personService(){
return Feign.builder()
.decoder(new GsonDecoder())
.target(PersonService.class, "http://localhost:1980");
}
}

E voilà! Já temos nosso consumidor pronto!

Exato, não existe “implementação” para nossos métodos, apenas uma configuração geral para nossa classe. Não é nosso objetivo aprofundar no feign nesse momento. Para mais informação pode consultar a documentação no Github: https://github.com/OpenFeign/feign. Só duas dicas remarcáveis antes de continuar:

  • Só colocamos o decoder. Se precisar enviar json (por exemplo, criar novo usuário fazendo post e enviando um objeto), devemos incluir um encoder no builder. E se queremos codificar e decodificar um tipo de dato de alguma forma especifica, podemos incluir no encoder e decoder nosso próprio serializer e deserializer. Sim, você também passou por problemas de conversão de datas. E sabe disso. Não minta!
  • Ao especificar um decoder para todo a interface, a solução complica quando tem dois métodos com formato de retorno diferentes. Por exemplo, um voltando json, outro voltando um arquivo em byte[]. Claro que não aprofundei para ver possíveis soluções, ou seja, se tiver sugestão, comenta ai.

Brincando com os Profiles

Nossas máquinas de desenvolvimento são normalmente bem parrudas. O java é bem mais optimizado nessa versão 8 que na 5 por exemplo, mas mesmo assim precisa ainda uma quantidade considerável máquina para rodar fluido. No nosso exemplo, temos dois aplicativos web rodando, mas num mundo microservice pode precisar informações de várias fontes. Nesse cenário fica difícil desenvolver pois temos que subir todos os aplicativos de API e, além de ficar com a máquina lenta, é meio chato ter que ficar subindo todos os aplicativos.

Vamos resolver isso de um jeito simples. Vamos usar os profiles de Spring. Inicialmente, vamos adicionar no nosso arquivo de configurações o profile usado.

spring.profiles.active=local

Poderíamos usar uma variável de ambiente ou outro nome para o profile, pois é livre, mas para o exemplo, essa servirá. Agora, no nossa configuração podemos especificar para usar o service só quando o profile não seja local (ou especificar um profile diferente):

@Profile("!local")
@Bean
public PersonService personService(){
return Feign.builder()

E podemos criar uma classe PersonServiceImpl, implementando nosso serviço e marcando-a como Bean só quando profile ativo seja local.

@Profile("local")
@Service
public class PersonServiceImpl implements PersonService {
@Override
public PersonVO getById(@Param("id") Long id) {
if (id <= 3) {
return new PersonVO(id,//
"test" + id + "@devcave.com.br",//
"Teste " + id + " devcave");
}
return null;
}
@Override
public PersonVO getByEmail(@Param("email") String email) {
if (email.contains("devcave")) {
return new PersonVO(1L, email, "Teste devcave");
}
return null;
}
}

A implementação é livre, fica a vontade para retornar os dados que você achar legal. Se já trabalhou com SoapUI, é parecido. Mockamos o retorno de um webservice, mas sem precisar um serviço isolado para isso. Fica a dica, usa para desenvolver, mas não esqueça de testar também apontando para os serviços reais, pois com o profile local só garantimos os fluxos felizes!

Continuará…

Dev Cave

O ponto de encontro dos trogloditas

iundarigun

Written by

Dev Cave

Dev Cave

O ponto de encontro dos trogloditas

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade