Takip et

Spring Validasyon Örneği – Spring MVC Form Validator

Spring Validasyon Örneği – Spring MVC Form Validator Web uygulamalarının geliştirilmesinde, kullanıcıdan alınan verilerin doğruluğunu ve

Spring Validasyon Örneği – Spring MVC Form Validator

Web uygulamalarının geliştirilmesinde, kullanıcıdan alınan verilerin doğruluğunu ve bütünlüğünü sağlamak kritik bir öneme sahiptir. Geçersiz veya kötü niyetli veriler, uygulamanın çökmesine, güvenlik açıklarına veya yanlış iş mantığı yürütülmesine neden olabilir. Spring Framework, bu ihtiyacı karşılamak üzere güçlü ve esnek bir validasyon mekanizması sunar. Bu rehberde, Spring MVC uygulamalarında form verilerini nasıl doğrulayacağımızı, Bean Validation API’sini (JSR 380) Spring ile nasıl entegre edeceğimizi ve pratik bir örnek üzerinden adım adım nasıl uygulayacağımızı öğreneceğiz.

Giriş: Neden Validasyon Gereklidir?

Validasyon, kullanıcıdan gelen girdilerin belirli kurallara uygun olup olmadığını kontrol etme işlemidir. Bu kurallar, veri tipinden (sayı, metin, tarih), uzunluk kısıtlamalarına, belirli bir formata (e-posta, telefon numarası) veya iş mantığına özgü koşullara kadar geniş bir yelpazeyi kapsayabilir. Spring MVC uygulamalarında, formlar aracılığıyla kullanıcıdan alınan verilerin sunucu tarafında doğrulanması, yalnızca kullanıcı deneyimini iyileştirmekle kalmaz, aynı zamanda uygulamanın güvenliğini ve kararlılığını da artırır. Client-side (tarayıcı tarafı) validasyon genellikle kullanıcıya anlık geri bildirim sağlamak için kullanılırken, sunucu tarafı validasyon güvenlik ve veri bütünlüğü için vazgeçilmezdir; çünkü client-side validasyon kolayca atlatılabilir.

Spring Validasyon Mekanizmasına Genel Bakış

Spring, validasyon için öncelikle Java EE’nin standart validasyon API’si olan Bean Validation API‘sini (JSR 380, önceki adıyla JSR 303) kullanır. Hibernate Validator ise bu API’nin referans implementasyonudur ve genellikle Spring Boot projelerinde otomatik olarak bağımlılık olarak eklenir.

Bean Validation API Annotasyonları

Bean Validation API, bir model sınıfının alanlarına doğrudan uygulanabilen bir dizi hazır annotasyon sunar. En sık kullanılanlardan bazıları şunlardır:

  • @NotNull: Alanın null olmamasını sağlar.
  • @NotEmpty: String, Collection veya Map’in boş olmamasını sağlar (boş string veya boş koleksiyon).
  • @NotBlank: String’in null olmamasını ve en az bir boşluk dışı karakter içermesini sağlar.
  • @Size(min, max): String, Collection veya Array’in boyutunu kontrol eder.
  • @Min(value), @Max(value): Sayısal değerler için minimum ve maksimum değerleri belirler.
  • @Email: Alanın geçerli bir e-posta formatında olmasını sağlar.
  • @Pattern(regexp): Düzenli ifade (regex) kullanarak alanın belirli bir desene uymasını sağlar.
  • @Past, @Future: Tarih alanlarının geçmişte veya gelecekte olmasını sağlar.

Spring’in Bean Validation Entegrasyonu

Spring MVC, controller metotlarında kullanılan model nesnelerine uygulanan bu annotasyonları otomatik olarak algılar ve doğrulama işlemini tetikler. Bu entegrasyon için anahtar bileşenler şunlardır:

  • @Valid veya @Validated annotasyonları: Controller metotlarında bir model nesnesinin doğrulanması gerektiğini belirtmek için kullanılır.
  • BindingResult: Doğrulama sonuçlarını (hataları) tutan bir nesnedir. Bu nesne, controller metot parametresi olarak @Valid veya @Validated annotasyonlu model nesnesinden hemen sonra gelmelidir.
  • WebDataBinder: Spring’in form verilerini model nesnelerine bağlamak için kullandığı mekanizmadır. Aynı zamanda validatörleri kaydetmek için de kullanılabilir.

