Validação Declarativa de Dados no Spring MVC

Validação Declarativa

O Spring MVC integra o Bean Validation (JSR-303) desde a versão 3, permitindo validar objetos de comando diretamente por anotações. Abaixo está o passo a passo para ativar e usar esse recurso.

Dependências Necessárias

Usaremos o Hibernate Validator como implementação de referência. Adicione os seguintes JARs ao classpath da aplicação:

  • validation-api-1.0.0.GA.jar — API da especificação JSR-303.
  • hibernate-validator-4.3.0.Final.jar — Implementação do Hibernate Validator.
  • jboss-logging-3.1.0.CR2.jar — Dependência de logging do Hibernate Validator.

Registro do Validador no Spring

Crie um bean do tipo LocalValidatorFactoryBean e enforme ao <mvc:annotation-driven> que ele deve ser usado:

<bean id="validador" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
    <property name="validationMessageSource" ref="fonteMensagens"/>
</bean>

<bean id="fonteMensagens" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basename" value="classpath:mensagens"/>
    <property name="defaultEncoding" value="UTF-8"/>
    <property name="cacheSeconds" value="120"/>
</bean>

<mvc:annotation-driven validator="validador"/>

O arquivo mensagens.properties, colocado no src, pode conter entradas como:

nome.obrigatorio=O nome do cliente é obrigatório
email.invalido=O endereço de e-mail informado não é válido
idade.minima=A idade mínima permitida é 18 anos

Modelo com Restrições

As restrições são declaradas diretamente nos atributos do modelo:

public class ClienteModel {

    @NotBlank(message = "{nome.obrigatorio}")
    @Size(min = 3, max = 60, message = "{nome.tamanho}")
    private String nome;

    @Email(message = "{email.invalido}")
    private String email;

    @Min(value = 18, message = "{idade.minima}")
    private int idade;

    // getters e setters omitidos
}

Controller que Dispara a Validação

Use @Valid antes do objeto de comando. Logo em seguida, adicione um parâmetro BindingResult (ou Errors) para acessar os erros:

@Controller
public class CadastroController {

    @RequestMapping("/clientes/cadastrar")
    public String cadastrar(
            @Valid @ModelAttribute("cliente") ClienteModel cliente,
            BindingResult resultado) {

        if (resultado.hasErrors()) {
            return "cadastro/erro";
        }
        return "cadastro/sucesso";
    }
}

Exibição dos Erros na Visão

Na página JSP, utilize a tag <form:errors> para listar as violações:

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<form:form commandName="cliente">
    <form:errors path="*" cssStyle="color: red"/>
</form:form>

Para testar, faça uma requisição sem informar o nome:

http://localhost:8080/app/clientes/cadastrar?nome=&email=invalido&idade=10

Principais Anotações de Validação

O Hibernate Validator oferece um conjunto rico de restrições. A tabela abaixo resume as mais usadas:

  • @AssertFalse: o valor booleano deve ser false.
  • @AssertTrue: o valor booleano deve ser true.
  • @NotNull: o valor não pode ser null.
  • @Null: o valor deve ser null.
  • @Min(valor): o número deve ser maior ou igual ao limite.
  • @Max(valor): o número deve ser menor ou igual ao limite.
  • @DecimalMin(valor): equivalente a @Min, mas aceita BigDecimal e String.
  • @DecimalMax(valor): equivalente a @Max, mas aceita BigDecimal e String.
  • @Digits(inteiro, fracao): limita a quantidade de dígitos inteiros e decimais.
  • @Size(min, max): tamanho da coleção, do array ou da String deve estar dentro do intervalo.
  • @Past: a data deve ser anterior ao momento atual.
  • @Future: a data deve ser posterior ao momento atual.
  • @NotBlank: a String não pode ser null, vazia ou composta apenas de espaços.
  • @Length(min, max): o comprimento da String deve estar dentro do intervalo.
  • @NotEmpty: a coleção, array ou String não pode ser null nem vazia.
  • @Range(min, max): o valor numérico deve estar dentro do intervalo.
  • @Email: a String deve ter formato de e-mail.
  • @Pattern(regexp): a String deve corresponder à expressão regular.
  • @Valid: força a validação recursiva de um objeto associado.

