Table of contents
No headings in the article.
Olá pessoal,
Hoje vamos adotar mais uma boa prática no nosso projeto: a validação dos campos de entrada enviados para a nossa API.
Essa validação tem como objetivo garantir que as informações recebidas estejam corretas e coerentes com o objetivo da aplicação. Além disso, ela reduz e elimina as validações manuais que são recomendadas antes de salvar os dados no banco.
Outro ponto importante é que essa validação pode evitar erros de segurança, como a injeção de comandos no banco de dados ou a execução de scripts JavaScript maliciosos (por exemplo, ataques XSS). Também ajuda a prevenir ataques de negação de serviço (DoS), onde um usuário mal-intencionado pode enviar um volume excessivo de dados, consumindo muita memória e travando a API.
O primeiro passo é adicionar a dependência spring-boot-starter-validation no seu arquivo pom dentro de dependencies. O Spring Validation é um "starter" do Spring Boot que simplifica e traz automaticamente as dependências necessárias para validar nossos objetos de entrada (DTOs).
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
O segundo passo é ajustar a classe SeriesController com a anotação @Valid, conforme exemplo abaixo:
@PatchMapping(path = "/{id}", consumes = APPLICATION_JSON, produces = APPLICATION_JSON)
public ResponseEntity<SeriesResponseDTO> updateSeries(@PathVariable String id,
@RequestBody @Valid SeriesUpdateDTO updatedSeriesDTO) {
log.info("Received request to update series with ID: {}", id);
return ResponseEntity.ok().body(seriesService.updateSeries(id, updatedSeriesDTO));
}Segue abaixo as anotações que foram implementadas no projeto. Para clonar o projeto, basta acessar aqui.
-
Garante que o valor do campo não seja nulo, vazio ou composto apenas por espaços em branco.
Não pode ser utilizado com tipos números.
-
Pode ser aplicada em Strings, coleções (List, Set), mapas (Map) e arrays. Seu objetivo é garantir que o objeto não esteja nulo e que contenha pelo menos um elemento (ou caractere, no caso de uma String). Também não é indicada para tipos numéricos.
-
Pode ser aplicada a qualquer tipo de objeto para assegurar que o valor não seja nulo. Ela é bastante genérica e não impõe restrições quanto ao conteúdo ou tamanho, apenas verifica a nulidade.
-
Utilizada para restringir o tamanho (comprimento ou número de elementos) de Strings, coleções, mapas e arrays. Não faz sentido aplicar @Size a tipos numéricos diretamente, pois sua finalidade é medir a "quantidade" de elementos ou caracteres, e não o valor numérico.
-
Utilizada para validar o valor mínimo e máximo permitido para o campo. Pode ser utilizado em campos numéricos.
-
Anotação usada para validar objetos dentro do objeto principal (objetos aninhados).
-
Indicado para propriedades booleanas (tanto o primitivo boolean quanto o wrapper Boolean). Ela exige que o valor seja true, sendo útil para validar condições que devem ser verdadeiras.
Observe como as classes ficaram:
@Data @Builder @NoArgsConstructor @AllArgsConstructor public class SeriesCreateDTO { @NotBlank(message = "Series name is required") @Size(min = 3, max = 100, message = "Series name must be between 3 and 100 characters") private String name; @NotEmpty(message = "At least one genre is required") @Size(max = 3, message = "Maximum of 3 genres allowed") private List<SeriesGenre> genres; @Min(value = 1900, message = "Release year must be at least 1900") private int releaseYear; @NotEmpty(message = "At least one season is required") @Size(max = 50, message = "Maximum of 50 seasons allowed") @Valid private List<SeasonDTO> seasons; @NotNull(message = "Completion status is required") private Boolean completed; @AssertTrue(message = "Release year cannot be in the future") public boolean isInvalidReleaseYear() { return releaseYear <= Year.now().getValue(); } } @Data @Builder @NoArgsConstructor @AllArgsConstructor public class SeasonDTO { @Positive(message = "Season number must be positive") @Max(value = 50, message = "Season number cannot exceed 50") private int seasonNumber; @Positive(message = "Number of episodes must be positive") @Max(value = 100, message = "Number of episodes cannot exceed 100") private int episodes; }
Segue outras anotações que você pode utilizar em sua API.
-
Útil para garantir que uma String tenha o formato de um endereço de e-mail.
-
Permite validar se o valor de uma String atende a um padrão definido por uma expressão regular. Isso é muito usado para formatos específicos, como CPF, CNPJ, telefones, etc.
@Past, @Future, @PastOrPresent e @FutureOrPresent
Essas anotações são comuns para campos que representam datas ou instantes, garantindo que o valor seja, respectivamente, uma data passada, futura ou presente.
@Positive (ou @PositiveOrZero) e @Negative (ou @PositiveOrZero)
Quando se trabalha com números, você pode garantir que o valor seja positivo ou negativo.
Lembrando que em caso de envio de campos no formato incorreto ou que exceda os limites definidos, pode ser lançado dois tipos de exceções: ConstraintViolationException ou MethodArgumentNotValidException.
Para manter um retorno de erro padronizado, utilize a classe anotado com o @RestControllerAdvice para realizar o tratamento. Para consultar um exemplo de implementação, consulte aqui.
Ficou com alguma dúvida? Deixe nos comentários.
Até a próxima 😊