Form Helper ve Fluent Validation Kullanarak ASP.NET Core Validation İşlemleri

.NET Framework üzerinde çalışan Fluent Validation makalesinden sonra bu kez de daha kapsamlı ASP.NET Core üzerinde Fluent Validation kullanımını inceliyor olacağız. Aynı zamanda hazırlayacağımız html formumuzdan alacağımız verileri sunucuya göndermek ve FluentValidation’a client-side desteği kazandırabilmek için Form Helper kütüphanesinden faydalanacağız.

Not: Yazı boyunca istemci tarafı (kullanıcı tarayıcısı) client-side, sunucu tarafı ise server-side olarak adlandırılacaktır. İş kuralları ve verilerin doğruluğunun sağlanması içinse Validation ifadesi kullanılacaktır.

 

Neden Validation’a ihtiyaç duyuyoruz?

İster web ister mobil bir uygulama yazalım, geliştirdiğimiz uygulama yüzlerce hatta binlerce kişi tarafından kullanılabilir. Kullanıcılardan aldığımız verilerin doğruluğu yazılımımızın düzgün işleyebilmesi için önemli. Ayrıca uygulamamızı kullanan hedef kitle içerisinde kötü niyetli kişilerin de olabileceğini düşünürsek aldığımız verilerin doğruluğu büyük önem arz ediyor.

javascript kapatmak

Web tarafında jQuery Validation veya alternatif client-side çalışan javascript kütüphaneleri ile bu sorunun önüne kısmen geçsek dahi bu doğrulamalar Javascript ile yapıldığından tam güvenlik sağlayamazlar. (Mobil taraf içinde durum pek farklı değildir.) Uygulamamıza dışarıdan  (get-post vb.) talep gelebileceğinden veya internet tarayıcısı üzerinde kolaylıkla Javascript kapatılabileceğinden client-side  yapılan hiç bir doğrulama yöntemine güvenemeyiz.

 

Örnek içerisinde kullanacağımız kütüphaneleri kısaca inceleyelim.

 

Neden Fluent Validation?

Server-side doğrulama işlemini ASP.NET Core ile birlikte gelen (built-in) “Data Annotations” lar veya kendi yazacağımız attributeler ile de yapabiliriz. Ya da GitHub üzerinde bulunan farklı validation kütüphanelerini de tercih edebiliriz. Ancak Fluent Validation, basit fluent yazım tarzı ve kompleks validationlar için kullanışlı olması nedeniyle en çok tercih edilen .NET validation kütüphanesi olarak öne çıkıyor. Nuget üzerinde ise 14 Milyon civarı indirilme sayısına ve GitHub üzerinde 4000+ yıldıza sahip. Fluent Validation ile server-side validationlar yapılabildiği gibi kısmi olarak (required, maxlength vb.) client-side validationlar da yapılabilmektedir. Client-side desteği için jQuery Validation ve jQuery Validation Unobtrusive kütüphanelerine ihtiyaç duymaktadır.

https://github.com/JeremySkinner/FluentValidation

 

Not: Fluent Validation kütüphanesi Data Annotations alternatifi olmayıp  birlikte uyum içerisinde çalışmaktadır. Örneğin propertyler için verilen DisplayName işlemi yine Data Annotations üzerinden yapılmaktadır.

 

Neden Form Helper?

Form Helper, ASP.NET Core üzerinde javascript kodu yazmamıza gerek kalmadan ajax destekli formlar oluşturmamızı sağlayan bir kütüphane. Yine benzer şekilde server-side olarak yazılan validationları javascript kodu yazmadan client-side’a taşıyabiliyor. İçerisinde gelen hazır methodlar ile Controllerlardan dönülen yanıt ile ekranda notification gösterip, form kaydedildiğinde başka bir url’e redirect (yönlendirme) de yapabiliyor.

Örneğimizde Fluent Validation ile oluşturulan server-side kompleks validationları basit bir şekilde client-side’a taşıyacağız.

https://github.com/SinanBozkus/FormHelper

 

