Takip et

ASP.NET Core’da FluentValidation ile Veri Doğrulama Sanatı

Modern web uygulamaları geliştirirken, kullanıcıdan alınan verilerin güvenilir ve tutarlı olması kritik öneme sahiptir. ASP.NET Core projelerinde veri doğrulama süreçlerini sadeleştiren, esnek ve okunabilir hale getiren FluentValidation kütüphanesi, geliştiricilere güçlü bir çözüm sunar. Bu makale, FluentValidation’ın temellerinden ileri düzey kullanımına, gerçek dünya senaryolarından mobil uyumlu doğrulama ipuçlarına kadar her şeyi kapsayan kapsamlı bir rehber niteliğindedir.

Bir web uygulaması, kullanıcıdan gelen girdilerle sürekli etkileşim halindedir. Bu girdiler, bir kayıt formu, ürün siparişi ya da arama sorgusu olabilir. Ancak, her zaman beklenen formatta veya değer aralığında olmayabilirler. Geçersiz veya kötü niyetli verilerin sisteme girmesi, uygulamanın çökmesine, güvenlik açıklarına yol açmasına, veri bütünlüğünün bozulmasına ve dolayısıyla kullanıcı deneyiminin olumsuz etkilenmesine neden olabilir. Bu nedenle, verilerin işlenmeden önce doğru ve geçerli olduğundan emin olmak hayati bir adımdır.

Geleneksel olarak, ASP.NET Core’da veri doğrulama için genellikle Data Annotations (veri açıklamaları) kullanılır. Bu yöntem, model özelliklerinin üzerine [Required], [StringLength] gibi nitelikler eklemeyi içerir. Ancak, iş mantığı karmaşıklaştıkça veya birden fazla doğrulama kuralı bir araya geldiğinde, Data Annotations yöntemi yeterince esnek olmayabilir. Kurallar modelin içine dağıldığı için kodun okunabilirliği düşer ve özellikle aynı modelin farklı senaryolarda farklı doğrulama kurallarına ihtiyaç duyması durumunda yönetimi zorlaşır. İşte tam bu noktada FluentValidation devreye girer. FluentValidation, doğrulama kurallarını ayrı sınıflarda tanımlayarak modelden ayırır ve böylece daha temiz, daha test edilebilir ve sürdürülebilir bir kod yapısı sunar. Bu sayede, uygulamanızın doğrulama katmanı bağımsız bir birim olarak ele alınabilir ve gerektiğinde kolayca değiştirilebilir veya genişletilebilir.

FluentValidation’ın en büyük avantajlarından biri, lambda ifadeleri kullanarak kuralları son derece okunabilir ve akıcı bir şekilde tanımlamanıza olanak tanımasıdır. Bu, kuralların ne işe yaradığını anlamayı kolaylaştırır ve yeni bir geliştiricinin projeye adapte olmasını hızlandırır. Örneğin, bir kullanıcının e-posta adresinin geçerli bir formatta olması gerektiğini veya bir şifrenin minimum uzunluğa sahip olması gerektiğini belirtmek, FluentValidation ile adeta doğal bir dilmiş gibi yazılabilir. Ayrıca, koşullu doğrulama, özel hata mesajları, kural kümeleri ve asenkron doğrulama gibi güçlü özellikler sayesinde, en karmaşık doğrulama senaryoları bile şeffaf ve yönetilebilir bir şekilde ele alınabilir. Bu esneklik, özellikle büyük ve sürekli gelişen projelerde zaman ve emek tasarrufu sağlar, aynı zamanda yazılım kalitesini önemli ölçüde artırır. Dolayısıyla, veri bütünlüğünü sağlamak ve kullanıcı deneyimini iyileştirmek için FluentValidation, ASP.NET Core projelerinde vazgeçilmez bir araç haline gelmiştir.

ASP.NET Core’da FluentValidation: Temel Kavramlar Nelerdir?

FluentValidation, .NET dünyasında popüler bir doğrulama kütüphanesidir ve ASP.NET Core ile mükemmel bir uyum içinde çalışır. Temel amacı, model doğrulama kurallarını açık ve okunabilir bir şekilde tanımlamanıza olanak tanımaktır. Gelin, FluentValidation’ın temel yapı taşlarına ve nasıl çalıştığına yakından bakalım.

Model ve Doğrulayıcı (Validator) Ayrımı

FluentValidation’ın merkezinde “model” ve “doğrulayıcı” kavramlarının net bir şekilde ayrılması yatar. Bir model, uygulamanızın işleyeceği verileri (örneğin, bir kullanıcı kaydı formu veya bir ürün detayı) temsil eden düz bir C# sınıfıdır. Doğrulayıcı ise, bu model üzerindeki doğrulama kurallarını içeren ayrı bir sınıftır. Bu ayrım, model sınıflarınızın iş mantığından ve doğrulama kurallarından bağımsız kalmasını sağlar. Böylece, modeliniz sadece veriyi taşırken, doğrulayıcınız sadece bu verinin geçerliliğini kontrol eder.

Örneğin, bir KullaniciKayitModel‘iniz varsa, buna karşılık gelen bir KullaniciKayitValidator sınıfınız olur. Bu doğrulayıcı sınıfı, AbstractValidator sınıfından türetilir ve yapıcı metodu (constructor) içinde modelin her bir özelliği için doğrulama kurallarını tanımlamanıza olanak tanır. Kurallar, RuleFor() metodu ile başlar ve ardından bir dizi zincirlenebilir doğrulama metodu (NotEmpty(), EmailAddress(), Length() vb.) ile devam eder. Bu akıcı (fluent) arayüz, kuralların doğal bir dil gibi okunmasını sağlar.

Fluent API ile Kural Tanımlama

