Capítulo 10

Validando formulários

Quando temos um formulário para adicionar dados no nosso sistema, geralmente precisamos verificar se os dados estão corretos e consistentes. Por exemplo não gostaríamos que o usuário digitasse "vinte e três reais" no campo preço, ou que o campo nome fosse vazio.

Então, antes de salvar esses dados no banco precisamos verificar se tudo está como esperamos. Chamamos isso de validação. Existem duas maneiras de fazer validação:

A validação do lado do cliente geralmente é feita usando javascript, impedindo que o formulário seja submetido caso aconteça algum erro. A do lado do servidor é feita em Java, e o VRaptor pode te ajudar a fazê-la.

10.1 - Validator

O VRaptor possui um componente, chamado Validator, para ajudar a fazer validações do lado do servidor. Com ele, você pode executar a lógica de validação, e caso haja algum erro, especificar para onde deve ser redirecionada a requisição. Para obter um Validator basta recebê-lo no construtor do seu Controller:

import br.com.caelum.vraptor.Validator;

@Resource
public class ProdutosController {

  private final ProdutoDao dao;
  private final Result result;
  private final Validator validator;

  public ProdutosController(ProdutoDao dao, Result result, 
      Validator validator) {
    this.dao = dao;
    this.result = result;
    this.validator = validator;
  }
  //...
}

Com essa instância do Validator podemos adicionar erros de validação para o caso em que os dados da requisição não vieram corretos. Geralmente validamos quando estamos adicionando ou atualizando algo no banco, então vamos executar a validação no método adiciona.

Para isso vamos definir primeiro as regras de validação para um produto:

Para executar essa lógica com o Validator, temos duas maneiras:

Em ambas as maneiras você precisa especificar pra qual lógica voltar quando der erro de validação. No nosso caso, ao dar erro queremos voltar pro formulário, então fazemos:

validator.onErrorUsePageOf(ProdutosController.class).formulario();

A lógica de redirecionamento é a mesma que se você estivesse usando o result.of(). Você deve colocar essa chamada logo no final da sua lógica de validação, no momento em que, se houver algum erro, a execução deve parar e voltar para o formulário, mostrando os erros.

Quando existem erros de validação, o VRaptor disponibiliza para a jsp um atributo chamado ${errors}, que contém a lista dos erros que aconteceram. Então você usar o seguinte código para mostrar as mensagens no seu jsp:

<ul>
<c:forEach items=${errors} var="error">
  <li>${error.category} - ${error.message}</li>
</c:forEach>
</ul>

Além disso, se quisermos que o formulário volte preenchido quando houver algum erro de validação, precisamos passar como valor dos inputs, o que foi mandado para a requisição:

<form action="adiciona">
  <fieldset>
    <legend>Adicionar Produto</legend>
    
    <label for="nome">Nome:</label>
    <input id="nome" type="text" name="produto.nome" 
      value="${produto.nome }"/>

    <label for="descricao">Descrição:</label>
    <textarea id="descricao" name="produto.descricao"> ${produto.descricao } </textarea>

    <label for="preco">Preço:</label>
    <input id="preco" type="text" name="produto.preco" 
      value="${produto.preco }"/>

    <button type="submit">Enviar</button>
  </fieldset>
</form>

10.2 - Exercícios

  1. Receba um Validator no construtor do ProdutosController e guarde-o num atributo:

    import br.com.caelum.vraptor.Validator;
    
    @Resource
    public class ProdutosController {
    
      private final ProdutoDao dao;
      private final Result result;
      private final Validator validator;
    
      public ProdutosController(ProdutoDao dao, Result result, 
          Validator validator) {
        this.dao = dao;
        this.result = result;
        this.validator = validator;
      }
      //...
    }
    
  2. Modifique o método adiciona para que inclua a validação dos campos do produto. Se preferir, faça a validação na forma fluente.

    public void adiciona(final Produto produto) {
      if (produto.getNome() == null || 
          produto.getNome().length() < 3) {
        validator.add(new ValidationMessage(
            "Nome é obrigatório e precisa ter mais" + 
            " de 3 letras", "produto.nome"));
      }
      if (produto.getDescricao() == null ||
          produto.getDescricao().length() > 40) {
        validator.add(new ValidationMessage(
            "Descrição é obrigatória não pode ter mais" + 
            " que 40 letras", "produto.descricao"));
      }
      if (produto.getPreco() <= 0) {
        validator.add(new ValidationMessage(
            "Preço precisa ser positivo", "produto.preco"));
      }
      validator.onErrorUsePageOf(ProdutosController.class)
          .formulario();
      
      dao.salva(produto);
      result.redirectTo(this).lista();
    }
    
  3. Abra o arquivo header.jspf, e adicione o código para mostrar os erros de validação, dentro da div com id="erros".

    <div id="erros">
      <ul>
        <c:forEach items="${errors}" var="error">
          <li>${error.category } - ${error.message }</li>
        </c:forEach>
      </ul>
    </div>
    
  4. Adicione o código para que o formulário continue preenchido quando der erro de validação:

    <form action="adiciona">
      <fieldset>
        <legend>Adicionar Produto</legend>
        
        <label for="nome">Nome:</label>
        <input id="nome" type="text" name="produto.nome" 
            value="${produto.nome }"/>
    
        <label for="descricao">Descrição:</label>
        <textarea id="descricao" name="produto.descricao"> ${produto.descricao } </textarea>
    
        <label for="preco">Preço:</label>
        <input id="preco" type="text" name="produto.preco" 
            value="${produto.preco }"/>
    
        <button type="submit">Enviar</button>
      </fieldset>
    </form>
    
  5. Abra o browser e tente adicionar um produto inválido.

    erro-de-validacao.png
  6. (Opcional) O método atualiza também precisa de validação. Você consegue aproveitar o código do adiciona para fazer isso? Ele deve redirecionar para o mesmo lugar em caso de erro?