Örnek Uygulama Geliştirme

Şimdi pratik bir örnek üzerinden Spring MVC form validasyonunu adım adım inceleyelim. Bir kullanıcı kayıt formu oluşturacak ve bu formdaki alanları doğrulayacağız.

1. Proje Kurulumu

Yeni bir Spring Boot projesi oluştururken veya mevcut bir projeye eklerken aşağıdaki bağımlılıkları pom.xml dosyanıza eklemeniz gerekmektedir:



    
    
        org.springframework.boot
        spring-boot-starter-web
    
    
    
        org.springframework.boot
        spring-boot-starter-validation
    
    
    
        org.springframework.boot
        spring-boot-starter-thymeleaf
    

2. Model Sınıfı Oluşturma (User DTO)

Kullanıcı kayıt formumuz için bir DTO (Data Transfer Object) veya model sınıfı oluşturalım ve Bean Validation annotasyonlarını uygulayalım:


package com.example.validationdemo.model;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import javax.validation.constraints.Min;
import javax.validation.constraints.Max;

public class User {

    @NotBlank(message = "Kullanıcı adı boş bırakılamaz.")
    @Size(min = 3, max = 20, message = "Kullanıcı adı 3 ile 20 karakter arasında olmalıdır.")
    private String username;

    @NotBlank(message = "E-posta boş bırakılamaz.")
    @Email(message = "Geçerli bir e-posta adresi giriniz.")
    private String email;

    @NotBlank(message = "Şifre boş bırakılamaz.")
    @Size(min = 6, message = "Şifre en az 6 karakter olmalıdır.")
    private String password;

    @Min(value = 18, message = "Yaşınız en az 18 olmalıdır.")
    @Max(value = 100, message = "Yaşınız 100'den büyük olamaz.")
    private int age;

    // Getter ve Setter metotları
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
}

Gördüğünüz gibi, her alan için uygun validasyon annotasyonlarını ve hata mesajlarını doğrudan model sınıfı üzerinde tanımladık.

3. Controller Sınıfı Oluşturma

Şimdi bu User modelini kullanan bir Spring MVC Controller sınıfı oluşturalım:


package com.example.validationdemo.controller;

import com.example.validationdemo.model.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import javax.validation.Valid;

@Controller
public class UserController {

    @GetMapping("/register")
    public String showRegistrationForm(Model model) {
        model.addAttribute("user", new User());
        return "user-form"; // user-form.html şablonunu döner
    }

    @PostMapping("/register")
    public String processRegistrationForm(
            @ModelAttribute("user") @Valid User user,
            BindingResult bindingResult,
            Model model) {

        if (bindingResult.hasErrors()) {
            // Validasyon hataları varsa, formu tekrar göster
            return "user-form";
        }

        // Validasyon başarılı ise, kullanıcıyı kaydetme veya başka bir sayfaya yönlendirme
        // Örneğin: userService.saveUser(user);
        model.addAttribute("message", "Kayıt Başarılı! Hoş geldiniz, " + user.getUsername());
        return "success"; // success.html şablonunu döner
    }
}

Yukarıdaki controller’da dikkat edilmesi gerekenler:

  • showRegistrationForm metodu, formu göstermek için boş bir User nesnesini modele ekler.
  • processRegistrationForm metodu, form gönderildiğinde çalışır.
  • @ModelAttribute("user"): Gelen form verilerini User nesnesine bağlar.
  • @Valid User user: Spring’e bu User nesnesinin Bean Validation kurallarına göre doğrulanması gerektiğini söyler.
  • BindingResult bindingResult: Doğrulama sonuçlarını tutar. @Valid annotasyonundan hemen sonra gelmesi ZORUNLUDUR. Aksi takdirde, validasyon hatası olduğunda Spring bir istisna fırlatır.
  • bindingResult.hasErrors(): Validasyon hataları olup olmadığını kontrol eder. Hata varsa, kullanıcı aynı sayfaya geri yönlendirilir ve hatalar gösterilir.

4. View Katmanı Oluşturma (Thymeleaf)

Şimdi user-form.html ve success.html adında iki Thymeleaf şablonu oluşturalım. Bunları src/main/resources/templates/ dizinine koymalısınız.

user-form.html:





    Kullanıcı Kayıt Formu
    


    

