Spring Validation – walidacja danych

TL;DR: Spring Validation pozwala automatycznie sprawdzać poprawność danych przesyłanych przez użytkowników. Używając adnotacji jak @NotNull, @Size czy @Valid możesz zabezpieczyć swoją aplikację przed błędnymi danymi i zapewnić lepsze doświadczenie użytkownika.

Dlaczego walidacja danych jest ważna?

Każda aplikacja webowa musi weryfikować dane otrzymywane od użytkowników. Bez walidacji twoja aplikacja może otrzymać niepoprawne dane, które spowodują błędy w bazie danych, problemy z bezpieczeństwem lub po prostu nieprzewidywalne zachowanie. Spring Validation rozwiązuje ten problem w elegancki sposób, pozwalając na deklaratywne określenie reguł walidacji.

Co się nauczysz:

  • Jak skonfigurować walidację w Spring Boot
  • Jakie są najważniejsze adnotacje walidacyjne
  • Jak walidować dane w kontrolerach
  • Jak obsługiwać błędy walidacji
  • Jak tworzyć własne walidatory
Wymagania wstępne: Podstawowa znajomość Spring Boot, Java oraz obsługi żądań HTTP w kontrolerach.

Czym jest Spring Validation?

Spring Validation to mechanizm oparty na standardzie JSR-303 (Bean Validation), który pozwala na automatyczne sprawdzanie poprawności obiektów Java. Zamiast pisać ręcznie kod sprawdzający każde pole, używasz adnotacji które określają reguły walidacji.

JSR-303 Bean Validation – Java standard określający sposób walidacji obiektów poprzez adnotacje. Spring implementuje ten standard i dodaje własne rozszerzenia.

Konfiguracja Spring Validation

W Spring Boot 2.x walidacja jest włączona domyślnie. Wystarczy dodać zależność do pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
W wersjach Spring Boot przed 2.3.0 walidacja była włączona automatycznie z spring-boot-starter-web. Od wersji 2.3.0 trzeba dodać ją osobno.

Podstawowe adnotacje walidacyjne

Spring Validation oferuje bogaty zestaw gotowych adnotacji do walidacji. Oto najważniejsze z nich:

AdnotacjaZastosowaniePrzykład
@NotNullPole nie może być null@NotNull String name
@NotEmptyString/Collection nie może być pusty@NotEmpty String email
@NotBlankString nie może być pusty lub składać się z białych znaków@NotBlank String username
@SizeKontroluje rozmiar String/Collection@Size(min=2, max=50) String name
@EmailSprawdza format adresu email@Email String email
@Min/@MaxMinimalna/maksymalna wartość liczbowa@Min(18) int age

Tworzenie klasy z walidacją

Zobaczmy jak wygląda typowa klasa DTO z adnotacjami walidacyjnymi:

public class UserRegistrationDto {
    
    @NotBlank(message = "Imię nie może być puste")
    @Size(min = 2, max = 50, message = "Imię musi mieć od 2 do 50 znaków")
    private String firstName;
    
    @NotBlank(message = "Nazwisko nie może być puste")
    @Size(min = 2, max = 50, message = "Nazwisko musi mieć od 2 do 50 znaków")
    private String lastName;
    
    @NotBlank(message = "Email nie może być pusty")
    @Email(message = "Niepoprawny format emaila")
    private String email;
    
    @NotNull(message = "Wiek jest wymagany")
    @Min(value = 18, message = "Musisz mieć co najmniej 18 lat")
    @Max(value = 120, message = "Wiek nie może przekraczać 120 lat")
    private Integer age;
    
    // konstruktory, gettery i settery
    public UserRegistrationDto() {}
    
    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    
    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
}
Pro tip: Zawsze dodawaj własne komunikaty błędów w parametrze message. Domyślne comunicaty są często niezrozumiałe dla użytkowników końcowych.

Walidacja w kontrolerach

