Introdução
A validação de dados é uma parte importante e corriqueira de qualquer sistema, seja ela a partir de dados informados por um usuário humano, um androide, uma I.A...
Porém, também é uma das tarefas mais maçantes que temos que realizar enquanto programadores. Normalmente, no back-end, ela é feita de forma manual na camada de controle ou de serviços, utilizando-se no pior caso If's alinhados e no menos pior métodos e classes específicas.
Ok! Se você tiver um objeto com 4 ou 5 atributos criar um método para validá-los não é tão ruim, mas no mundo real as coisas não são tão simples, e em muitas ocasiões você pode acabar com objetos repletos de atributos, regras e domínios específicos. E é neste momento de caos que a Bean Validation(JSR-380) vai te ajudar.
Como todas as API's do java, ela possui uma especificação, que pode ser consultada no link acima, e uma implementação padrão, no caso, o Hibernate Validator.
Na prática
Para ilustrar, vamos considerar como exemplo uma simples aplicação de cadastro de usuários:
OBS: O código-fonte completo do exemplo está disponível no Github
O usuário acessa uma URl para realizar seu cadastro (sign-in), fornecendo os seguintes dados:
- userName
- confirmEmail
- password
- confirmPassword
E as regras de validação para o cadastro são:
- Nenhum dado pode ser null e nem vazio.
- O email precisa ser único.
- O email precisa ser válido.
- O userName precisa ser único.
- O usuário precisa confirmar o email.
- O usuário precisa confirmar o password.
- Se o cadastro for bem sucedido, um registro para o usuário é criado e o sistema retorna o código HTTP 201.
- Caso alguma validação não passe, nenhum registro é criado e o sistema retorna o código HTTP 400.
Vamos utilizar o Apache Maven como gerenciador do ciclo de vida e dependências. As dependências para a Bean Validation são as seguintes:
Para a entrada de dados, vamos utilizar um Dto bem simples:
A Bean Validation funciona através de annotations, e com elas vamos garantir as regras de campos não nulos, vazios e emails:
- NotNull
- NotEmpty
- NotBlank
Note que para cada annotation também podemos definir uma mensagem padrão. Essa mensagem será retornada pelo Validator.
Porém, para que o Dto seja realmente validado, precisamos informar ao Spring, e fazemos isso no controlador usando a anotação @valid:
Os erros de validação podem ser interceptados em tempo de execução e devidamente tratados:
Com isso já possuímos uma validação básica, contudo, existem situações mais específicas, onde somente as anotações padrão não bastam.
Customizando os validadores
Um dos nossos requisitos é bem específico:
2. O email precisa ser único.
Nesse caso, será necessário um validador exclusivo. Para tal, vamos criar uma anotação que vai executar uma classe de validação customizada:
- Na linha 10 definimos o alvo desse validador, no caso, um campo: ElementType.Field.
- Na linha 11 definimos que essa anotação será acessada em Runtime.
- Na linha 12 temos o trecho mais importante, onde definimos a classe concreta que conterá a lógica para a validação de dados: EmailAlreadyExistsValidator.class:
Note que na classe de implementação também definimos qual interface será utilizada.
O método isValid será chamado pela API e é onde devemos implementar nossa lógica:
Após a criação da Inteface e do Método, podemos então anotar no Dto diretamente no campo em que vamos utilizar:
Ordem de validação e validation Groups
Podemos também definir em qual ordem as validações serão executadas, principalmente, quando temos muitas validações a serem realizadas.
Para tal, devemos implementar grupos de validação e usar a anotação @GroupSequence para definir a ordem:
Além disso, em cada campo devemos definir a qual grupo ele está ligado:
Dessa forma, conseguimos garantir que a validação seja feita de forma simples, manutenível e extensível. Verifique o código-fonte para mais exemplos, inclusive a parte de testes.
Validando através do código
Em algumas situações pode ser necessário um maior controle na execução das validações, como por exemplo, quando necessitamos capturar as mensagens de erro durante um processamento sem interrompê-lo.
Nesse caso, devemos chamar programaticamente a API.
Considere a seguinte classe:
Ao invés de chamar automaticamente a validão no controlador com o @valid, vamos criar um método no service e dentro dele chamaremos a validação:
No método validate utilizamos um ValidationFactory para obter o validador e então chamar a validação:
Dessa forma obtemos mais flexibilidade nas situações onde precisamos de um maior controle.
E chegamos ao final! Espero que o artigo possa te ajudar no seu dia-a-dia, e caso queira conferir melhor o código, ele está disponível no Github. Muito Obrigado!
Top comments (0)