08.2 Aplicação Web com VRaptor 4 – Contato Controller

Olá, como vai você? Hoje vamos mexer no nosso 2° Controller. Aquele carinha que contém os nossos métodos de acesso ao sistema (Rest). Agora vamos escrever o Contato Controller.

Mas antes de começarmos, vamos aos recadinhos, que são sempre os mesmos, rs. Já estamos trabalhando no nosso projeto, caso tenha caído neste tutorial acidentalmente, pode ver a lista dos episódios anteriores na introdução que fiz para estes posts. E sempre lembre, você pode e deve acompanhar os códigos pelo meu github, segue o link ai em baixo:

https://github.com/pedro-hos/aprendendo-vraptor

 Antes de mais nada, crie a classe ContatoController, dentro do mesmo pacote que o controller anterior que fizemos está. Ele será quase idêntico ao anterior com uma diferença no value do @Path.

Outra semelhança será os construtores, com a diferença que vamos adicionar a classe Contatos que contem nossos métodos de acesso ao banco. Está na Parte 7.0 e 7.1 dos posts.

Agora chega de papo, vamos ao código.

package br.com.pedroHos.controller;

import static br.com.caelum.vraptor.view.Results.json;
import static br.com.caelum.vraptor.view.Results.status;

import javax.inject.Inject;

import br.com.caelum.vraptor.Consumes;
import br.com.caelum.vraptor.Controller;
import br.com.caelum.vraptor.Delete;
import br.com.caelum.vraptor.Get;
import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.Post;
import br.com.caelum.vraptor.Put;
import br.com.caelum.vraptor.Result;
import br.com.caelum.vraptor.serialization.gson.WithoutRoot;
import br.com.pedroHos.model.entities.contato.Contato;
import br.com.pedroHos.model.entities.contato.Telefone;
import br.com.pedroHos.model.entities.contato.TipoContato;
import br.com.pedroHos.model.repositories.contato.Contatos;

@Controller
@Path(value = "/contato")
public class ContatoController {
    private Result result;
    private Contatos contatos;
    
    /**
     * CDI eyes only
     * @deprecated
     */
    public ContatoController(){}
    
    @Inject
    public ContatoController( Result result, Contatos contatos ){
        this.result = result;
        this.contatos = contatos;
    }

}

Agora vamos aos métodos, o primeiro será bem parecido com o que pegamos os tipos dos telefones, para esse vamos pegar os tipos de contato.


//import omitdos

@Controller
@Path(value = "/contato")
public class ContatoController {

// construtores e objetos privados omitidos

    @Get
    @Path(value = { "/tipo", "/tipo/" })
    public void tipos() {
        result.use(json())
              .withoutRoot()
              .from(TipoContato.values())
              .serialize();
    }
}

Bem semelhante né? Diferenças apenas no .from() onde passamos o Enum TipoContato. Então, quando passarmos a URL http://localhost:8080/meus-contatos/contato/tipo no poster teremos uma resposta 200 ok como mostrado a seguir

poster_02_requisicao_e_resposta

Pronto, agora criar um método de criar novo contato:


//import omitdos

@Controller
@Path(value = "/contato")
public class ContatoController {
    
// construtores e objetos privados omitidos

    @Post
    @Path(value = {"/", ""})
    @Consumes(value = "application/json", options = WithoutRoot.class)
    public void novo( Contato contato ) {
        setPhone(contato);
        contatos.novo(contato);
        result.use(status()).created();
    }

    protected void setPhone(Contato contato) {
        if(contato.getTelefones() != null) {
            for (Telefone telefone : contato.getTelefones()) {
                telefone.setContato(contato);
            }
        }
    }
}

Temos um método @Post, ou seja, vamos inserir um contato novo. Passamos o objeto Contato como parâmetro do método. No @Path passamos os values normalmente, nada de novidade até então.

Temos uma anotação nova, o @Consumes, no value, passamos o “application/json” isso indica que esperamos no corpo um objeto json, e temos um options = WithoutRoot.class, isso indica que não queremos receber a raiz no corpo, bem semelhante ao método .withoutRoot() que fizemos nos métodos GET.

