Yazılım geliştirme süreçlerinde şüphesiz en önemli kıymet veridir. Verinin doğru şekilde saklanması için öncelikle doğru olarak alınması gereklidir. Bir girdi işlenirken çeşitli kontroller yapılır. Kontrollerin ardından veri istenilen kriterlerde ise verinin işlenmesine geçilir ve nihayet veri kaydedilerek süreç tamamlanır.

Üye kaydı yaptığımızı varsayarak Register isimli bir metod hazırlayalım ve yaptığımız kontrolleri görelim.

public IActionResult Register(UserInfoDto input)
{
var Errors = new List();
if (!string.IsNullOrEmpty(input.Ad))
{
Errors.Add("Ad Boş olamaz!");
}
if (!string.IsNullOrEmpty(input.Soyad))
{
Errors.Add("Soyad Boş olamaz!");
}
if (!string.IsNullOrEmpty(input.TcKimlikNo))
{
Errors.Add("TcKimlikNo Boş olamaz!");
}
if (input.Yas < 18)
{
Errors.Add("Yaş 18'den küçük olamaz!");
}
//diğer kodlar
return View(Errors);
}

Görüldüğü üzere 4 adet input’umuzu valide etmek için için 4 kontrol yapıldı. Register metodunun içinde yapılan bu kontroller sonrasında girdi ile ilgili işlemler devam edecektir.

Aspect Oriented Programming yaklaşımında sıkça yapılan ve aslında iş kavramını (business) etkilemeyen kontrol ve yapıların süreçlerin dışına taşınması söz konusudur.

Asp.Net Core bize Action Filter aracılığıyla attribute’ler eklememizi sağlamaktadır. Bu attribute’ler netcore’un entegre mimarisi ile aspect programlama yapma imkanı sunmaktadır. Merak edenler için Net Framework’de de benzer yapılar söz konusudur.

Validasyon işlemini Attribute düzeyine aldığımızda kodumuzun nasıl göründüğüne bir bakalım.

[MyValidation(typeof(UserInfoValidator))]
public IActionResult Register(UserInfoDto input)
{
return View();
}

Az önce 20 satır olan Register metodu 4 satıra indirgenmiş durumda ve içi oldukça temiz. İçerisinde başka işlemler yapılabilir. Üst kısmında MyValidation isimli attribute’umuz ile validasyon yapmaktayız. UserInfoValidator olarak verdiğimiz tip ise Abstract bir sınıfdır. Birazdan örnekte detaylı olarak vereceğim.

Öncelikle kullanacağımız FluentValidation isimli popüler validasyon paketini projemize indirelim. Farklı ürünleri de kullanabilirsiniz.

Sonrasında validasyon işlemimizi yapacak olan Helper sınıfını oluşturacağız

public static class ValidationHelper
    {       
        /// <summary>
        /// Inline veya Attribute üzerinden validasyon kontrolü için kullanılır.
        /// </summary>
        /// <param name="type">Validator tipi</param>
        /// <param name="items">Valide edilecek nesneler (Array)</param>
        public static void Validate(Type type, object[] items)
        {
            //verilen tip ile validator oluşturma durumu kontrol ediliyor.
            if (!typeof(IValidator).IsAssignableFrom(type))
                throw new Exception("Hata: Validator tipi geçersiz!");

            var validator = (IValidator)Activator.CreateInstance(type);

            //gelen tüm argumanlar için validasyon yapılacak.
            foreach (var item in items)
            {
                //arguman tipi valide edilebiliyor mu bakılıyor.
                if (validator.CanValidateInstancesOfType(item.GetType()))
                {
                    var result = validator.Validate(new ValidationContext<object>(item));

                    //eğer valid durumda değilse hatalar dönülür.
                    //valid ise void metod işletilir ve çıkılır.
                    if (!result.IsValid)
                        throw new ValidationException(result.Errors);
                }
            }
        }

        /// <summary>
        /// Inline olarak validasyon işlemi yapar.
        /// </summary>
        /// <param name="type">Validator tipi</param>
        /// <param name="item">Valide edilecek nesne</param>
        /// <returns>ValidationResult tipinde Validasyon sonucu dönüşünü sağlar</returns>
        public static ValidationResult Validate(Type type, Object item)
        {
            //verilen tip ile validator oluşturma durumu kontrol ediliyor.
            if (!typeof(IValidator).IsAssignableFrom(type))
                throw new Exception("Hata: Validator tipi geçersiz!");

            var validator = (IValidator)Activator.CreateInstance(type);

            //valid veya valid olmama durumunun tamamı result olarak dönülür.
            return validator.Validate(new ValidationContext<object>(item));
        }
    }
}


MyValidation Attribute’umuzu hazırlayalım.