FluentValidation’ın en belirgin özelliklerinden biri, doğrulama kurallarını tanımlamak için kullandığı akıcı (fluent) API’dir. Bu API, lambda ifadeleri ve metod zincirleme (method chaining) kullanarak kuralları ardışık ve okunabilir bir şekilde yazmanıza imkan tanır. Örneğin, bir e-posta adresinin boş olmaması ve geçerli bir e-posta formatında olması gerektiğini şu şekilde belirtebilirsiniz:


public class KullaniciKayitModel
{
    public string Ad { get; set; }
    public string Soyad { get; set; }
    public string Eposta { get; set; }
    public string Sifre { get; set; }
}

public class KullaniciKayitValidator : AbstractValidator
{
    public KullaniciKayitValidator()
    {
        RuleFor(k => k.Ad).NotEmpty().WithMessage("Ad alanı boş bırakılamaz.");
        RuleFor(k => k.Soyad).NotEmpty().WithMessage("Soyad alanı boş bırakılamaz.");
        RuleFor(k => k.Eposta)
            .NotEmpty().WithMessage("E-posta alanı boş bırakılamaz.")
            .EmailAddress().WithMessage("Geçerli bir e-posta adresi giriniz.");
        RuleFor(k => k.Sifre)
            .NotEmpty().WithMessage("Şifre alanı boş bırakılamaz.")
            .MinimumLength(8).WithMessage("Şifre en az 8 karakter olmalıdır.");
    }
}

Yukarıdaki örnekte, RuleFor() metoduna geçirilen lambda ifadesi (k => k.Eposta) hangi özellik için kural tanımlandığını belirtir. Ardından NotEmpty() ve EmailAddress() gibi doğrulayıcı metotlar zincirlenir. Her kural için özel hata mesajları WithMessage() metodu ile kolayca tanımlanabilir. Bu yapı, hem karmaşık kuralları ifade etme gücü sunar hem de kodu anlaşılır kılar.

Dependency Injection (Bağımlılık Enjeksiyonu) ile Entegrasyon

ASP.NET Core'un temel taşlarından biri olan Bağımlılık Enjeksiyonu (DI), FluentValidation ile kusursuz bir şekilde entegre olur. FluentValidation doğrulayıcılarınızı ASP.NET Core'un servis kapsayıcısına kaydettiğinizde, bunları kontrolcülerinizde veya diğer servislerinizde kolayca kullanabilirsiniz. Bu, doğrulayıcılarınızın yaşam döngüsünü yönetmeyi kolaylaştırır ve uygulamanızın daha modüler olmasını sağlar. Doğrulayıcılarınızı bağımlılık enjeksiyonu ile yönetmek, test yazmayı da kolaylaştırır, çünkü doğrulayıcıları ayrı ayrı test edebilir ve sahte (mock) bağımlılıklarla çalışabilirsiniz.

Bu temel kavramlar, FluentValidation ile güçlü ve sürdürülebilir veri doğrulama çözümleri oluşturmanız için sağlam bir temel sağlar. Kütüphanenin sunduğu bu esneklik ve okunabilirlik, özellikle büyük ölçekli ve ekip tabanlı projelerde geliştirici verimliliğini artırır ve yazılım kalitesini yükseltir.

FluentValidation Entegrasyonu: Adım Adım ASP.NET Core Uygulamanıza Nasıl Dahil Edilir?

ASP.NET Core uygulamanıza FluentValidation'ı entegre etmek oldukça basit bir süreçtir. Bu bölümde, gerekli adımları ve yapılandırmaları adım adım ele alacağız, böylece uygulamanızda güçlü bir doğrulama altyapısı kurabilirsiniz.

1. FluentValidation Paketlerini Yükleme

İlk adım, projenize FluentValidation NuGet paketlerini eklemektir. Genellikle iki ana pakete ihtiyacınız olacaktır: FluentValidation ve FluentValidation.AspNetCore. İkinci paket, FluentValidation'ın ASP.NET Core'un dahili model doğrulama mekanizmasıyla entegrasyonunu sağlar.


dotnet add package FluentValidation
dotnet add package FluentValidation.AspNetCore

Veya NuGet Paket Yöneticisi aracılığıyla bu paketleri projenize ekleyebilirsiniz.

2. Bağımlılık Enjeksiyonu (DI) ile FluentValidation'ı Yapılandırma

FluentValidation doğrulayıcılarınızın ASP.NET Core'un DI sistemine kayıt edilmesi gerekmektedir. Bu genellikle Program.cs (veya eski projelerde Startup.cs) dosyasında AddControllersWithViews() veya AddRazorPages() çağrısının yapıldığı yerde gerçekleştirilir.


// Program.cs
using FluentValidation;
using FluentValidation.AspNetCore;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews(); // veya AddControllers() eğer sadece API ise

// FluentValidation entegrasyonu
builder.Services.AddFluentValidationAutoValidation(); // Model otomatik doğrulamasını etkinleştirir
builder.Services.AddFluentValidationClientsideAdapters(); // İstemci tarafı doğrulama adaptörlerini ekler

// Doğrulayıcılarınızı servis olarak kaydedin.
// Assembly scanning ile tüm doğrulayıcıları otomatik olarak kaydedebilirsiniz.
builder.Services.AddValidatorsFromAssemblyContaining();

var app = builder.Build();

// ... diğer yapılandırmalar

app.Run();

