out 24 2010

Spring MVC 3 na Prática com Bean Validation

Category: Spring,Spring MVC 3.0Edson Gonçalves @ 20:56

Olá Pessoal, tudo bom? Como vão vocês?

Este é o terceiro artigo da série Spring MVC 3.0. Se vocês não tiveram um contato inicial com o framework, recomendo ver este artigo primeiro.

Para acompanhar esta terceira parte, recomendo ter criado o projeto do segundo artigo.

Como sempre, dúvidas e críticas são bem vindas.

Alterando o CRUD criado com o Spring MVC

O projeto neste artigo é o mesmo do segundo artigo da série. Entretanto, faremos algumas alterações para que passe a utilizar validações do Bean Validation.

Adicionando as bibliotecas ao projeto utilizando o Maven

Mais uma vez, recorreremos ao Maven para adicionar as bibliotecas que necessitamos. Neste caso, teremos que baixar o Hibernate Validator. Para utilizar o Bean Validation, utilizaremos dois JARs: hibernate-validator-4.0.2.GA.jar e validation-api-1.0.0.GA.jar.

Abram o arquivo pom.xml , encontrado na view Package Explorer para que possamos adicionar as configurações que necessitamos.

Criando a propriedade

Na aba Overview, em Properties, cliquem no botão Create. Na caixa de diálogo Add property, preencham como na Figura 1.

Figura 1 – Criação da property da versão do hibernate validator

Criando a dependência

Com a propriedade definida para informar qual versão desejamos utilizar, no qual o Maven deverá baixar, resta configurar as dependências.

Na aba Dependencies, cliquem no botão Create e preencham conforme a Figura 2 ilustra.

Figura 2 – A dependência do Hibernate Validator

Alterando a entidade Contato

A entidade do artigo, chamada de Contato, será a primeira coisa que iremos modificar no projeto.

Graças a JSR 303, chamada de Bean Validation, podemos anotar as entidades com validações. Com as anotações de Bean Validation na entidade, concentramos a validação em um único local, de forma padronizada, tornando possível portar estas validações para as classes controladoras do Spring MVC.

A Listagem 1 exibe  a entidade Contato modificada. Note as anotações de validação onde colocamos as mensagens de erro embutidas. Mais adiante iremos capturá-las para exibir o problema ao usuário.

Listagem 1. A entidade Contato com anotações Bean Validation.

package br.com.integrator;

import javax.persistence.*;
import javax.validation.constraints.*;

@Entity
@Table(name = "contato")
public class Contato {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id")
	private Long id;
	@NotNull
	@Size(min=5, message="O nome não pode ter menos que 5 caracteres!")
	private String nome;
	@NotNull
	@Pattern(regexp = "^[\\w\\-]+(\\.[\\w\\-]+)*@([A-Za-z0-9-]+\\.)+[A-Za-z]{2,4}$", message="E-mail com formato incorreto.")
	private String email;
	@Pattern(regexp = "\\(?\\b([0-9]{2})\\)?[-. ]?([0-9]{4})[-. ]?([0-9]{4})\\b", message="Telefone em formato incorreto")
	private String telefone; 

     //getters e setters
 }

A infra-estrutura definida pela JSR 303 nos permite descrever as restrições, usando anotações no modelo de classes de persistência, como definir se um campo aceitará uma quantidade mínima de caracteres ou se não aceitará nulo, por exemplo.

Cada anotação é associada a uma validação, verificando se a instância da entidade anotada obedece à regra ou não.

A Tabela 1 apresenta todas as anotações possíveis de serem usadas e suas funcionalidades.

