Validação dos campos de entrada da API com Spring Validation

Robert AdamRobert Adam
4 min read

Table of contents

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.
  • @NotBlank

    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.

  • @NotEmpty

    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.

  • @NotNull

    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.

  • @Size

    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.

  • @Min e @Max

    Utilizada para validar o valor mínimo e máximo permitido para o campo. Pode ser utilizado em campos numéricos.

  • @Valid

    Anotação usada para validar objetos dentro do objeto principal (objetos aninhados).

  • @AssertTrue

    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.

  • @Email

    Útil para garantir que uma String tenha o formato de um endereço de e-mail.

  • @Pattern

    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 😊

0
Subscribe to my newsletter

Read articles from Robert Adam directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Robert Adam
Robert Adam