Yukarıdaki kod bloğunda şunları yapıyoruz:

  • AddFluentValidationAutoValidation(): ASP.NET Core'un varsayılan model doğrulama altyapısını FluentValidation ile değiştirir. Bu sayede, FluentValidation kurallarına uymayan modeller otomatik olarak hataları yakalayacaktır.
  • AddFluentValidationClientsideAdapters(): Bu yöntem, sunucu tarafında tanımladığınız FluentValidation kurallarının bazıları için istemci tarafında da doğrulama adaptörleri oluşturmaya çalışır. Bu, kullanıcıların sunucuya istek göndermeden önce anında geri bildirim almasını sağlar ve kullanıcı deneyimini iyileştirir. Ancak tüm kurallar için istemci tarafı doğrulama otomatik olarak desteklenmeyebilir.
  • AddValidatorsFromAssemblyContaining(): Bu çok kullanışlı bir metottur. Belirttiğiniz tipin bulunduğu assembly (derleme) içindeki tüm AbstractValidator türevlerini otomatik olarak DI konteynerine kaydeder. Böylece her doğrulayıcıyı tek tek kaydetmek zorunda kalmazsınız.

3. Model ve Doğrulayıcı Sınıflarını Tanımlama

Önceki bölümde gördüğünüz gibi, uygulamanızdaki her doğrulanabilir model için bir doğrulayıcı sınıfı oluşturmanız gerekir. Örneğin, bir kayıt işlemi için KullaniciKayitModel ve buna karşılık gelen KullaniciKayitValidator.


// Models/KullaniciKayitModel.cs
public class KullaniciKayitModel
{
    public string Ad { get; set; }
    public string Eposta { get; set; }
    public string Sifre { get; set; }
    public string SifreTekrar { get; set; }
}

// Validators/KullaniciKayitValidator.cs
public class KullaniciKayitValidator : AbstractValidator
{
    public KullaniciKayitValidator()
    {
        RuleFor(k => k.Ad)
            .NotEmpty().WithMessage("Ad alanı boş bırakılamaz.")
            .Length(2, 50).WithMessage("Ad en az 2, en fazla 50 karakter olmalıdır.");

        RuleFor(k => k.Eposta)
            .NotEmpty().WithMessage("E-posta alanı boş bırakılamaz.")
            .EmailAddress().WithMessage("Geçerli bir e-posta adresi giriniz.");

        RuleFor(k => k.Sifre)
            .NotEmpty().WithMessage("Şifre alanı boş bırakılamaz.")
            .MinimumLength(8).WithMessage("Şifre en az 8 karakter olmalıdır.");

        RuleFor(k => k.SifreTekrar)
            .Equal(k => k.Sifre).WithMessage("Şifreler eşleşmiyor.");
    }
}

4. Kontrolcünüzde Doğrulamayı Kullanma

FluentValidation'ı yapılandırdıktan sonra, kontrolcülerinizde manuel bir işlem yapmanıza gerek kalmaz. ASP.NET Core'un model bağlama (model binding) süreci sırasında, gelen model FluentValidation tarafından otomatik olarak doğrulanır. Doğrulama hataları, ModelState koleksiyonuna eklenir ve siz bu hataları kontrol edebilirsiniz.


using Microsoft.AspNetCore.Mvc;
using YourAppName.Models; // Modelinizin namespace'i

public class HesapController : Controller
{
    [HttpPost]
    public IActionResult KayitOl(KullaniciKayitModel model)
    {
        if (!ModelState.IsValid)
        {
            // ModelState geçerli değilse, doğrulama hataları var demektir.
            // Hataları loglayabilir, kullanıcıya geri döndürebilir veya başka işlemler yapabilirsiniz.
            foreach (var state in ModelState)
            {
                foreach (var error in state.Value.Errors)
                {
                    Console.WriteLine($"{state.Key}: {error.ErrorMessage}");
                }
            }
            return View(model); // Ya da BadRequest(ModelState) eğer bir API ise
        }

        // Model geçerliyse, kayıt işlemini tamamla
        // ...
        return RedirectToAction("KayitBasarili");
    }
}

Uzman İpucu: API projelerinde, [ApiController] niteliği (attribute) kullanılan kontrolcülerde ModelState.IsValid kontrolü otomatik olarak yapılır ve geçersiz modeller için 400 Bad Request yanıtı otomatik olarak döndürülür. Bu, API geliştiricileri için büyük bir kolaylık sağlar.

Bu adımları takip ederek FluentValidation'ı ASP.NET Core uygulamanıza başarıyla entegre edebilir ve veri doğrulama süreçlerinizi çok daha verimli ve yönetilebilir hale getirebilirsiniz.

Karmaşık Senaryolar İçin FluentValidation: İleri Düzey Kullanım Teknikleri

FluentValidation, sadece basit alan doğrulamaları yapmakla kalmaz, aynı zamanda karmaşık iş mantığını ve koşullu doğrulama senaryolarını da güçlü bir şekilde ele almanızı sağlar. Bu bölümde, daha deneyimli kullanıcılar için bazı ileri düzey tekniklere ve püf noktalarına odaklanacağız.

Koşullu Doğrulama (Conditional Validation)

Bazı durumlarda, bir kuralın yalnızca belirli koşullar altında uygulanması gerekebilir. Örneğin, bir kullanıcının telefon numarası alanı, yalnızca eğer IletisimTercihi özelliği 'Telefon' olarak ayarlanmışsa zorunlu olabilir. FluentValidation, When() ve Unless() metotları ile koşullu doğrulama yapmanıza olanak tanır.


public class IletisimModel
{
    public string IletisimTercihi { get; set; } // "Email" veya "Telefon"
    public string EpostaAdresi { get; set; }
    public string TelefonNumarasi { get; set; }
}

public class IletisimValidator : AbstractValidator
{
    public IletisimValidator()
    {
        RuleFor(x => x.EpostaAdresi)
            .NotEmpty().WithMessage("E-posta adresi boş bırakılamaz.")
            .EmailAddress().WithMessage("Geçerli bir e-posta adresi giriniz.")
            .When(x => x.IletisimTercihi == "Email"); // Sadece tercih Email ise bu kural uygulanır

        RuleFor(x => x.TelefonNumarasi)
            .NotEmpty().WithMessage("Telefon numarası boş bırakılamaz.")
            .Matches(@"^\d{10}$").WithMessage("Geçerli bir 10 haneli telefon numarası giriniz.")
            .When(x => x.IletisimTercihi == "Telefon"); // Sadece tercih Telefon ise bu kural uygulanır
    }
}