Yerli bir kütüphane olan FormHelper‘ı GitHub üzerinde destekleyebilir ve katkıda bulunabilirsiniz.

Not: Fluent Validation kütüphanesi server-side yani backend tarafta validation işlemlerini yapmaktadır. FluentValidation.AspNetCore kütüphanesi ASP.NET Core’a destek verse de required, maxlength gibi basit validationlar dışında tüm validationları server-side yapmaktadır. Varsayılan kullanımda bunların client-side’a yansıtılması için tüm sayfanın post olması ve tekrar üretilmesi gerekmektedir.

 

Örneğimiz için senaryomuz bir öğrenci kaydı alıp, daha sonra güncellemesi yapacak şekilde olacaktır. Aynı model için kayıt ve güncelleme esnasında farklı doğrulama kuralları çalıştırarak ve kompleks validationlar yaparak Fluent Validation’ın kullanışlı özelliklerinden de faydalanacağız. Fluent Validation tarafında oluşan validationları Form Helper kütüphanesi yardımıyla client-side’a aktaracağız. Kayıt işlemi sonrasında gösterilecek bildirim ve yönlendirme işlemleri için yine Form Helper ile birlikte gelen hazır methodlardan faydalanacağız.

Yeni bir ASP.NET Core projesi (MVC Template) oluşturarak Nuget Package Manager üzerinden Fluent Validation ve Form Helper kütüphanelerini kuralım.

 

 

“Startup.cs” içerisine gelip kütüphanelerin çalışması için gerekli olan servisleri kayıt edelim. “Configure Services” methodu altında bulunan “services.AddMvc()” bölümünü bulup aşağıdaki gibi değiştiriyoruz.

Gerekli Namespaceler: (Uygulamamızın ilerleyen bölümlerinde de bu namespacelere ihtiyaç duyulacaktır.)


using FormHelper;
using FluentValidation.AspNetCore;


services.AddFormHelper(new FormHelperConfiguration
{
CheckTheFormFieldsMessage = "Form alanlarını kontrol ediniz.";
});

services.AddMvc()
.AddFluentValidation();

 

“CheckTheFormFields” ataması uyarı mesajımızın Türkçe gelmesini sağlayacaktır, FormHelper kütüphanesinde varsayılan dil İngilizcedir. Diğer gelecek hata/uyarı mesajları uygulamamızın diline göre Fluent Validation tarafından üretilecektir.

 

Kullandığımız kütüphaneler client-side çalışacağından bazı javascript kütüphanelerine (jquery form, jquery validation unobstrative ve toastr) ihtiyaç duyuyor. Bunları “Views/Shared” klasörü altında bulunan  “_Layout.cstml” imize ekliyor ve devam ediyoruz.

 


<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.form/4.2.2/jquery.form.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>

 

Models klasörü içerisine “StudentViewModel” adında bir class oluşturuyor ve aşağıdaki propertyleri ekliyoruz.

public int Id { get; set; }
public string StudentName { get; set; }
public string Email { get; set; }
public string StudentNumber { get; set; }
public int Credit { get; set; }
public bool Graduated { get; set; }

public bool IsNew => Id == default;

Not: IsNew, modelimizi view ve controller içerisinde kullanırken yeni kayıt mı, yoksa güncelleme mi yapılacağının bilgisini  iletecektir. Bunu yaparken Id değeri eğer integer default değerine yani sıfıra eşit ise bize true değilse false dönecektir. Profesyonel kullanımda bunu BaseViewModel içerisinde tanımlamak daha doğru olacaktır.

 

 

Oluşturduğumuz bu ViewModel için bir Validator oluşturalım ve formumuzda kaydet butonuna basıldığında hangi kontrollerin yapılacağıyla ilgili validationları yazalım.

Uygulamamızın ana dizininde “Validators” adında bir klasör ve içeriside “StudentViewModelValidator” adında bir class oluşturuyoruz. Bu class içerisinde Fluent Validation’dan faydalanarak bir validator yazacağız. Bu yüzden ilk iş namepacelerimize “using FluentValidation;” ekliyoruz.