Kullanıcı Kayıt Formu

Thymeleaf’in th:field="{fieldName}" yapısı, form alanlarını modeldeki ilgili özelliklere bağlar. th:if="${#fields.hasErrors('fieldName')}" ve th:errors="{fieldName}" ise belirli bir alan için validasyon hatası varsa, ilgili hata mesajını göstermek için kullanılır.

success.html:





    Kayıt Başarılı
    


    

Kayıt Başarılı

Yeni Kayıt Yap

Hata Mesajlarını Özelleştirme ve İleri Seviye Validasyon

Hata Mesajlarını Özelleştirme (messages.properties)

Model sınıfındaki annotasyonlarda doğrudan message="..." kullanarak hata mesajlarını belirtebiliriz. Ancak, bu mesajları ayrı bir kaynak dosyasında tutmak (örneğin uluslararasılaştırma için) daha iyi bir yaklaşımdır. Bunun için src/main/resources/messages.properties dosyasını kullanabiliriz:


messages.properties

NotBlank.user.username=Kullanıcı adı boş bırakılamaz. Size.user.username=Kullanıcı adı {min} ile {max} karakter arasında olmalıdır. Email.user.email=Geçerli bir e-posta adresi giriniz. NotBlank.user.email=E-posta boş bırakılamaz. NotBlank.user.password=Şifre boş bırakılamaz. Size.user.password=Şifre en az {min} karakter olmalıdır. Min.user.age=Yaşınız en az {value} olmalıdır. Max.user.age=Yaşınız {value}'den büyük olamaz.

Bu dosyayı kullanabilmek için Spring Boot genellikle otomatik yapılandırır. Eğer otomatik çalışmazsa, application.properties dosyanıza spring.messages.basename=messages ekleyebilirsiniz. Mesaj anahtarları genellikle {ConstraintName}.{objectName}.{fieldName} veya sadece {ConstraintName} formatındadır.

Programatik Validasyon (Custom Validator)

Bazen Bean Validation annotasyonları yeterli olmayabilir, özellikle birden fazla alanı ilgilendiren karmaşık iş kurallarını doğrulamak gerektiğinde (örneğin, “şifre ve şifre tekrarı aynı olmalı” gibi). Bu durumlarda Spring’in org.springframework.validation.Validator arayüzünü uygulayan özel bir validatör yazabiliriz.


package com.example.validationdemo.validator;

import com.example.validationdemo.model.User;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

@Component
public class UserValidator implements Validator {

    @Override
    public boolean supports(Class> clazz) {
        return User.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        User user = (User) target;

        // Örnek: Kullanıcı adı ve e-posta aynı olamaz kuralı
        if (user.getUsername() != null && user.getEmail() != null &&
            user.getUsername().equalsIgnoreCase(user.getEmail())) {
            errors.rejectValue("email", "email.username.match", "E-posta kullanıcı adınızla aynı olamaz.");
        }

        // Başka karmaşık iş kuralları buraya eklenebilir.
        // Örneğin: Şifre tekrarı kontrolü için farklı bir alan olsaydı.
        // if (!user.getPassword().equals(user.getConfirmPassword())) {
        //     errors.rejectValue("confirmPassword", "password.mismatch", "Şifreler eşleşmiyor.");
        // }
    }
}

Bu özel validatörü controller’da kullanmak için @InitBinder metodunu kullanabiliriz:


// UserController sınıfı içinde
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import com.example.validationdemo.validator.UserValidator; // Yeni eklenen import

// ...

@Controller
public class UserController {

    private final UserValidator userValidator;

    public UserController(UserValidator userValidator) {
        this.userValidator = userValidator;
    }

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.addValidators(userValidator);
    }

    // ... diğer metodlar (showRegistrationForm, processRegistrationForm)
    // processRegistrationForm metodundaki @Valid anotasyonu hem Bean Validation'ı hem de
    // addValidators ile eklenen özel validatörü tetikleyecektir.
}

Artık @Valid annotasyonu hem Bean Validation kurallarını hem de UserValidator içindeki kuralları tetikleyecektir.

Sonuç ve Sıkça Sorulan Sorular (SSS)