Unless() ise When() metodunun tam tersi şekilde çalışır; belirli bir koşul doğru değilse kuralı uygular. Bu sayede, uygulamanızın doğrulama mantığını iş akışınıza göre hassas bir şekilde ayarlayabilirsiniz.

İç İçe Doğrulama (Nested Validators)

Uygulamalar genellikle karmaşık veri yapılarına sahiptir. Bir model başka bir model nesnesini içerebilir (örneğin, bir Sipariş modelinin içinde birden fazla SiparisKalemi nesnesi olması). FluentValidation, SetValidator() metodu ile iç içe modelleri doğrulamayı da destekler.


public class Adres
{
    public string Sokak { get; set; }
    public string Sehir { get; set; }
    public string PostaKodu { get; set; }
}

public class KullaniciProfilModel
{
    public string KullaniciAdi { get; set; }
    public Adres EvAdresi { get; set; }
}

public class AdresValidator : AbstractValidator
{
    public AdresValidator()
    {
        RuleFor(adres => adres.Sokak).NotEmpty().WithMessage("Sokak boş bırakılamaz.");
        RuleFor(adres => adres.Sehir).NotEmpty().WithMessage("Şehir boş bırakılamaz.");
        RuleFor(adres => adres.PostaKodu).NotEmpty().Length(5).WithMessage("Geçerli bir 5 haneli posta kodu giriniz.");
    }
}

public class KullaniciProfilValidator : AbstractValidator
{
    public KullaniciProfilValidator()
    {
        RuleFor(k => k.KullaniciAdi).NotEmpty().WithMessage("Kullanıcı adı boş bırakılamaz.");
        RuleFor(k => k.EvAdresi).SetValidator(new AdresValidator()); // İç içe doğrulayıcıyı ayarla
    }
}

Bu örnekte, KullaniciProfilValidator içinde EvAdresi özelliği için AdresValidator'ı kullanarak iç içe doğrulamayı etkinleştirdik. Bu sayede, daha küçük, odaklanmış doğrulayıcılar oluşturabilir ve bunları karmaşık modellerde yeniden kullanabilirsiniz, bu da kod tekrarını azaltır ve bakımı kolaylaştırır.

Kural Kümeleri (Rule Sets)

Bazı durumlarda, aynı modelin farklı senaryolarda farklı doğrulama kurallarına sahip olması gerekebilir. Örneğin, bir kullanıcının kayıt aşamasında belirli kurallar, profil güncelleme aşamasında ise farklı kurallar geçerli olabilir. FluentValidation, RuleSet() ile kural kümeleri tanımlamanıza olanak tanır.


public class UrunModel
{
    public int Id { get; set; }
    public string Ad { get; set; }
    public decimal Fiyat { get; set; }
    public int StokAdedi { get; set; }
}

public class UrunValidator : AbstractValidator
{
    public UrunValidator()
    {
        // Varsayılan kural kümesi (kural kümesi belirtilmediğinde uygulanır)
        RuleFor(u => u.Ad).NotEmpty().WithMessage("Ürün adı boş olamaz.");
        RuleFor(u => u.Fiyat).GreaterThan(0).WithMessage("Fiyat sıfırdan büyük olmalıdır.");

        // "Create" kural kümesi
        RuleSet("Create", () =>
        {
            RuleFor(u => u.Id).Equal(0).WithMessage("Yeni ürün oluşturulurken ID değeri olmamalıdır.");
            RuleFor(u => u.StokAdedi).GreaterThanOrEqualTo(0).WithMessage("Stok adedi negatif olamaz.");
        });

        // "Update" kural kümesi
        RuleSet("Update", () =>
        {
            RuleFor(u => u.Id).GreaterThan(0).WithMessage("Ürün güncellenirken ID değeri belirtilmelidir.");
            RuleFor(u => u.Ad).Length(3, 100).WithMessage("Ürün adı 3-100 karakter arasında olmalıdır.");
        });
    }
}

Bir kontrolcüde belirli bir kural kümesini çalıştırmak için, doğrulayıcıyı çağırırken RuleSet parametresini belirtebilirsiniz:


public class UrunController : Controller
{
    private readonly IValidator _urunValidator;

    public UrunController(IValidator urunValidator)
    {
        _urunValidator = urunValidator;
    }

    [HttpPost("create")]
    public IActionResult UrunOlustur([FromBody] UrunModel model)
    {
        var result = _urunValidator.Validate(model, options => options.IncludeRuleSets("Create"));
        if (!result.IsValid)
        {
            return BadRequest(result.Errors);
        }
        // ... Ürün oluşturma işlemleri
        return Ok("Ürün başarıyla oluşturuldu.");
    }

    [HttpPut("update")]
    public IActionResult UrunGuncelle([FromBody] UrunModel model)
    {
        var result = _urunValidator.Validate(model, options => options.IncludeRuleSets("Update"));
        if (!result.IsValid)
        {
            return BadRequest(result.Errors);
        }
        // ... Ürün güncelleme işlemleri
        return Ok("Ürün başarıyla güncellendi.");
    }
}

Bu ileri düzey özellikler, FluentValidation'ın karmaşık iş gereksinimlerini karşılamada ne kadar esnek ve güçlü olduğunu göstermektedir. Bu teknikleri ustaca kullanarak, daha modüler, anlaşılır ve bakımı kolay doğrulama katmanları oluşturabilirsiniz.

Özel Doğrulayıcılar ve Asenkron Doğrulama: Esneklik Nasıl Sağlanır?