Mensagens de Erro

As mensagens podem ser definidas de duas formas: diretamente no atributo message da anotação ou externamente, via MessageSource.

Mensagem em Hard Code

@NotBlank(message = "O nome é obrigatório")
private String nome;

Essa abordagem dificulta a manutenção e a internacionalização. Prefira externalizar as mensagens.

Mensagem Externalizada

Como visto anteriormente, basta usar a sintaxe {chave} e manter as traduções em arquivos .properties:

@NotBlank(message = "{nome.obrigatorio}")
private String nome;

Vários Objetos Validados no Mesmo Método

Quando um método de controle precisa validar mais de um objeto, cada objeto deve ser seguido pelo seu próprio BindingResult:

@RequestMapping("/pedidos/finalizar")
public String finalizar(
        @Valid @ModelAttribute("cliente") ClienteModel cliente,
        BindingResult clienteErros,
        @Valid @ModelAttribute("pedido") PedidoModel pedido,
        BindingResult pedidoErros) {

    if (clienteErros.hasErrors()) {
        return "pedidos/erro-cliente";
    }

    if (pedidoErros.hasErrors()) {
        return "pedidos/erro-pedido";
    }

    return "pedidos/sucesso";
}

Na visão, cada formulário exibe seus próprios erros:

<form:form commandName="cliente">
    <form:errors path="*" cssStyle="color: red"/>
</form:form>

<form:form commandName="pedido">
    <form:errors path="*" cssStyle="color: red"/>
</form:form>

Tratamento de Exceções

O Spring MVC oferece três mecanismos principais para tratar exceções: implementar HandlerExceptionResolver, usar SimpleMappingExceptionResolver ou anotar métodos com @ExceptionHandler.

Implementando HandlerExceptionResolver

Crie um bean que decide qual visão exibir para cada tipo de exceção:

public class TratadorExcecoesGlobal implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) {

        if (ex instanceof IllegalArgumentException) {
            System.out.println("Argumento inválido detectado");
            return new ModelAndView("erro/argumento-invalido");
        }

        return new ModelAndView("erro/geral");
    }
}

Registre-o no XML:

<bean id="tratadorExcecoes" class="com.exemplo.spring.TratadorExcecoesGlobal"/>

Se o método retornar null, o Spring continua consultando os demais resolvedores registrados até que algum retorne uma visão.

SimpleMappingExceptionResolver

Uma alternativa declarativa é mapear exceções para visões diretamante no XML:

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="defaultErrorView" value="erro/geral"/>
    <property name="exceptionAttribute" value="excecao"/>
    <property name="exceptionMappings">
        <props>
            <prop key="java.lang.IllegalArgumentException">erro/argumento-invalido</prop>
            <prop key="java.lang.NumberFormatException">erro/formato-numerico</prop>
        </props>
    </property>
</bean>

O objeto da exceção ficará disponível no request com o nome excecao.

@ExceptionHandler

Para um tratamento mais específico dentro de um controller, use o método anotado:

@ExceptionHandler({ IllegalArgumentException.class, NumberFormatException.class })
public String tratarExcecao(Exception ex, Model model) {
    model.addAttribute("excecao", ex);

    if (ex instanceof IllegalArgumentException) {
        return "erro/argumento-invalido";
    }

    return "erro/geral";
}

Ordem de Execução

Quando uma exceção ocorre, os mecanismos são consultados na seguinte ordem:

  1. O Spring procura primeiro por um @ExceptionHandler no próprio controller.
  2. Em seguida, busca beans que implementam HandlerExceptionResolver.
  3. Caso haja múltiplos resolvedores, eles são executados na ordem de configuração.
  4. O processamento termina asim que um resolvedor retornar uma visão não nula.

Tags: Spring MVC hibernate validator bean validation JSR-303 jsp

Publicado em 7-4 16:08