Trabalhar com Java na web apresenta diversas possibilidades com frameworks MVC baseados em ações. O VRaptor é um desses frameworks, e em sua versão 4 utiliza o CDI 1.1 como base. Veremos neste artigo alguns dos princípios do framework e uma seleção de novidades da nova versão.
Controllers e Convenções
Tudo que se precisa fazer para criar um controller do VRaptor é adicionar a anotação @Controller. A partir daí o framework já utiliza suas convenções de URLs e JSPs, exigindo o mínimo de configurações. Veja um exemplo:
@Controller public class UsuarioController { public void lista() { ... } }
A convenção de URLs do VRaptor é nomeDoController/nomeDoMetodo; portanto o método lista() seria mapeado para a URL /usuario/lista. Repare que o sufixo Controller não é considerado na rota.
Outra convenção importante é da página JSP pra a qual o VRaptor vai fazer o dispatch. Ela é bem parecida com a convenção de URLs.O VRaptor procura a JSP no diretório:
WEB-INF/jsp/nomeDoController/nomeDoMetodo.jsp,
que neste caso será:
WEB-INF/jsp/usuario/lista.jsp.
Todos os métodos públicos dos controllers serão mapeados seguindo essas convenções, e passam a atender requests independente do verbo HTTP utilizado.
Pode-se explicitamente declarar qual tipo de requisição cada método vai atender, por exemplo utilizando a anotação @Get. Há ainda as anotações @Post, @Put e @Delete. Se preferir não utilizar a convenção de URLs, você pode passar a URL como value dessas anotações de verbo, ou utilizar a anotação @Path se o verbo for indiferente:
@Get("alguma/outra/url") public void lista(){ ... }
Recebimento de parâmetros
Pode-se receber parâmetros nos métodos do seu controller. De acordo com os parâmetros da requisição o VRaptor tenta popular esses valores. Isso pode ser feito com valores simples, como um long no caso do seguinte método:
@Get public void busca(long id){ … }
Se o request tiver algum parâmetro cujo nome seja id, ou seja, igual ao do parâmetro do método, o VRaptor tenta converter esse valor para o tipo esperado.
O método busca(), na primeira forma que foi escrito, pode ser acessado pela URL /usuario/busca?id=1, por exemplo. Há ainda como fazer isso utilizando uma URL parametrizada:
@Get("usuario/busca/{id}") public void busca(long id){ … }
Neste caso um exemplo de URL seria /usuario/busca/1.
Também podemos receber objetos mais complexos, como no caso do método adiciona() que recebe um objeto Usuario:
@Post public void adiciona(Usuario usuario){ ... }
Na view teríamos o formulario.jsp parecido com:
<form action=”/usuario/adiciona” method=”post”> <input type=”text” name=”usuario.nome”/> <input type=”text” name=”usuario.email”/> <input type=”submit” value=”Adicionar”> </form>
Exemplos de parâmetros do request para este formulário seriam:
usuario.nome = Rodrigo Turini usuario.email = rodrigoatturini@caelum.com.br ...
Manipulação do resultado
Para deixar os objetos dos métodos acessíveis pela página JSP podemos simplesmente retorná-los - da seguinte forma:
@Get public List<Usuario> lista(){ return usuarioDao.lista(); }
Como estamos retornando um List<Usuario>, o nome da variável acessada em sua view será ${usuarioList}. Se o retorno fosse um único objeto, a variável seria apenas ${usuario}.
Há outra forma de retornar objetos para a view: utilizando a interface especialista Result. Podemos pedir esse bean injetado em nosso controller e utilizar seu método include():
@Controller public class UsuarioController { @Inject private Result result; @Inject private UsuarioDao usuarioDao; @Get public void lista(){ List<Usuario> lista = usuarioDao.lista(); result.include(lista); result.include("usuarios", lista); } }
Na primeira chamada a include(), a variável incluída será ${usuarioList}, assim como no retorno do método. Porém repare que no segundo include() demos um nome para o objeto incluído; portanto será este o utilizado.
Existem diversos outros métodos no Result para nos auxiliar com o trabalho com a view. Por exemplo, veja como se pode devolver essa lista de objetos serializada em JSON:
@Get public void listaEmJSON(){ List<Usuario> usuarios = usuarioDao.lista(); result.use(json()).from(usuarios).serialize(); }
O método json() vem da classe Results, que possui outros métodos para resultados comuns - como xml(), html() e jsonp(). Há ainda o método representation(), que retorna de acordo com o accept format do request.
Facilmente configurável
Tudo isso que vimos é facilmente configurável. Como suas classes são gerenciadas pelo CDI (managed beans) podemos especializar qualquer componente do VRaptor.
Por exemplo, para mudar a view renderizada por padrão, ou mudar o local em que é procurada, especializamos o DefaultPathResolver:
@Specializes public class CustomPathResolver extends DefaultPathResolver { @Override protected String getPrefix() { return "/pasta/raiz/"; } }
Da mesma forma, pode-se mudar a convenção de URLs sobrescrevendo o PathAnnotationRoutesParser.
Integrando VRaptor com JPA
A integração com CDI também facilita o gerenciamento das classes que não são de nosso projeto. Integrar o VRaptor com JPA, por exemplo, é um processo simples. Pode-se escrever um producer para o EntityManager parecido com esse:
public class EntityManagerCreator { @Inject private EntityManagerFactory factory; @Produces @RequestScoped public EntityManager getEntityManager() { return factory.createEntityManager(); } public void destroy(@Disposes EntityManager em) { if (em.isOpen()) { em.close(); } } }
Com isso, em qualquer bean da sua aplicação, é possível injetar esse objeto. Por exemplo:
public class UsuarioDao { @Inject private EntityManager em; public void adiciona(Usuario usuario) { em.getTransaction().begin(); em.persist(usuario); em.getTransaction().commit(); } ... }
Pode-se criar um interceptor simples para fazer o open transaction in view, mas isso e diferentes outros recursos já foram implementados nos diversos plugins para o VRaptor 4. Veja alguns desses plugins nessa página da documentação.
Mais sobre o Vraptor
Mais informações sobre o framework podem ser obtidas em sua documentação, que conta com guias de 1 e 10 minutos, cookbooks enviados por usuários e um guia rápido para auxiliar na migração de versões anteriores. Pode ser útil também ler mais sobre a especificação do CDI 1.1 e todos seus recursos, que se integram ao núcleo do Vraptor.
Sobre o autor
Rodrigo Turini é bacharel em Ciência da Computação, desenvolvedor e instrutor pela Caelum. Desenvolve sistemas em Java para web e atualmente foca seus estudos em programação funcional e Java 8, assuntos coberto no livro que escreveu com Paulo SIlveira.