FluentValidation'ın sunduğu yerleşik doğrulama kuralları çoğu senaryo için yeterli olsa da, bazen iş mantığınızın gerektirdiği benzersiz kontrolleri uygulamak için özel doğrulayıcılara veya harici kaynaklara (veritabanı, API) erişim gerektiren asenkron doğrulamalara ihtiyacınız olabilir. Bu bölümde, bu senaryoları nasıl ele alacağınızı inceleyeceğiz.

Özel Doğrulayıcılar (Custom Validators) Oluşturma

FluentValidation, yerleşik kurallarının ötesine geçmenizi sağlayan çeşitli yollar sunar. En basit yol, Must() metodunu kullanarak kendi doğrulama mantığınızı doğrudan bir kural içinde tanımlamaktır. Daha karmaşık ve yeniden kullanılabilir bir doğrulama için, özel bir kural uzantısı veya özel bir PropertyValidator türevi oluşturabilirsiniz.


// Doğrudan kural içinde özel mantık
public class KullaniciValidatorWithCustomLogic : AbstractValidator
{
    public KullaniciValidatorWithCustomLogic()
    {
        RuleFor(k => k.Sifre)
            .Must(sifre => SifreGucunuKontrolEt(sifre))
            .WithMessage("Şifre en az bir büyük harf, bir küçük harf, bir rakam ve bir özel karakter içermelidir.");
    }

    private bool SifreGucunuKontrolEt(string sifre)
    {
        // Burada kendi şifre karmaşıklık mantığınızı uygulayın
        // Örnek: regex kullanarak kontrol
        return sifre != null && Regex.IsMatch(sifre, @"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{8,}$");
    }
}

// Özel kural uzantısı olarak yeniden kullanılabilir doğrulayıcı
public static class CustomValidatorExtensions
{
    public static IRuleBuilder SifreGucuneSahip(this IRuleBuilder ruleBuilder)
    {
        return ruleBuilder.Must(sifre =>
        {
            // Daha karmaşık şifre gücü kontrolü
            return sifre != null && Regex.IsMatch(sifre, @"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{8,}$");
        }).WithMessage("Şifre zayıf. En az 8 karakter, büyük/küçük harf, rakam ve özel karakter içermelidir.");
    }
}

// Kullanım
public class KullaniciValidatorWithExtension : AbstractValidator
{
    public KullaniciValidatorWithExtension()
    {
        RuleFor(k => k.Sifre).SifreGucuneSahip(); // Kendi uzantı metodumuzu kullanıyoruz
    }
}

Özel uzantı metotları, sıkça kullanılan karmaşık doğrulama mantıklarını soyutlamanıza ve kod tekrarını önlemenize yardımcı olur. Bu, kod tabanınızın daha temiz ve yönetilebilir olmasını sağlar.

Asenkron Doğrulama (Asynchronous Validation)

Bazen doğrulama işlemleri, bir veritabanı sorgusu veya harici bir API çağrısı gibi zaman alıcı operasyonlar gerektirebilir. Örneğin, bir kullanıcının kaydolurken girdiği e-posta adresinin veritabanında daha önce kullanılıp kullanılmadığını kontrol etmek. Bu tür senaryolar için FluentValidation, asenkron doğrulama desteği sunar.

MustAsync() metodu, Task döndüren bir lambda ifadesi veya metot ile kullanılabilir. Bu, I/O yoğun işlemlerin ana iş parçacığını (thread) bloke etmeden gerçekleştirilmesini sağlar.


public class YeniKullaniciModel
{
    public string Eposta { get; set; }
    public string KullaniciAdi { get; set; }
}

public interface IKullaniciServisi
{
    Task EpostaMevcutMu(string eposta);
    Task KullaniciAdiMevcutMu(string kullaniciAdi);
}

public class YeniKullaniciValidator : AbstractValidator
{
    public YeniKullaniciValidator(IKullaniciServisi kullaniciServisi)
    {
        RuleFor(k => k.Eposta)
            .NotEmpty().WithMessage("E-posta boş bırakılamaz.")
            .EmailAddress().WithMessage("Geçerli bir e-posta adresi giriniz.")
            .MustAsync(async (eposta, cancellation) => !await kullaniciServisi.EpostaMevcutMu(eposta))
            .WithMessage("Bu e-posta adresi zaten kullanımda.");

        RuleFor(k => k.KullaniciAdi)
            .NotEmpty().WithMessage("Kullanıcı adı boş bırakılamaz.")
            .MustAsync(async (kullaniciAdi, cancellation) => !await kullaniciServisi.KullaniciAdiMevcutMu(kullaniciAdi))
            .WithMessage("Bu kullanıcı adı zaten alınmış.");
    }
}

Yukarıdaki örnekte, YeniKullaniciValidator, DI aracılığıyla bir IKullaniciServisi almaktadır. Bu servis, veritabanı sorgularını veya API çağrılarını simüle eden asenkron metotlara sahiptir. MustAsync() metodu, e-posta ve kullanıcı adının sistemde mevcut olup olmadığını asenkron olarak kontrol eder. Bu yapı, uygulamanızın performansını ve yanıt verebilirliğini korurken karmaşık iş kurallarını uygulamanıza olanak tanır.

Uzman İpucu: Asenkron doğrulama kullanırken, performans etkisini göz önünde bulundurun. Her doğrulama kuralının asenkron olmaması gerekir. Yalnızca I/O veya ağ işlemleri içeren doğrulama kuralları için MustAsync kullanın. Ayrıca, cancellation token'ı doğru bir şekilde yönetmek, uzun süren işlemlerin iptal edilmesini sağlayarak kaynak israfını önler.

Özel doğrulayıcılar ve asenkron doğrulama yetenekleri sayesinde FluentValidation, uygulamanızın doğrulama ihtiyaçlarına tam olarak uyum sağlayabilir. Bu esneklik, geliştiricilere karmaşık ve performans gerektiren senaryolarda bile güçlü çözümler üretme imkanı sunar.