Já conhece os cursos online Alura?

A Alura oferece centenas de cursos online em sua plataforma exclusiva de ensino que favorece o aprendizado com a qualidade reconhecida da Caelum. Você pode escolher um curso nas áreas de Java, Front-end, Ruby, Web, Mobile, .NET, PHP e outros, com um plano que dá acesso a todos os cursos.

Conheça os cursos online Alura.

10.3 - Para saber mais: Hibernate Validator

Hibernate Validator é um poderoso framework para validação de objetos. Ele já vem com validadores que verificam as regras mais comuns, como campos obrigatórios, tamanhos mínimos e cartões de crédito.

Além disso, o framework também é bastante flexível, já que permite a criação dos seus próprios validadores.

Para definir as regras de validação, basta anotar os campos do nosso modelo que precisam ser validados. Para tal, usaremos as anotações prontas no Hibernate Validator. Alguns exemplos:

Munidos dessas anotações, vamos aplicar nossas regras de validação no Produto:

import org.hibernate.validator.Length;
import org.hibernate.validator.Min;
import org.hibernate.validator.NotNull;

@Entity
public class Produto {

  @Id @GeneratedValue
  private Long id;

  @NotNull
  @Length(min=3)
  private String nome;

  @NotNull
  @Length(max=40)
  private String descricao;

  @Min(0)
  private Double  preco;
}

Com nosso modelo anotado, podemos substituir as validações que fizemos pelo Hibernate Validator. O interface Validator do VRaptor possui um método chamado validate que valida qualquer objeto usando o Hibernate Validator. Assim, se você chamar:

validator.validate(produto)

todos os erros de validação que ocorrerem serão adicionados ao validator automaticamente. Assim podemos substituir as validações que tínhamos feito antes, pelas equivalentes do Hibernate Validator.

10.4 - Exercícios Opcionais

  1. Se as anotações não aparecerem, vá para a pasta que você tinha extraído o zip do VRaptor e copie o jar hibernate-validator-X.X.X.jar que está na pasta lib/optional/hibernate para a pasta WEB-INF/lib da aplicação.

  2. Anote os campos do produto para adicionar nossas regras de validação.

    import org.hibernate.validator.Length;
    import org.hibernate.validator.Min;
    import org.hibernate.validator.NotNull;
    
    @Entity
    public class Produto {
    
      @Id @GeneratedValue
      private Long id;
    
      @NotNull
      @Length(min=3)
      private String nome;
    
      @NotNull
      @Length(max=40)
      private String descricao;
    
      @Min(0)
      private Double  preco;
    }
    
  3. Mude a lógica de validação do método adiciona do ProdutosController para usar o Hibernate Validator:

    @Resource
    public class ProdutosController {
      //...
      public void adiciona(final Produto produto) {
        validator.validate(produto);
        validator.onErrorUsePageOf(ProdutosController.class).formulario();
    
        dao.salva(produto);
        result.redirectTo(this).lista();
      }
    }
    
  4. Acesse o browser e adicione algum produto inválido, para ver as mensagens de validação.

    hibernate-validator.png
  5. (Opcional) Repare que as mensagens são as mensagens padrão e estão em inglês. Para usar uma mensagem em português, precisamos passá-la para as anotações do Hibernate Validator.

    @NotNull(message="Nome precisa ser preenchido")
      @Length(min=3, message="Nome precisa ter mais de 3 letras")
    private String nome;
    

    Se quisermos usar internacionalização nessas mensagens, basta colocar a chave de internacionalização entre {}:

    @NotNull(message="{nome.precisa.ser.preenchido}")
      @Length(min=3, message="{nome.curto}")
    private String nome;
    

    e colocar as respectivas mensagens no arquivo messages.properties:

    nome.precisa.ser.preenchido = Nome precisa ser preenchido
    nome.curto = Nome muito curto. Deve ter mais de 3 letras
    

    Mude as mensagens