Aby włączyć walidację w kontrolerze, używamy adnotacji @Valid lub @Validated:

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @PostMapping("/register")
    public ResponseEntity<String> registerUser(@Valid @RequestBody UserRegistrationDto user) {
        // Jeśli walidacja przejdzie pomyślnie, kod się wykona
        // W przeciwnym razie zostanie rzucony MethodArgumentNotValidException
        
        // Logika rejestracji użytkownika
        System.out.println("Rejestracja użytkownika: " + user.getFirstName() + " " + user.getLastName());
        
        return ResponseEntity.ok("Użytkownik zarejestrowany pomyślnie");
    }
}

Obsługa błędów walidacji

Gdy walidacja się nie powiedzie, Spring rzuca wyjątek MethodArgumentNotValidException. Możemy go obsłużyć globalnie:

@ControllerAdvice
public class ValidationExceptionHandler {
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationExceptions(
            MethodArgumentNotValidException ex) {
        
        Map<String, String> errors = new HashMap<>();
        
        // Przechodzimy przez wszystkie błędy walidacji
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        
        return ResponseEntity.badRequest().body(errors);
    }
}
Dzięki @ControllerAdvice obsługa błędów walidacji działa dla wszystkich kontrolerów w aplikacji. Nie musisz duplikować kodu w każdym kontrolerze.

Przykład odpowiedzi z błędami

Gdy wyślesz niepoprawne dane, otrzymasz odpowiedź JSON z szczegółami błędów:

{
    "firstName": "Imię musi mieć od 2 do 50 znaków",
    "email": "Niepoprawny format emaila",
    "age": "Musisz mieć co najmniej 18 lat"
}

Walidacja zagnieżdżonych obiektów

Możesz walidować również zagnieżdżone obiekty używając @Valid:

public class UserWithAddressDto {
    
    @NotBlank
    private String name;
    
    @Valid // Ważne! Bez tego adres nie będzie walidowany
    @NotNull
    private AddressDto address;
    
    // gettery i settery
}

public class AddressDto {
    
    @NotBlank(message = "Ulica nie może być pusta")
    private String street;
    
    @NotBlank(message = "Miasto nie może być puste")
    private String city;
    
    @Pattern(regexp = "\\d{2}-\\d{3}", message = "Kod pocztowy musi być w formacie XX-XXX")
    private String zipCode;
    
    // gettery i settery
}
Pułapka: Pamiętaj o dodaniu @Valid do zagnieżdżonych obiektów, inaczej nie będą walidowane!
Jaka jest różnica między @NotNull, @NotEmpty i @NotBlank?

@NotNull sprawdza czy wartość nie jest null. @NotEmpty sprawdza czy String/Collection nie jest pusty (ale może być null). @NotBlank sprawdza czy String nie jest pusty i nie składa się tylko z białych znaków.

Czy mogę używać walidacji bez Spring Boot?

Tak, JSR-303 Bean Validation to standard Java. Możesz używać implementacji jak Hibernate Validator również w zwykłych aplikacjach Java.

Jak walidować parametry w metodach?

Użyj @Validated na klasie kontrolera i dodaj adnotacje walidacyjne do parametrów metod, np. @Min(1) @PathVariable Long id.

Czy walidacja działa automatycznie?

Walidacja działa automatycznie tylko gdy użyjesz @Valid lub @Validated. Bez tych adnotacji Spring nie będzie walidować danych.

Jak przetestować walidację?

Użyj MockMvc w testach Spring Boot: mockMvc.perform(post().content(invalidJson)).andExpect(status().isBadRequest())

Czy mogę grupować walidacje?

Tak, możesz tworzyć grupy walidacji używając interfejsów jako markery i parametru groups w adnotacjach walidacyjnych.

Co z wydajnością walidacji?

Walidacja w Spring jest bardzo szybka. Adnotacje są przetwarzane przez reflection, ale wyniki są cache’owane. W praktyce overhead jest minimalny.

Przydatne zasoby:

🚀 Zadanie dla Ciebie

Stwórz formularz rejestracji z walidacją, który sprawdza: imię i nazwisko (min 2 znaki), email (poprawny format), hasło (min 8 znaków) i potwierdzenie hasła (musi być identyczne). Dodaj obsługę błędów i przetestuj z niepoprawymi danymi.

Czy używałeś już Spring Validation w swoich projektach? Jakie napotkałeś problemy z walidacją danych? Podziel się swoimi doświadczeniami w komentarzach!

Zostaw komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Przewijanie do góry