Gerçek Dünya Senaryosu: Bir API Projesinde FluentValidation ile Kullanıcı Kaydı Doğrulaması

Bu bölümde, FluentValidation'ı gerçek bir ASP.NET Core Web API projesinde, kullanıcı kayıt işlemi sırasında veri doğrulama amacıyla nasıl kullanacağımızı adım adım inceleyeceğiz. Bu senaryo, tipik bir API geliştirme sürecindeki yaygın doğrulama ihtiyaçlarını kapsayacaktır.

Senaryo: Yeni Kullanıcı Kayıt API'si

Bir e-ticaret uygulamasının arka ucu için yeni bir kullanıcı kayıt API'si geliştirdiğinizi düşünün. Kullanıcılardan ad, soyad, e-posta ve şifre gibi bilgileri alıyorsunuz. Bu bilgilerin belirli kurallara göre doğrulanması gerekiyor:

  • Ad ve Soyad boş olmamalı, belirli bir uzunlukta olmalı.
  • E-posta adresi boş olmamalı, geçerli formatta olmalı ve veritabanında daha önce kullanılmamış olmalı (benzersizlik).
  • Şifre boş olmamalı, minimum uzunlukta olmalı, en az bir büyük harf, bir küçük harf, bir rakam ve bir özel karakter içermeli.
  • Şifre tekrarı, şifre ile eşleşmeli.

Adım 1: DTO (Data Transfer Object) Tanımlama

API'ye gelen veriyi temsil etmek için bir DTO (Data Transfer Object) oluşturalım. Bu DTO, istemciden beklediğimiz alanları içerecektir.


// Models/KullaniciKayitDTO.cs
public class KullaniciKayitDTO
{
    public string Ad { get; set; }
    public string Soyad { get; set; }
    public string Eposta { get; set; }
    public string Sifre { get; set; }
    public string SifreTekrar { get; set; }
}

Adım 2: Kullanıcı Servisi Oluşturma (E-posta Benzersizliği İçin)

E-posta benzersizliği kontrolünü simüle etmek için basit bir servis arayüzü ve uygulaması tanımlayalım.


// Services/IKullaniciRepository.cs
public interface IKullaniciRepository
{
    Task EpostaVarMi(string eposta);
}

// Services/KullaniciRepository.cs
public class KullaniciRepository : IKullaniciRepository
{
    // Gerçek bir uygulamada bu veritabanı sorgusu yapardı.
    // Şimdilik sadece örnek amaçlı bazı e-postaları "kullanımda" olarak işaretleyelim.
    private readonly List _mevcutEpostalar = new List { "test@example.com", "admin@example.com" };

    public async Task EpostaVarMi(string eposta)
    {
        await Task.Delay(100); // Asenkron bir işlemi simüle et
        return _mevcutEpostalar.Contains(eposta.ToLower());
    }
}

Adım 3: FluentValidation Doğrulayıcısını Tanımlama

Şimdi KullaniciKayitDTO için tüm doğrulama kurallarını içeren KullaniciKayitValidator sınıfını oluşturalım.


// Validators/KullaniciKayitValidator.cs
using FluentValidation;
using System.Text.RegularExpressions;
using YourAppName.Models;
using YourAppName.Services; // IKullaniciRepository için

public class KullaniciKayitValidator : AbstractValidator
{
    private readonly IKullaniciRepository _kullaniciRepository;

    public KullaniciKayitValidator(IKullaniciRepository kullaniciRepository)
    {
        _kullaniciRepository = kullaniciRepository;

        RuleFor(x => x.Ad)
            .NotEmpty().WithMessage("Ad alanı boş bırakılamaz.")
            .Length(2, 50).WithMessage("Ad en az 2, en fazla 50 karakter olmalıdır.");

        RuleFor(x => x.Soyad)
            .NotEmpty().WithMessage("Soyad alanı boş bırakılamaz.")
            .Length(2, 50).WithMessage("Soyad en az 2, en fazla 50 karakter olmalıdır.");

        RuleFor(x => x.Eposta)
            .NotEmpty().WithMessage("E-posta alanı boş bırakılamaz.")
            .EmailAddress().WithMessage("Geçerli bir e-posta adresi giriniz.")
            .MustAsync(async (eposta, cancellation) => !await _kullaniciRepository.EpostaVarMi(eposta))
            .WithMessage("Bu e-posta adresi zaten kullanımda.");

        RuleFor(x => x.Sifre)
            .NotEmpty().WithMessage("Şifre alanı boş bırakılamaz.")
            .MinimumLength(8).WithMessage("Şifre en az 8 karakter olmalıdır.")
            .Must(sifre => Regex.IsMatch(sifre, @"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{8,}$"))
            .WithMessage("Şifre en az bir büyük harf, bir küçük harf, bir rakam ve bir özel karakter içermelidir.");

        RuleFor(x => x.SifreTekrar)
            .Equal(x => x.Sifre).WithMessage("Şifreler eşleşmiyor.");
    }
}

Adım 4: Startup Yapılandırması ve Bağımlılık Enjeksiyonu

Program.cs dosyasında, FluentValidation'ı ve IKullaniciRepository servislerimizi kaydedelim.


// Program.cs
using FluentValidation;
using FluentValidation.AspNetCore;
using YourAppName.Services;
using YourAppName.Validators; // KullaniciKayitValidator için

var builder = WebApplication.CreateBuilder(args);

// API kontrolcülerini ekle
builder.Services.AddControllers();

// Servisleri ekle
builder.Services.AddScoped();

// FluentValidation entegrasyonu
builder.Services.AddFluentValidationAutoValidation();
builder.Services.AddValidatorsFromAssemblyContaining(); // Tüm doğrulayıcıları kaydet