Annotation O que faz?
@AssertFalse Checa se a propriedade anotada é falsa.
@AssertTrue Checa se a propriedade anotada é verdadeira.
@DecimalMax(value=) A propriedade anotada precisa ser um número, cujo valor deve estar menor ou igual ao valor máximo previsto. O parâmetro value é a representação em string do valor máximo aceito de acordo com o formato representado em BigDecimal. Suporta tipos como BigDecimal, BigInteger, String, byte, short, int, long e os respectivos wrappers de tipos primitivos.
@DecimalMin(value=) A propriedade anotada precisa ser um número, cujo valor deve estar maior ou igual ao valor mínimo previsto. O parâmetro value é a representação em string do valor mínimo de acordo com a representação de sequência de BigDecimal. Suporta tipos como BigDecimal, BigInteger, String, byte, short, int, long e os respectivos wrappers de tipos primitivos.
@Digits(integer=, fraction=) Verifica se a propriedade possui a quantidade de dígitos antes e depois do separador de casa decimal. Por exemplo: @Digits(integer=9, fraction=2) significa que espera-se 9 dígitos inteiros e 2 dígitos fracionários. Suporta os tipos: BigDecimal, BigInteger, String, byte, short, int, long e os respectivos wrappers de tipos primitivos.
@Future Checa se a data está no futuro. Suporta os tipos java.util.Date e java.util.Calendar.
@Max(value=) Verifica se o valor é menor ou igual ao valor anotado. Suporta os tipos: BigDecimal, BigInteger, String, byte, short, int, long e os respectivos wrappers de tipos primitivos.
@Min(value=) Verifica se o valor é maior ou igual ao valor anotado. Suporta os tipos: BigDecimal, BigInteger, String, byte, short, int, long e os respectivos wrappers de tipos primitivos.
@NotNull Checa se o valor anotado não é nulo (null). Uma String cujo valor seja vazio (“”) vai passar.
@Null Checa se o valor anotado é nulo (null).
@Past Checa se uma data está no passado. Suporta os tipos java.util.Date e java.util.Calendar.
@Pattern(regex=, flag=) Checa se a propriedade obedece à expressão regular.
@Size(min=, max=) Confere se a quantidade de elementos está entre o mínimo e o máximo, suportando tipos como: Strings, Collections, Maps e arrays.
@Valid Impõe uma validação recursiva aos objetos associados. Digamos que, no bean Post, houvesse a anotação @Valid no atributo comments. Já na classe Comment, temos um atributo de validação anotado. Se um valor transmitido a comments, de Post, não estiver de acordo com a validação existente na classe Comment, de acordo com o atributo anotado, um erro será gerado em tempo de execução. A notação @Valid está sendo usada no PostController para validar os erros existentes em Post ou Comment.

Tabela 1. Anotações e regras para criar restrições (Constraints).

Alterando o controlador

Alteraremos a classe ContatoController, criada no pacote br.com.integrator.web, de acordo com o mostrado na Listagem 2.

Listagem 2. A classe ContatoController alterada.

package br.com.integrator.web;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import br.com.integrator.dao.ContatoDAO;
import br.com.integrator.Contato;

@Controller
@RequestMapping("/contato/**")
public class ContatoController {
@Autowired
private ContatoDAO contatoDao;

@RequestMapping(value = "/contato/{id}", method = RequestMethod.GET)
public String show(@PathVariable("id") Long id, ModelMap modelMap) {
modelMap.addAttribute("contato", contatoDao.find(id));
return "contato/show";
}

@RequestMapping(value = "/contato", method = RequestMethod.GET)
public String list(ModelMap modelMap) {
modelMap.addAttribute("contatos", contatoDao.findAll());
return "contato/list";
}

@RequestMapping(value = "/contato/{id}", method = RequestMethod.DELETE)
public String delete(@PathVariable("id") Long id) {
contatoDao.remove(contatoDao.find(id));
return "redirect:/contato";
}

@RequestMapping(value = "/contato/form", method = RequestMethod.GET)
public String form(ModelMap modelMap) {
modelMap.addAttribute("contato", new Contato());
return "contato/create";
}

@RequestMapping(value = "/contato", method = RequestMethod.POST)
public String create(@Valid Contato contato, BindingResult result) {
if (result.hasErrors())
return "contato/create";

contatoDao.persist(contato);
return "redirect:/contato";
}

@RequestMapping(value = "/contato/{id}/form", method = RequestMethod.GET)
public String updateForm(@PathVariable("id") Long id, ModelMap modelMap) {
modelMap.addAttribute("contato", contatoDao.find(id));
return "contato/update";
}

@RequestMapping(method = RequestMethod.PUT)
public String update(@Valid Contato contato, BindingResult result) {
if (result.hasErrors())
return "contato/update";

contatoDao.merge(contato);
return "redirect:/contato";
}

}

Como visto na Listagem 1, as anotações @Size e @Pattern possuem o atributo message, que capturamos pelo Spring MVC na classe ContatoController (Listagem 2)  – através da classe javax.validation.Valid (anotação @Valid). Assim como a anotação @Controller, as anotações pertencentes a Bean Validation são obtidas graças a adição do elemento <mvc:annotation-driven />. Isto permite que as informações anotadas sejam injetadas onde a validação é necessária na sua aplicação.Caso haja um erro, o método hasErrors(), de org.springframework.validation.BindingResult, retorna true, nos possibilitando conduzir a navegação da página, com seus respectivos erros, inclusive paralisando a ação ao qual se encontra. A utilização de hasErrors() ocorre em ContatoController, nos métodos create() e update() com o seguinte if:

if (result.hasErrors())

//executa uma ação para conduzir a página com os erros ao usuário