Not: Klasör ve Validator isimleri varsayılan kullanımlara göre verilmiştir. Farklı bir isimlendirme de kullanabilirsiniz.

Classımızın Fluent Validation’dan gelen “AbstractValidator” sınıfından miras alması gerekiyor. Bu class generic yapıda ve içerisine hangi class için validator hazırlamak istiyorsak onu göndermemiz gerekiyor. Bu yüzden classımız “AbstractValidator<StudentViewModel>” den miras alacaktır.

 

Doğrulama senaryomuz şu şekilde olacak:

  • StudentName (öğrenci adı) alanı boş olamaz ve en az 4, en fazla 50 karakter olabilir.
  • Email (e-posta) alanı boş olabilir ancak doluysa e-posta formatına uymak zorunda.
  • StudentNumber (öğrenci no) alanı “A”, “B”, “C” harflerinden biriyle başlamalı ve en az 6 en fazla 8 karakter olmalıdır.
  • Credit (öğrenci notu/puanı) boş olabilir ancak doluysa 0 ile 100 arasında bir sayı olabilir.
  • Graduated (mezun olma durumu) seçili ise öğrenci puanı 70 üzerinde olmalıdır, aksi halde mezun olamaz.

Kurallarımızı yazabilmek için class içerisinde bir constructor oluşturuyor ve içerisine yukarıdaki senaryoya göre validationları tanımlıyoruz.

RuleFor(x => x.StudentName)
.NotEmpty()
.Length(4, 50);

RuleFor(x => x.Email)
.EmailAddress();

RuleFor(x => x.StudentNumber)
.NotEmpty()
.Length(6, 8)
.Custom((studentNumber, context) =>
{
var arr = new[] { "A", "B", "C" };

if (!arr.Contains(studentNumber.Substring(0, 1)))
{
context.AddFailure("Student number must start with 'A', 'B' or 'C'.");
}
});

RuleFor(x => x.Credit)
.InclusiveBetween(0, 100)
.GreaterThanOrEqualTo(70).When(x => x.Graduated == true);

Class’ımızın son hali aşağıdaki gibi görünecektir.

 

Fluent Validation ile yazabileceğimiz kurallar neredeyse sınırsız. İşin tadını kaçırıp veritabanından query dahi çekebiliriz. :)

Şimdi oluşturduğumuz bu validator’ı “Startup.cs” üzerinde kayıt etmemiz gerekiyor. “Startup” classımız içerisinde bulunan “ConfigureServices” methoduna aşağıdaki satırı ekliyoruz.

services.AddTransient<IValidator<StudentViewModel>, StudentViewModelValidator>();

Not: Örnekte Controller ismi olarak “HomeController” kullanılacaktır. İstediğiniz isimde oluşturabilirsiniz.

Server-side işlerimizi hallettikten sonra client-side işlemlere geçebiliriz. Controller’ımızın içerisinde “Form” adında bir action oluşturuyoruz. Bu action’ı yeni kayıt ve düzenleme işlemlerinde formumuzu ekranda göstermesi için kullanacağız. Eğer url’imize bir ID geliyorsa güncelleme amacıyla, ID gelmiyorsa yeni bir kayıt oluşturmak amacıyla gösterilecektir.

Not: HasValue, Id != null ile aynı kullanıma sahiptir.


public IActionResult Form(int? id)
{
if (!id.HasValue)
{
return View(new StudentViewModel());
}
else
{
// Edit Mode
// You can select current record from database.
return Content("not implemented.");
}
}

 

Not: Makalenin çok uzamaması için “Edit” bölümünü boş bırakıyoruz. Uygulamanızın gereksinimlerine göre kendiniz doldurabilirsiniz.

 

Not: HasValue ile ID değerinin null olup olmadığı kontrol ediliyor. Null ise yeni bir form açılacak değilse ilgili ID değerine sahip veritabanından çekilip model döndürülebilir.

Viewimizi oluşturmaya geçmeden önce “_ViewImports” dosyamıza “@using FormHelper” ekliyoruz ve FormHelper’ı projemizdeki tüm viewlerimiz için kullanılabilir hale getiriyoruz.