Temos também um método setPhone(contato), esse método pega todos os telefones passados pelo usuário e seta ele no objeto Telefone (não achei melhor maneira de fazer isso).

Enfim, criamos nosso contato chamando o método novo(contato) de Contatos, nosso reposotory. E retornamos o result, colocando o status() como created(), código 201 no HTTP status. Ah, status() também está sendo importado estaticamente de Result.status(), assim como o json().

Para testar no poster, temos que fazer:

{ 
    "nome": "Pedro Hos",  
    "email": "pedro-hos@outlook.com", 
    "tipoContato": "AMIGO",
    "telefones": [ 
        {
            "ddd": "12", 
            "telefone": "9999-9999", 
            "tipoTelefone": "CELULAR"
        }
     ]
}

poster_03_requisicao_e_resposta

Estamos quase acabando, faltam apenas 3 métodos, um para pegar todos os contatos ativos, um para editar os contatos e outro para desativar um contato.

Vamos abordar agora o método que trás todos os objetos ativos de contato. Ele é bem parecido com os métodos que pegavam os tipos de contato e de telefone, a diferença é que vamos passar no .from() o método de Contatos que pega todos os usuários ativos (todosAtivos()), esse método fizemos na segunda parte que criamos o repository. Veja o código:


//import omitdos

@Controller
@Path(value = "/contato")
public class ContatoController {

// construtores, métodos e objetos privados omitidos
    @Get
    @Path(value = {"/", ""})
    public void todos() {
        result.use(json())
              .withoutRoot()
              .from(contatos.todosAtivos())
              .include("telefones")
              .serialize();
    }
}

Vamos agora ao método que edita um contato, ele é bem parecido com o que cria, com algumas peculiaridades:

  1. Temos a anotação @Put em vez de @Post, porque por convenção esse é o verbo usado para editar um objeto;
  2. No value @Path temos uma coisa nova, passamos o “/{ id }”, isso indica que o id do contato a ser editado deve ser passado na URL. Esse id será recuperado como parâmetro. O Contato deve ser passado com as modificações desejadas no corpo da requisição;
  3. Em vez de salvarmos o objeto, usamos o método atualizar(contato) da classe Contatos;
  4. E em vez de retornar status(), 201 criado, retornamos 200, ou seja, ok();

Vamos ver o método então:


//import omitdos

@Controller
@Path(value = "/contato")
public class ContatoController {

// construtores, métodos e objetos privados omitidos
    @Put
    @Path(value = "/{id}")
    @Consumes(value = "application/json", options = WithoutRoot.class)
    public void editar(Contato contato, Long id) {
        contato.setId(id);
        setPhone(contato);
        contatos.atualizar(contato);
        result.use(status()).ok();
    }
}

Vamos agora então, fazer o teste no poster.

  • Passe a URL: http://localhost:8080/meus-contatos/contato/6. Obs: Lembre-se que no lugar do 6 deve vir o id que foi cadastrado quando fizemos o post
  • passar application/json no Content Type
  • E no corpo alterar algum coisa, tem que copiar o objeto json inteiro que fizemos no post e alterar algo.
  • Apertar o botão PUT do poster

Exemplo

poster_04_requisicao_e_resposta

Por fim vamos ao método que irá desativar um contato, ele deve ser anotado com @Delete e passar um {id} no parâmetro e na URL como fizemos com o método de atualizar. Ficará assim:


//import omitdos

@Controller
@Path(value = "/contato")
public class ContatoController {

// construtores, métodos e objetos privados omitidos

    @Delete
    @Path(value = "/{id}")
    public void remover(Long id) {
        contatos.desativarComId(id);
        result.use(status()).ok();
    }
}

Para testar no poste, temos que colocar o Id que queremos desativar na URL, como por exemplo: http://localhost:8080/meus-contatos/contato/6 Em seguida colocar DELETE no combo, e apertar o botão verde ao lado do combo, como por exemplo:

poster_05_requisicao_e_resposta

Obs: Pessoal, essa aplicação tem como principio ser bem básica, não vamos passar por validação, mensagem de erro, tratamento entre outras. Se fosse uma aplicação de produção deveria ser melhor avaliado esses conceitos. A ideia aqui é apresentar o VRaptor, suas facilidades e sua integração com o Angular js.

