Validando listas em um controlador Spring
1. Introdução
A validação de entradas do usuário é um requisito comum em qualquer aplicativo. Neste tutorial, examinaremos as maneiras devalidate a List of objects as a parameter to a Spring controller.
Vamos adicionar validação na camada do controlador para garantir que os dados especificados pelo usuário satisfaçam as condições especificadas.
2. Adicionando restrições a um bean
Para nosso exemplo, usaremos um controlador Spring simples que gerencia um banco de dados de filmes. Vamos nos concentrar em um método que aceita uma lista de filmes e os adiciona ao banco de dados após realizar validações na lista.
Então, vamos começar poradding constraints on the Movie bean usandojavax validation:
public class Movie {
private String id;
@NotEmpty(message = "Movie name cannot be empty.")
private String name;
// standard setters and getters
}
3. Adicionando anotações de validação no controlador
Vamos dar uma olhada em nosso controlador. Primeiro, vamosadd the @Validated annotation to the controller class:
@Validated
@RestController
@RequestMapping("/movies")
public class MovieController {
@Autowired
private MovieService movieService;
//...
}
A seguir, vamos escrever o método do controlador onde validaremos a lista de objetosMovie passados.
Vamosadd the @NotEmpty annotation to our list of movies para validar que deve haver pelo menos um elemento na lista. Ao mesmo tempo, adicionaremos a anotação@Valid para garantir que os próprios objetosMovie sejam válidos:
@PostMapping
public void addAll(
@RequestBody
@NotEmpty(message = "Input movie list cannot be empty.")
List<@Valid Movie> movies) {
movieService.addAll(movies);
}
Se chamarmos o método do controlador com uma entrada de listaMovie vazia, a validação falhará por causa da anotação@NotEmpty, e veremos a mensagem:
Input movie list cannot be empty.
A anotação@Valid garantirá que as restrições especificadas na classeMovie sejam avaliadas para cada objeto na lista. Portanto, se passarmosMovie com um nome vazio na lista, a validação falhará com a mensagem:
Movie name cannot be empty.
4. Validadores personalizados
Também podemos adicionarcustom constraint validators à lista de entrada.
Para o nosso exemplo, a restrição personalizada validará a condição de que o tamanho da lista de entrada seja restrito a um máximo de quatro elementos. Vamos criar esta anotação de restrição personalizada:
@Constraint(validatedBy = MaxSizeConstraintValidator.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MaxSizeConstraint {
String message() default "The input list cannot contain more than 4 movies.";
Class>[] groups() default {};
Class extends Payload>[] payload() default {};
}
Agora, vamos criar um validador que aplicará a restrição acima:
public class MaxSizeConstraintValidator implements ConstraintValidator> {
@Override
public boolean isValid(List values, ConstraintValidatorContext context) {
return values.size() <= 4
}
}
Finalmente, vamos adicionar a anotação@MaxSizeConstraint ao nosso método de controle:
@PostMapping
public void addAll(
@RequestBody
@NotEmpty(message = "Input movie list cannot be empty.")
@MaxSizeConstraint
List<@Valid Movie> movies) {
movieService.addAll(movies);
}
Aqui,@MaxSizeConstraint validará o tamanho da entrada. Portanto, se passarmos mais de quatro objetosMovie na lista de entrada, a validação falhará.
5. Manipulando a exceção
Se alguma das validações falhar,ConstraintViolationException é lançado. Agora, vamos ver como podemos adicionar um componenteexception handling para capturar essa exceção.
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity handle(ConstraintViolationException constraintViolationException) {
Set> violations = constraintViolationException.getConstraintViolations();
String errorMessage = "";
if (!violations.isEmpty()) {
StringBuilder builder = new StringBuilder();
violations.forEach(violation -> builder.append(" " + violation.getMessage()));
errorMessage = builder.toString();
} else {
errorMessage = "ConstraintViolationException occured.";
}
return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
}
6. Testando a API
Agora, vamos testar nosso controlador com entradas válidas e inválidas.
Em primeiro lugar, vamos fornecer dados válidos para a API:
curl -v -d [{"name":"Movie1"}] -H "Content-Type: application/json" -X POST http://localhost:8080/movies
Nesse cenário, obteremos uma resposta HTTP status 200:
...
HTTP/1.1 200
...
A seguir, verificaremos nossa resposta da API quando passarmos entradas inválidas.
Vamos tentar uma lista vazia:
curl -d [] -H "Content-Type: application/json" -X POST http://localhost:8080/movies
Neste cenário, obteremos uma resposta HTTP status 400. Isso ocorre porque a entrada não satisfaz a restrição@NotEmpty.
Input movie list cannot be empty.
A seguir, vamos tentar passar cinco objetosMovie na lista:
curl -d [{"name":"Movie1"},{"name":"Movie2"},{"name":"Movie3"},{"name":"Movie4"},{"name":"Movie5"}]
-H "Content-Type: application/json" -X POST http://localhost:8080/movies
Isso também resultará em uma resposta de status HTTP 400 porque falhamos na restrição@MaxSizeConstraint:
The input list cannot contain more than 4 movies.
7. Conclusão
Neste artigo rápido, aprendemos como validar uma lista de objetos no Spring.
Como sempre, o código-fonte completo dos exemplos éover on GitHub.