Şimdi “Form.cshtml” viewimizi oluşturuyoruz. İçeriği aşağıdaki gibi olacaktır.


@model StudentViewModel

@{
var formConfig = new FormConfig(ViewContext)
{
FormId = "StudentForm",
FormTitle = Model.IsNew ? "New Student" : "Edit Student"
};
}
<div class="container">
<div class="row justify-content-center">
<div class="col col-lg-8">
<div class="card">
<div class="card-header">@formConfig.FormTitle</div>
<div class="card-body"><form id="@formConfig.FormId"><input type="hidden" />
<div class="form-group"><label></label>
<input class="form-control" type="text" /></div>
<div class="form-group"><label></label>
<input class="form-control" type="email" /></div>
<div class="form-group"><label></label>
<input class="form-control" type="text" /></div>
<div class="form-group"><label></label>
<input class="form-control" type="text" /></div>
<div class="form-group"><label></label>
<input type="checkbox" /></div>

<hr />

<div class="form-group text-right"><button class="btn btn-secondary" type="reset">Reset</button>
<button class="btn btn-primary" type="submit">Save</button></div>
</form></div>
</div>
</div>
</div>
</div>
@section Scripts {
@await Html.RenderFormScript(formConfig)
}

Burada bilmemiz gereken iki önemli kısım var. Birincisi formumuzun tanımlarını barındıracak olan FormConfig classıdır. Yeni bir FormConfig oluştururken içerisinde ViewContext göndermemiz gerekli. ViewContext varsayılan olarak View üzerinde bulunur.


Html formumuzu oluştururken formumuza “ID” tanımlamayı unutmamamız gerekiyor.

Son olarak FormHelper’ın bizim için formumuza ait javascript kodlarını üretebilmesi için “RenderFormScript” helper methodunu sayfamıza ekliyoruz.

View tarafında yapmamız gereken tüm işlemler tamamlandı. Şimdi Controller’ımıza gidip “Save” Action’ını oluşturup “[FormValidator]” attribute’ünü tanımlıyoruz.


[HttpPost, FormValidator]
public IActionResult Save(StudentViewModel viewModel)
{
// You can write your own code.
return View("Index");

}

Uygulamamızı çalıştırıyoruz. “Save” butonuna bastığımızda önce client-side validationlar çalışıyor. Herhangi bir hata alınmazsa ajax ile server-side’a gidiyor, burada bir hata yakalanırsa bunları client-side’a yansıtıyor. Tek satır javascript kod yazmadık ve kompleks  validationları bir kaç satırda hallettik. Her şey mükemmel!

Eğer ki validation işlemi sorunsuz olarak gerçekleştiyse, istersek “Save” action’ında tekrar servise gidip aynı kaydın veritabanında olup olmadığı gibi kontroller gerçekleştirebilir ve ekrana yine bir uyarı mesajı döndürülebilir. Gelen istekte hiç bir sorun yoksa ekrana olumlu bir mesaj döndürülebilir ve mesaj sonrasında ana listeye yönlendirilmesini sağlanabilir. Form Helper bu gibi işlemlerin tamamı için hazır methodlar barındırıyor.

Hata mesajı göstermek için:


return FormResult.CreateErrorResult("Bir hata oluştu.");

Uyarı mesajı göstermek için:


return FormResult.CreateWarningResult("Aynı isimde bir öğrenci kayıtlıdır.");

Bilgi mesajı göstermek için:


return FormResult.CreateInfoResult("Öğrenci kaydı tamamlandıktan sonra öğrenciye sözel bilgi vermeyi unutmayınız.");

Başarılı bir mesaj göstermek ve daha sonra listeye yönlendirmek için:


return FormResult.CreateSuccessResult("İşlem başarılı, listeye yönlenlendiriliyorsunuz...", Url.Action("Index", "Home"));

 

Proje kaynak kodlarına github üzerinden ulaşabilirsiniz.

https://github.com/sinanbozkus/FluentValidation-Sample

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir