Consumindo uma API — Parte I

Somos S2IT
Feb 23, 2017 · 5 min read

Atualmente, a moda das arquiteturas de sistemas é focada em 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, claro) de API. Se não está muito claro para você, leia 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.

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, você 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, comente aqui e tentaremos fazer um post sobre).

Na pasta client-web tem um projeto com um formulário de consulta de usuários que faz a verificação por id ou por e-mail, 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 dá para fazer mil coisas para melhorar o código apresentado mas, talvez pela minha idade, algumas mudanças sejã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 após a mudança e depois veremos 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 fazer 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.

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 com 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 dado de alguma forma específica, 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 toda 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, então, se tiver sugestão, comente aqui.

Nossas máquinas de desenvolvimento são normalmente bem parrudas. O java é bem mais otimizado nessa versão 8 que na 5, por exemplo. Mas mesmo assim ainda precisa de uma quantidade considerável de máquina para rodar fluído. No nosso exemplo, temos dois aplicativos web rodando, mas num mundo microservice pode precisar de 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 tudo.

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, na nossa configuração podemos especificar para usar o service só quando o profile não for 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 for 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. Fique à vontade para retornar os dados que você achar legal. Se já trabalhou com SoapUI é parecido. Mockamos o retorno de um webservice, mas sem precisar de 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!

Continua…


Written by iundarigun

Originally published at localhost on February 23, 2017.

somosS2IT

www.s2it.com.br

    Somos S2IT

    Written by

    Espaço colaborativo de opiniões e ideias dos talentos da S2. Compartilhamos conhecimento para abrir caminhos para se aprender mais! www.s2it.com.br

    somosS2IT

    somosS2IT

    www.s2it.com.br

    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