[AttributeUsage(AttributeTargets.Method)]
   public class MyValidationAttribute : ActionFilterAttribute
   {
       private Type validatorType;

       public MyValidationAttribute(Type ValidatorType)
       {
           validatorType = ValidatorType;
       }

       public override void OnActionExecuting(ActionExecutingContext context)
       {
           ValidationHelper.Validate(validatorType, context.ActionArguments.Values.ToArray());

           base.OnActionExecuting(context);
       }
   }

Herşey hazır gibi görünüyor. Ancak birşey eksik. Abstract validator sınıfı gerekmekte. Örnek verecek olursak UserInfoDto için UserInfoDtoValidator, LoginInfoDto için LoginInfoDtoValidator gibi olabilir.

public class UserInfoValidator : AbstractValidator<UserInfoDto>
   {
       public UserInfoValidator()
       {
           RuleFor(p => p.Ad).NotEmpty().WithMessage("Ad boş olamaz!");
           RuleFor(p => p.Soyad).NotEmpty().WithMessage("Soyad boş olamaz!");
           RuleFor(p => p.TcKimlikNo).NotEmpty().WithMessage("TcKimlikNo boş olamaz!");
           RuleFor(p => p.Yas).GreaterThanOrEqualTo(18).WithMessage("Yaş 18'den büyük olmalıdır!");
       }
   }

Ekleyeceğimiz validatorler AbstractValidator<T> abstract sınıfından türemelidir. T  sınıfı girdi olacak sınıfı ifade ediyor. Örnekte UserInfoDto

Validator içinde çeşitli kontroller konulmalıdır. Kontroller RuleFor deyimi ile başlar. RuleFor deyimi içerisinde expresson alır ve ilgili sınıfın propertyleri işaret edilir.

RuleFor(p=>p.Ad) Ad propertysini seçmektedir. Sonrasında Seçilen propertynin nasıl bir kontrole tabi olduğu metod ile belirtilir. NotEmpty() metodu propertynin boş olamayacağını bildirir. Bunun gibi birçok metod vardır. Kullanım sırasında görebilirsiniz. Sonrasında WithMessage metodu ile propertynin uyumsuz olması durumunda verilecek mesaj belirtilir. Eğer WithMessage  kullanılmaz ise default mesaj verilecektir.

Farklı olarak GreaterThanOrEqualTo(18) metodu, seçilen propertynin 18 değerinden eşit veya büyük olması gerektiğini ifade etmektedir.

Sonuç olarak Register metodu işletilmeden önce UserInfoValidator işletilir ve verilerde eksik veya hata var ise kullanıcıya dönülür.

Eğer Api projesi hazırlıyorsak hataları Middleware ile istediğimiz formatta dönebiliriz.

Mvc projesi hazırlıyorsak hataları List olarak dönebiliriz. O da alttaki örnekte verilmiştir. Mvc projesinde ilgili View’a dönüş söz konusu olduğu için validasyonu inline yapmak bir çözüm olabilir. Ancak attribute’ü değiştirerek yine aspect programlama prensibine dayalı olarak yapacağız.

Inline validasyon etmek istersek alt kısımdaki gibi olabilir.

public IActionResult Register(UserInfoDto input)
{
    var result = ValidationHelper.Validate(typeof(UserInfoValidator), input);
    return View(result.Errors);
}

MyValidationAttribute tekrar düzenlenerek MVC Razor sayfası için hazırlandı. Hata var ise ModelState’e eklenmektedir.

[AttributeUsage(AttributeTargets.Method)]
   public class MyValidationAttribute : ActionFilterAttribute
   {
       private Type validatorType;

       public MyValidationAttribute(Type ValidatorType)
       {
           validatorType = ValidatorType;
       }

       public override void OnActionExecuting(ActionExecutingContext context)
       {
           var objectList = context.ActionArguments.ToArray();
           if (objectList.Length > 0)
           {
               var result = ValidationHelper.Validate(validatorType, objectList[0].Value);

               if (!result.IsValid)
               {
                   foreach (var item in result.Errors)
                   {
                       context.ModelState.AddModelError(item.PropertyName, item.ErrorMessage);
                   }
               }
           }

           base.OnActionExecuting(context);
       }
   }

Alınan hataları web sayfasında görelim:

<div>Hatalar:</div>
@foreach (var modelState in ViewData.ModelState.Values) {
    foreach (var error in modelState.Errors) {
        <div>@error.ErrorMessage</div>
    }
}

Hataları ekranda görelim:

Sağlıkla kalın…

Kategoriler: CSharp

2 yorum

Ömer · 27 Mart 2022 00:42 tarihinde

Elinize sağlık çok güzel bir yazı olmuş.Başarılarınızın devamını diliyorum.

    Abdurrahman · 27 Mart 2022 22:37 tarihinde

    Teşekkür ederim zaman ayırıp okuduğunuz için..

Ömer için bir yanıt yazın Yanıtı iptal et

Avatar placeholder

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

*