// ... diğer servisler

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.UseRouting();
app.UseAuthorization();
app.MapControllers();

app.Run();

Adım 5: API Kontrolcüsünü Oluşturma

Son olarak, kullanıcı kayıt isteğini işleyecek olan API kontrolcüsünü oluşturalım.


// Controllers/HesapController.cs
using Microsoft.AspNetCore.Mvc;
using YourAppName.Models;
using System.Threading.Tasks;

[ApiController]
[Route("api/[controller]")]
public class HesapController : ControllerBase
{
    // [ApiController] niteliği sayesinde ModelState.IsValid kontrolünü otomatik yapıyor.
    // Manual kontrol yapmaya gerek kalmıyor.

    [HttpPost("kayitol")]
    public async Task KayitOl([FromBody] KullaniciKayitDTO model)
    {
        // FluentValidation tarafından otomatik olarak doğrulanacak.
        // Eğer ModelState geçerli değilse, [ApiController] otomatik olarak 400 Bad Request dönecektir.
        // Hata detayları JSON olarak gönderilecektir.

        // Model geçerliyse kayıt işlemini simüle edelim.
        // Gerçek bir uygulamada, burada KullaniciKayitDTO'yu Kullanici entity'sine dönüştürüp
        // veritabanına kaydetme, e-posta onayı gönderme vb. işlemler yapılır.

        await Task.Delay(100); // Kayıt işlemini simüle et

        return Ok(new { Mesaj = "Kullanıcı başarıyla kaydedildi.", KullaniciAdi = model.Ad, Eposta = model.Eposta });
    }
}

Bu gerçek dünya senaryosunda, FluentValidation'ın nasıl kullanıldığını, asenkron doğrulamayı ve [ApiController] niteliği ile otomatik hata yönetiminin API projelerinde ne kadar faydalı olduğunu gördük. Bu yaklaşım, API'lerinizin sağlam, güvenilir ve bakımı kolay olmasını sağlar.

Mobil Uyumlu Form Doğrulaması: Duyarlı Tasarım İpuçları

Modern web uygulamalarında, kullanıcı deneyimi mobil cihazlarda da kusursuz olmalıdır. Form doğrulama, mobil kullanıcılar için özellikle kritik bir rol oynar, çünkü küçük ekranlarda hata mesajlarının görünürlüğü ve anlaşılırlığı daha da önemlidir. FluentValidation doğrudan mobil uyumlu CSS veya JavaScript üretmese de, onunla birlikte çalışan ön yüz (front-end) kodunuzu nasıl tasarlayacağınız konusunda bazı önemli ipuçları vardır.

Doğrulama Hata Mesajlarının Görsel Sunumu

Mobil cihazlarda, doğrulama hata mesajlarının ekranı kaplamaması, ancak açıkça görünür olması gerekir. Genellikle, hatalı giriş alanının hemen altında, küçük ve okunabilir bir yazı tipiyle mesajları göstermek en iyi yaklaşımdır. Ayrıca, hatalı alanları görsel olarak vurgulamak (örneğin, kırmızı bir çerçeveyle) kullanıcıların hatayı hızlıca fark etmesini sağlar.

FluentValidation, API'nizde veya MVC uygulamanızda standart ModelState mekanizmasını kullandığı için, istemci tarafında bu hataları yakalayıp uygun şekilde görüntülemek sizin front-end kodunuza kalmıştır. JavaScript ile bu hataları işleyip DOM'a yerleştirebilirsiniz.

İstemci Tarafı Doğrulama ve Kullanıcı Deneyimi

Sunucu tarafı doğrulama her zaman son güvenlik katmanı olsa da, istemci tarafı doğrulama (JavaScript ile), kullanıcıların hataları anında düzeltmesine olanak tanır ve gereksiz sunucu gidiş-dönüşlerini azaltır. Bu, özellikle mobil ağ bağlantılarının yavaş olabileceği durumlarda kullanıcı deneyimini önemli ölçüde iyileştirir.

FluentValidation.AspNetCore paketi, belirli FluentValidation kurallarını (NotEmpty, EmailAddress, Length gibi) HTML5 ve jQuery Validate gibi istemci tarafı doğrulama kütüphaneleriyle uyumlu adaptörler sağlar. Bu, sunucu tarafında tanımladığınız kuralların bir kısmının otomatik olarak istemci tarafında da çalışmasına olanak tanır.



Yukarıdaki HTML ve JavaScript referansları ile, FluentValidation'ın sunucu tarafı kurallarının bir kısmı istemci tarafında da çalışacaktır. Ancak, tüm karmaşık FluentValidation kuralları (örneğin, MustAsync ile veritabanı kontrolü veya özel regex'ler) istemci tarafında otomatik olarak desteklenmez. Bu gibi durumlarda, manuel JavaScript doğrulama yazmanız gerekebilir veya kullanıcıların bu tür hataları sunucuya gönderdikten sonra görmelerini bekleyebilirsiniz.

Duyarlı Tasarım ile Hata Mesajı Konumlandırma

Hata mesajlarının mobil cihazlarda düzgün görünmesi için CSS media query'lerini kullanarak farklı ekran boyutlarına göre stil ayarlamaları yapabilirsiniz. Örneğin, küçük ekranlarda hata mesajlarının daha az yer kaplamasını veya farklı bir konumda görünmesini sağlayabilirsiniz.


/* Genel hata mesajı stili */
.text-danger {
    color: #dc3545;
    font-size: 0.875em;
    margin-top: 0.25rem;
}