Spring MVC, Bean Validation API ile entegrasyonu sayesinde form verilerini doğrulamak için güçlü, esnek ve okunabilir bir mekanizma sunar. Annotasyon tabanlı validasyon, çoğu senaryo için yeterli olsa da, özel validatörler aracılığıyla daha karmaşık iş kurallarını da kolayca uygulayabilirsiniz. Doğru validasyon stratejileri uygulamak, uygulamanızın sağlamlığını ve güvenliğini önemli ölçüde artıracaktır.

SSS (Sıkça Sorulan Sorular)

1. @Valid ile @Validated arasındaki fark nedir?

Her ikisi de bir nesnenin doğrulanması gerektiğini belirtmek için kullanılır. Temel fark şudur:

  • @Valid: Standart Bean Validation API’sinin bir parçasıdır. Tüm varsayılan validasyon gruplarını tetikler.
  • @Validated: Spring Framework’e özgüdür. @Valid‘in tüm özelliklerini sunar ve ek olarak “validasyon gruplarını” belirtmenize olanak tanır. Bu sayede, farklı senaryolar (örneğin, “kayıt” veya “güncelleme”) için farklı validasyon kurallarını tetikleyebilirsiniz.

Çoğu basit senaryoda @Valid yeterliyken, daha karmaşık uygulamalarda @Validated ile gruplama yeteneği oldukça faydalıdır.

2. Neden BindingResult parametresi @Valid‘den hemen sonra gelmeli?

Spring MVC, bir controller metodunda @Valid veya @Validated ile işaretlenmiş bir model nesnesi gördüğünde, doğrulama işlemini yapar. Eğer BindingResult parametresi hemen sonra gelmezse ve doğrulama hataları oluşursa, Spring bir MethodArgumentNotValidException veya BindException fırlatır ve bu da uygulamanızın 500 hatası vermesine neden olur. BindingResult nesnesi, bu hataları yakalamak ve işlemek için bir mekanizma sağlar, böylece uygulamanız hatayı fırlatmak yerine kullanıcıya dostça bir şekilde geri bildirimde bulunabilir.

3. Global hata mesajlarını nasıl gösteririz?

BindingResult nesnesi sadece alan bazlı hataları değil, aynı zamanda global hataları da içerebilir (örneğin, özel validatörler tarafından eklenen hatalar). Thymeleaf’te global hataları göstermek için th:if="${#fields.hasGlobalErrors()}" ve th:errors="*{global}" kullanılabilir:


4. Validasyon grupları nedir ve ne zaman kullanılır?

Validasyon grupları, belirli bir senaryo için geçerli olacak validasyon kurallarını gruplamanızı sağlar. Örneğin, bir “Kullanıcı Kayıt” formunda şifrenin zorunlu olması gerekirken, “Kullanıcı Profili Güncelleme” formunda şifrenin isteğe bağlı olması istenebilir. @Validated annotasyonu ile birlikte bir veya daha fazla interface sınıfını grup olarak belirtebilirsiniz. Örneğin:


// Gruplar için interface'ler
public interface OnCreate {}
public interface OnUpdate {}

// Model sınıfında annotasyonları gruplara göre ayırma
public class User {
    @NotBlank(groups = OnCreate.class) // Sadece OnCreate grubunda zorunlu
    private String password;
    // ...
}

// Controller'da belirli grubu tetikleme
@PostMapping("/register")
public String processRegistrationForm(@Validated(OnCreate.class) User user, BindingResult bindingResult) {
    // ...
}

Bu, aynı model sınıfını farklı validasyon gereksinimleriyle farklı formlarda veya işlemlerden kullanırken çok kullanışlıdır.

5. WebDataBinder ne işe yarar?

WebDataBinder, Spring MVC’de gelen HTTP istek parametrelerini (form verileri, query parametreleri vb.) controller metotlarının parametrelerine (model nesneleri, temel veri tipleri) bağlamakla görevli bir mekanizmadır. Ayrıca, bu bağlama işlemi sırasında veri tipi dönüşümlerini (örneğin string’i int’e çevirme) ve validasyon işlemlerini de yönetir. @InitBinder metodu aracılığıyla WebDataBinder‘ı özelleştirerek, özel validatörler ekleyebilir, özel tip dönüştürücüler tanımlayabilir veya belirli alanları bağlama işleminden hariç tutabilirsiniz. Bu, Spring MVC’nin veri bağlama ve doğrulama sürecine esnek bir şekilde müdahale etmenizi sağlar.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Gönder

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.
Exit mobile version