As informações recebidas da validação pela classe Controller são transmitidas para o formulário e capturadas. Veja como isto ocorre na descrição da Listagem 3.

Alterando as views

Temos três páginas que representam nosso CRUD feitas. Estas páginas foram criadas dentro do diretório chamado WEB-INF/jsp/contato(fisicamente na ferramenta: src/main/webapp/WEB-INF/jsp/contato). Teremos que alterá-las para permitir que os erros sejam exibidos.

O formulário de cadastro

Abra o arquivo create.jsp e altere como na Listagem 3, adicionando as tags  <form:errors />.

Listagem 3. A página create.jsp.

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
 <style type="text/css" media="screen">
 @import url("<c:url value="/static/styles/style.css"/>");
 </style>
<title>Cadastrar</title>

</head>
<body>
 <div id="wrap">

 <div id="menu">
 <%@ include file="/menu.jsp" %>
 </div>
 <div id="main">
 <div id="body">
 <c:url var="url" value="/contato" />
 <form:form action="${url}" method="POST" modelAttribute="contato">
 <div>
 <label for="nome">Nome:</label>
 <form:errors path="nome" cssClass="errors"/><br />
 <form:input cssStyle="width:250px" maxlength="30" path="nome" size="30"/>

 </div>
 <br/>
 <div>
 <label for="email">Email:</label>
 <form:errors path="email" cssClass="errors"/><br />
 <form:input cssStyle="width:250px" maxlength="30" path="email" size="30"/>

 </div>
 <br/>
 <div>
 <label for="telefone">Telefone:</label>
 <form:errors path="telefone" cssClass="errors"/><br />
 <form:input cssStyle="width:250px" maxlength="30" path="telefone" size="20"/>
</div>
 <br/><div>
 <input id="criar" type="submit" value="Criar Contato"/>
 </div>
 </form:form>
 </div>
 </div>
 </div>
</body>
</html>
A tag  <form:errors /> foi utilizada na Listagem 3 com dois parâmetros: path e cssClass. No parâmetro path transmitimos o  atributo da classe Contato, validada pelas anotações de Bean Validation, onde receberá a mensagem.

Figura 3 – Processo de validação partindo da anotação no JavaBean Contato

Podemos ter uma idéia de como ficará a página create.jsp depois das alterações visualizando a Figura 4.

Figura 4 – Validação transmitida para a página de JSP

Figura 4 – Validação transmitida para a página de JSP

Alterando a página de atualização de dados

Como feito na página create.jsp, a página update.jsp também receberá as tags <form:errors />. A Listagem 4 exibe a página com as mudanças, na íntegra.

Listagem 4. A página update.jsp.

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
 <style type="text/css" media="screen">
 @import url("<c:url value="/static/styles/style.css"/>");
 </style>
<title>Atualizar</title>
</head>
<body>
 <div id="wrap">
 <div id="menu">
 <%@ include file="/menu.jsp" %>
 </div>
 <div id="main">
 <div id="body">
 <c:url var="url" value="/contato/${contato.id}" />
 <form:form action="${url}" method="PUT" modelAttribute="contato">
 <div>
 <label for="nome">Nome:</label>
 <form:errors path="nome" cssClass="errors"/><br />
 <form:input cssStyle="width:250px" maxlength="30" path="nome" size="30"/>
 </div>
 <br/>
 <div>
 <label for="email">Email:</label>
 <form:errors path="email" cssClass="errors"/><br />
 <form:input cssStyle="width:250px" maxlength="30" path="email" size="30"/>
 </div>
 <br/>
 <div>
 <label for="telefone">Telefone:</label>
 <form:errors path="telefone" cssClass="errors"/><br />
 <form:input cssStyle="width:250px" maxlength="30" path="telefone" size="20"/>
 </div>
 <br/>
 <div>
 <input id="atualizar" type="submit" value="Atualizar Contato"/>
 </div>
 <form:hidden path="id"/>
 </form:form>
 </div>
 </div>
 </div>
</body>
</html>

O projeto para download

Clique aqui para baixar o projeto e alterá-lo como desejar.

Considerações finais

Com as validações, boa parte dos problemas iniciais de um desenvolvimento usando o Spring MVC foram resolvidos. Mas vejam só: é o começo. Caso haja interesse do leitor em aprender um exemplo mais complexo, a revista JavaMagazine #78 publicou, um artigo meu com o Spring MVC 3 na criação de um blog, do começo ao fim. É um bom início para se desenvolver um projeto mais completo e complexo.
Para os que acompanham o blog, o assunto Spring MVC não para por aqui. Veremos em breve um site completo, feito com o framework, unindo várias características do Spring, de seu framework MVC e a parte de segurança com Spring Security.

Tags: , , , , , , , ,