/* Küçük ekranlar için düzenlemeler */
@media (max-width: 768px) {
    .text-danger {
        font-size: 0.8em; /* Daha küçük font */
        position: relative; /* Pozisyonu ayarla */
        left: 0;
        right: 0;
        text-align: left; /* Metni sola hizala */
    }

    .form-group input.input-validation-error {
        border-color: #dc3545;
        padding-right: 2.25rem; /* Hata ikonu için yer aç */
        background-image: url("data:image/svg+xml,..."); /* Hata ikonu */
        background-repeat: no-repeat;
        background-position: right calc(0.375em + 0.1875rem) center;
        background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
    }
}

Bu CSS snippet'i, mobil ekran boyutları için hata mesajlarının font boyutunu ayarlıyor ve ayrıca hatalı giriş alanlarına özel bir hata ikonu ekleyerek görsel geri bildirimi güçlendiriyor. Bu tür duyarlı tasarım yaklaşımları, mobil kullanıcıların formları daha rahat doldurmasını ve hatalarla daha kolay başa çıkmasını sağlar. FluentValidation'ın gücünü, iyi tasarlanmış bir ön yüz ile birleştirerek, her cihazda sorunsuz bir kullanıcı deneyimi sunabilirsiniz.

Sonuç: FluentValidation ile Güvenli ve Güvenilir Uygulamalar

ASP.NET Core uygulamalarında veri doğrulama, uygulamanın güvenliği, veri bütünlüğü ve kullanıcı deneyimi açısından vazgeçilmez bir süreçtir. FluentValidation, bu süreci basitleştiren, okunabilirliğini ve sürdürülebilirliğini artıran güçlü ve esnek bir kütüphane olarak öne çıkar. Geleneksel Data Annotations'ın ötesine geçerek, doğrulama kurallarını ayrı bir katmanda tutma, akıcı API ile kolayca tanımlama, koşullu ve iç içe doğrulama gibi ileri düzey yetenekler sunar. Asenkron doğrulama ve özel kurallarla iş mantığınızı tam olarak yansıtma yeteneği, onu büyük ve karmaşık projeler için ideal bir çözüm haline getirir.

Bu makalede, FluentValidation'ın temel kavramlarından başlayarak, ASP.NET Core uygulamalarına nasıl entegre edileceğini, karmaşık senaryolar için ileri düzey tekniklerini, özel ve asenkron doğrulama mekanizmalarını ve son olarak gerçek dünya bir API projesinde kullanımını ve mobil uyumlu tasarım ipuçlarını ele aldık. FluentValidation'ı projelerinize dahil ederek, sadece geçerli verilerle çalışmanızı garanti altına almakla kalmaz, aynı zamanda daha temiz, test edilebilir ve bakımı kolay bir kod tabanı oluşturursunuz. Unutmayın, iyi bir doğrulama stratejisi, uygulamanızın kalitesinin ve güvenilirliğinin temelini oluşturur.

Sıkça Sorulan Sorular

Soru Cevap
FluentValidation'ı Data Annotations ile birlikte kullanabilir miyim? Evet, kullanabilirsiniz ancak genellikle önerilmez. Her iki doğrulama mekanizmasının aynı model üzerinde çalışması beklenmeyen davranışlara yol açabilir veya karmaşıklığı artırabilir. Genellikle projenin tamamında tek bir doğrulama yaklaşımı (FluentValidation veya Data Annotations) kullanmak daha iyidir. FluentValidation, AddFluentValidationAutoValidation() metodu ile ASP.NET Core'un varsayılan doğrulama sağlayıcısını değiştirdiği için, Data Annotations doğrulayıcıları devre dışı kalabilir veya öncelik sorunları yaşanabilir.
FluentValidation ile performans sorunları yaşar mıyım? Genel olarak hayır. FluentValidation oldukça optimize edilmiştir. Doğrulayıcılar bir kez oluşturulur ve tekrar tekrar kullanılır. Ancak, aşırı karmaşık özel kurallar veya her istekte çok sayıda I/O işlemi (örneğin veritabanı çağrıları) gerektiren MustAsync kullanımları performansı etkileyebilir. Bu tür durumlarda, doğrulama mantığınızı optimize etmek ve mümkünse önbelleğe alma stratejilerini kullanmak önemlidir.
Farklı DTO'lar için aynı doğrulayıcıyı kullanabilir miyim? Hayır, her AbstractValidator sınıfı belirli bir T tipi için özelleştirilmiştir. Farklı DTO'lar için farklı doğrulayıcılar tanımlamanız gerekir. Ancak, ortak doğrulama mantığını yeniden kullanılabilir uzantı metotları (CustomValidatorExtensions örneğindeki gibi) veya ortak bir soyut doğrulayıcı sınıfı oluşturarak paylaşabilirsiniz. Böylece kod tekrarını en aza indirirsiniz.
Hata mesajlarını kullanıcı arayüzünde nasıl gösterebilirim? API projelerinde, FluentValidation hataları standart ModelState mekanizması aracılığıyla JSON formatında döndürülür (400 Bad Request ile). İstemci tarafında (örneğin JavaScript ile), bu JSON yanıtını ayrıştırarak hataları ilgili form alanlarının yanında veya bir özet listesi halinde görüntüleyebilirsiniz. MVC projelerinde ise ve

gibi etiket yardımcıları (tag helpers) kullanılarak hatalar otomatik olarak işlenebilir.
FluentValidation ile client-side doğrulama yapabilir miyim? Evet, FluentValidation.AspNetCore paketi, bazı yerleşik FluentValidation kuralları için istemci tarafı (unobtrusive) doğrulama adaptörleri sağlar. AddFluentValidationClientsideAdapters() metodunu eklemeniz ve jQuery Validation Unobtrusive JavaScript kütüphanesini ön yüzde kullanmanız gerekir. Ancak, tüm sunucu tarafı FluentValidation kuralları (özellikle özel veya asenkron olanlar) otomatik olarak istemci tarafına aktarılamaz; bu tür durumlar için manuel JavaScript doğrulama gerekebilir.
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