Gereksinimler ve Ön Hazırlık
Uygulamalarınızı geliştirmek için .NET 9 veya üzeri bir SDK sürümüne sahip olmanız gerekmektedir. Proje yapınızda veri doğrulama işlemlerini standartlaştırmak için en yaygın ve güvenilir kütüphane olan FluentValidation kullanacağız.
- .NET SDK 9.0+
- Visual Studio 2022 veya JetBrains Rider
- FluentValidation NuGet paketi
- Temel düzeyde Dependency Injection (Bağımlılık Enjeksiyonu) bilgisi
Projenize FluentValidation kütüphanesini eklemek için terminal üzerinden şu komutu çalıştırın:
dotnet add package FluentValidation.DependencyInjectionExtensions
Bu komut, kütüphaneyi projenize dahil eder ve .NET'in yerleşik Dependency Injection yapısıyla entegre çalışmasını sağlar.
Katmanlı Mimaride Doğrulama Mantığı
Çok katmanlı mimaride veri doğrulama, verinin "geçerli" olup olmadığını kontrol etmekten öte, iş kurallarının (business rules) uygulanmasıdır. Veri doğrulama işlemini üç ana başlıkta ele alabiliriz:
| Doğrulama Türü | Avantajı | Dezavantajı |
|---|---|---|
| Data Annotation (Model) | Hızlı ve basit uygulama. | İş mantığı ile UI katmanını kirletir. |
| FluentValidation (Service) | Test edilebilir, temiz kod (Clean Code). | Ekstra kütüphane bağımlılığı. |
| Veritabanı Kısıtlamaları | En son güvenlik katmanı. | Hata mesajlarını yönetmek zordur. |
Adım Adım FluentValidation ile Doğrulama Kuralları Yazma
Öncelikle, iş mantığı katmanımızda bir DTO (Data Transfer Object) tanımlayalım. Bu nesne, kullanıcıdan gelen veriyi taşımak için kullanılacaktır. Ardından bu nesne için bir doğrulayıcı (Validator) sınıfı oluşturacağız.
public class UserRegisterDto
{
public string Username { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
Yukarıdaki sınıf, kullanıcının sisteme kayıt olması için gereken temel alanları içerir. Şimdi bu alanlar için kurallarımızı yazalım.
using FluentValidation;
public class UserRegisterValidator : AbstractValidator
{
public UserRegisterValidator()
{
RuleFor(x => x.Username).NotEmpty().WithMessage("Kullanıcı adı boş geçilemez.");
RuleFor(x => x.Email).EmailAddress().WithMessage("Geçerli bir e-posta adresi giriniz.");
RuleFor(x => x.Age).InclusiveBetween(18, 99).WithMessage("Yaş 18 ile 99 arasında olmalıdır.");
}
}
Bu validator sınıfı, AbstractValidator sınıfından türetilir. RuleFor metodu ile ilgili alanın kurallarını belirler, WithMessage ile ise kullanıcıya dönecek hatayı özelleştiririz.
Servis Katmanında Doğrulamayı Entegre Etme
Doğrulama kurallarını yazdık, şimdi bu kuralları servis katmanında (Business Logic Layer) nasıl tetikleyeceğimizi görelim. Dependency Injection kullanarak validator sınıfımızı servisimize enjekte ediyoruz.
public class UserService
{
private readonly IValidator _validator;
public UserService(IValidator validator)
{
_validator = validator;
}
public void RegisterUser(UserRegisterDto userDto)
{
var result = _validator.Validate(userDto);
if (!result.IsValid)
{
throw new ValidationException(result.Errors);
}
// Veritabanı işlemleri burada devam eder
}
}
Bu yapı sayesinde, iş mantığınız (Business Logic) verinin doğruluğundan emin olur. Eğer doğrulama başarısız olursa, bir ValidationException fırlatılarak işlem durdurulur.
Kritik Güvenlik Uyarısı: Veri doğrulama asla tek başına bir güvenlik önlemi değildir. Kullanıcıdan gelen veriler her zaman "kirli" kabul edilmeli ve veritabanına yazılmadan önce parametreli sorgular (Entity Framework Core kullanımı ile otomatik sağlanır) ile SQL Injection saldırılarına karşı korunmalıdır.
Global Hata Yönetimi (Middleware)
Her servis içinde try-catch blokları ile uğraşmak yerine, ASP.NET Core üzerinde bir Middleware kullanarak tüm validasyon hatalarını merkezi bir noktada yakalayabiliriz. Bu, kodunuzun temiz kalmasını sağlar.
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
public ExceptionHandlingMiddleware(RequestDelegate next) => _next = next;
public async Task Invoke(HttpContext context)
{
try { await _next(context); }
catch (ValidationException ex)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsJsonAsync(ex.Errors);
}
}
}
Bu Middleware, uygulama genelinde fırlatılan tüm doğrulama hatalarını yakalar ve istemciye 400 (Bad Request) koduyla birlikte hata detaylarını JSON formatında döner.
Sıkça Sorulan Sorular
FluentValidation neden Data Annotation'dan daha iyidir?
FluentValidation, doğrulama mantığını model sınıflarından ayırarak "Single Responsibility Principle" (Tek Sorumluluk Prensibi) ilkesine uyar. Ayrıca karmaşık ve koşullu doğrulama kurallarını yazmak çok daha kolaydır.
Doğrulama hatalarını kullanıcıya nasıl göstermeliyim?
Hataları bir API üzerinden dönüyorsanız, yukarıdaki Middleware örneğinde olduğu gibi bir JSON nesnesi olarak dönmeli ve ön yüz (Frontend) tarafında bu mesajları input alanlarının altında göstermelisiniz.
Veritabanı ile ilişkili doğrulama yapabilir miyim?
Evet, validator sınıflarınıza veritabanı bağlamını (DbContext) enjekte ederek, örneğin "Bu e-posta adresi zaten kayıtlı mı?" gibi veritabanı sorgusu gerektiren kontrolleri kolayca yapabilirsiniz.
Performans kaybı yaşar mıyım?
FluentValidation oldukça optimize edilmiş bir kütüphanedir. Doğru konfigüre edildiğinde, uygulamanın genel performansına etkisi ihmal edilebilir düzeydedir.
Çok katmanlı mimaride doğrulama nerede yapılmalıdır?
Doğrulama, verinin iş mantığı katmanına girdiği ilk noktada (Service Layer) yapılmalıdır. Bu, verinin sistemin derinliklerine hatalı girmesini engeller.
İleri Seviye Senaryo: Karmaşık Nesne Hiyerarşilerinde Doğrulama
Gerçek dünya projelerinde veriler genellikle iç içe geçmiş (nested) nesnelerden oluşur. Örneğin, bir Order (Sipariş) nesnesi içerisinde birden fazla OrderItem (Sipariş Kalemi) bulunabilir. FluentValidation, SetValidator metodunu kullanarak bu tür hiyerarşik yapıları kolayca yönetmenize olanak tanır.
Aşağıdaki örnekte, bir siparişin içindeki her bir ürün kaleminin de kendi doğrulama kurallarına sahip olduğu bir senaryoyu görebilirsiniz:
public class OrderValidator : AbstractValidator
{
public OrderValidator()
{
RuleFor(x => x.CustomerName).NotEmpty();
// Koleksiyon içindeki her bir eleman için ayrı validator tanımlıyoruz
RuleForEach(x => x.Items).SetValidator(new OrderItemValidator());
}
}
public class OrderItemValidator : AbstractValidator
{
public OrderItemValidator()
{
RuleFor(x => x.ProductName).NotEmpty().MaximumLength(100);
RuleFor(x => x.Quantity).GreaterThan(0);
}
}
Bu yaklaşım, doğrulama mantığınızı modüler hale getirir. OrderItemValidator sınıfını sadece siparişlerde değil, stok yönetimi veya fatura oluşturma gibi farklı süreçlerde de yeniden kullanabilirsiniz.
Asenkron Doğrulama Kuralları ve Veritabanı Entegrasyonu
Bazı durumlarda doğrulama işlemi için veritabanına erişmeniz gerekebilir; örneğin, bir kullanıcı kaydı sırasında e-posta adresinin sistemde zaten var olup olmadığını kontrol etmek gibi. FluentValidation, MustAsync metodu ile bu tür asenkron işlemleri destekler.
Dikkat: Asenkron doğrulama kuralları çalıştırılırken, doğrulama işlemini tetikleyen metodun da await anahtar kelimesi ile çağrılması gerektiğini unutmayın.
public class UserRegistrationValidator : AbstractValidator
{
private readonly IUserRepository _userRepository;
public UserRegistrationValidator(IUserRepository userRepository)
{
_userRepository = userRepository;
RuleFor(x => x.Email)
.NotEmpty()
.EmailAddress()
.MustAsync(async (email, cancellation) =>
{
var exists = await _userRepository.GetByEmailAsync(email);
return exists == null; // Eğer null ise e-posta benzersizdir
})
.WithMessage("Bu e-posta adresi zaten kullanımda.");
}
}
Bu yöntem, veritabanı sorgularını doğrulama katmanına dahil etmenize olanak tanır. Ancak, performans kaybını önlemek adına veritabanı sorgularının verimli olduğundan (index kullanımı vb.) emin olmalısınız. Çok sık yapılan doğrulama işlemlerinde, veritabanı yerine önbellek (Redis gibi) mekanizmalarını kullanmak sistemin yanıt süresini önemli ölçüde iyileştirecektir.
Doğrulama Stratejileri İçin İpuçları
- Kısa Devre (CascadeMode):
CascadeMode.Stopkullanarak, bir kural başarısız olduğunda o alan için diğer kuralların çalışmasını engelleyebilirsiniz. Bu, gereksiz işlem yükünü azaltır. - Özel Hata Kodları:
WithErrorCode("USER_001")metodunu kullanarak, ön yüz tarafında hata türüne göre dinamik mesajlar veya yönlendirmeler yapabilirsiniz. - Bağımlılık Enjeksiyonu: Validator sınıflarınızı
IServiceCollectioniçerisineAddValidatorsFromAssemblyContainingmetodu ile toplu bir şekilde kaydederek kod tekrarından kurtulun.
Sonuç
C# ile çok katmanlı mimaride veri doğrulama yapmak, uygulamanızın kalitesini ve güvenliğini doğrudan etkileyen bir süreçtir. FluentValidation kullanarak doğrulama mantığını iş katmanından ayırmak, projenizin test edilebilirliğini artırır. Bu rehberde öğrendiğiniz yöntemleri, kendi projelerinizde uygulayarak daha profesyonel ve hatasız kodlar yazabilirsiniz.
Sorumluluk Reddi: Bu makalede paylaşılan kod örnekleri eğitim amaçlıdır. Üretim ortamında (Production) kullanmadan önce, verilerinizi her zaman ek güvenlik katmanlarından (input sanitization, authorization) geçirdiğinizden emin olun. Kod güvenliği konusunda en güncel OWASP standartlarını takip etmeniz önerilir.
Bir sonraki adım olarak, bu yapıya "FluentValidation" ile asenkron (async) doğrulama kurallarının nasıl ekleneceğini ve karmaşık nesne hiyerarşilerinde doğrulamanın nasıl yönetileceğini araştırmanızı öneririm.


Yorumlar (0)
Yorum Yaz