Pe-pe-pe-soal, era isso. Qualquer dúvida ou sugestão me contatem pelos comentário. Aquele abraço.

Anúncios

9 comentários

  1. Pingback: Aplicação Web com VRaptor 4 – Introdução | Pedro Hos
  2. Eduardo Dicarte · maio 5, 2015

    Opa,

    Eu tentei utilizar o foreach do Java 8 com a utilização de lambda e estourou a exceção java.lang.ArrayIndexOutOfBoundsException: 28523
    protected void setPhone(Contato contato) {
    if (contato.getTelefones() != null) {
    contato.getTelefones().stream().forEach(
    telefone -> telefone.setContato(contato));
    }
    }
    Agora, se eu utilizar o for tradicional não ocorre esse problema
    protected void setPhone(Contato contato) {
    if(contato.getTelefones() != null) {
    for (Telefone telefone : contato.getTelefones()) {
    telefone.setContato(contato);
    }
    }
    }

    Sabe me dizer se o erro é devido ao VRaptor? Simulei o loop com lambda com uma classe main e funcionou normalmente.

    • Eduardo Dicarte · maio 5, 2015

      O forEach é sem o stream()
      Somente:
      contato.getTelefones().forEach(telefone -> telefone.setContato(contato));
      Assim que testei 🙂

      • Pedro Henrique de Oliveira Silva · maio 5, 2015

        Bom dia Eduardo. Olha, acredito que para funcionar perfeitamente tenha que colocar a dependência do Java 8 do VRaptor. Tenta inserir

        br.com.caelum.vraptor
        vraptor-java8
        4.0.0.Final

        No pom.xml, se der certo me fale. Até mais

  3. Eduardo Dicarte · maio 5, 2015

    Pedro,

    Com a inclusão da dependência vraptor-java8 não funcionou, na verdade quando inclui a dependência não funcionou de jeito nenhum e a exceção lançada foi outra.

    Funcionou da seguinte forma:

    – Retirei o método setPhone da classe ContatoController
    – Criei uma outra classe e criei um método estático setPhone, com a loop forEach
    – Fiz a chamada do método setPhone dentro do método novo da classe ContatoController

    Seguem os códigos:

    public class T {
    public static void setPhone(Contato contato){
    if (contato.getTelefones()!=null){
    contato.getTelefones().forEach(telefone -> telefone.setContato(contato));
    }
    }
    }

    @Post
    @Path(value={“/”,””})
    @Consumes(value=”application/json”,options=WithoutRoot.class)
    public void novo(Contato contato){
    T.setPhone(contato);
    contatos.novo(contato);
    result.use(Results.status()).created();
    }

    Obs: O problema é indiferente à chamada do método “novo” na classe ContatoController, o erro ocorre mesmo realizando a chamada GET (http://localhost:8080/meus-contatos/contato/tipo)

    • Eduardo Dicarte · maio 5, 2015

      Uma outra observação, além de retirar o método setPhone, tive que retirar a dependência vraptor-java8 do pom para funcionar.

      • Pedro Henrique de Oliveira Silva · maio 5, 2015

        Show de bola Eduardo. Obrigado pelo feedback!

  4. David Correia da Silva · novembro 25, 2015

    Estou tendo o seguinte erro ao subir uma aplicação para o Wildfly

    11:11:03,591 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-8) MSC000001: Failed to start service jboss.deployment.unit.”redukApp.war”.INSTALL: org.jboss.msc.service.StartException in service jboss.deployment.unit.”redukApp.war”.INSTALL: JBAS018733: Failed to process phase INSTALL of deployment “redukApp.war”
    at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:166) [wildfly-server-8.2.1.Final.jar:8.2.1.Final]
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1948) [jboss-msc-1.2.2.Final.jar:1.2.2.Final]
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1881) [jboss-msc-1.2.2.Final.jar:1.2.2.Final]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [rt.jar:1.8.0_66]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [rt.jar:1.8.0_66]
    at java.lang.Thread.run(Thread.java:745) [rt.jar:1.8.0_66]

  5. Flavio Barbosa · janeiro 7, 2016

    Alguém já conseguiu deserializar classes que tem outras classes em seus atributos?

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s