Merhaba,
Merhaba, bu yazıda Asp.Net Core MVC Form Validation konusu hakkında sahip olduğum bilgiler ve araştırmalarımı aktarmak istiyorum.
Validasyon kontroller bir uygulamadaki verinin istenilen şekil ve kuralda sunucuda depolanabilmesini kontrol eden yapıdır.
Tüm framework yapıları için kendilerine özel validasyon kontrolleri mevcuttur. .NET Core için ise bunu iki şekilde açıklayabiliriz.
- Data Annotations(Veri Açıklamaları) yardımı ile modeller tarafından gerçekleştirilen server-side(sunucu tarafı) kontroller.
- jQuery Unobtrusive Validation(jQuery Göze Batmayan Doğrulama) ile client-side(istemci tarafı)
jQuery Unobstrusive Validation, jQuery Validate kütüphanesini temel alan ve Microsoft tarafından geliştirilen bir kütüphanedir.
ASP.NET Core MVC ile projemizi oluşturuyoruz ve sonrasında CustomerController ekliyoruz.
[Route("Customer")]
public class CustomerController : Controller
{
[HttpGet]
[Route("Index")]
public IActionResult Index()
{
return View();
}
[HttpGet]
public IActionResult Create()
{
return View();
}
}
Bu kısımda ASP.NET Core model binding konusuna değinmeyeceğim. Bu konuda bu yazıdan bilgi alabilirsiniz. Şimdi ise View sayfasına geçelim.
@model Customer
<div>
<form asp-action="Create">
<div class="form-group row">
<label asp-for="ID" class="col-sm-2 col-form-label">ID</label>
<div class="col-sm-10">
<input asp-for="ID" type="text" class="form-control" />
<span asp-validation-for="ID" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="Name" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input asp-for="Name" type="text" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
Customer, Create.cshtml için model olacaktır. Burada “asp-for”, “asp-action” ve “asp-validation-for” gibi Tag Helpers kullandık. Bu kısımları ilerleyen kısımda konuşacağız.
public class Customer
{
public int ID { get; set; }
public string Name { get; set; }
}
Bu kısımda Customer modelimizi ve Controller kısmında HttpPost attribute ile Create metodunu oluşturup parametre olarak Customer tipinde bir değer ekledik. Şunu da hatırlatmak gerekiyor ki Customer modeli içerisinde yer alan property’ler için farklı data annotation(veri açıklamaları) ekleyebiliriz.
Bulduğum bir tablo içerisinde bunlara birkaç örnek verilebilir.

[HttpPost]
public IActionResult Create(Customer customer)
{
if (!ModelState.IsValid)
{
return View(customer);
}
return Content("Success");
}
Post action metodu ile gelen model ‘ModelState’ kullanılarak kontrol edilebilir ve ModelState.IsValid metodundan dönen sonuca göre işlem yapılır.
Burada eğer model doğru şekilde oluşturulduysa içerik olarak ekrana ‘Success’ mesajı yazdırılacaktır. Ama eğer ki bu durum geçerli değilse hatalı oluşturulan bu modelini tekrar Create.cshtml sayfasına döndür dedik.
Şimdi ise uygulamamızı çalıştıralım.

Yukarıdaki görüntü ile Create sayfasında karşılaşıyoruz.
Sayfada sağ tıklayıp incele kısmına gidersek;

Bu kısımda gördüğünüz gibi span etiketi içerisinde bir hata mesajı bulunmaktadır ancak şuan submit etmediğimiz için span etiketinin içi boş bulunmaktadır.
Şimdi kritik noktaya gelelim.
Burada eğer submit edersek ve Customer modelini Create metoduna ([HttpPost] metodu) yollamış oluruz. Daha sonra server tarafından render edilen hata mesajlarını ekranda görebiliriz.
Şunu iyi hatırlayalım;
Bu yaklaşım server-side(sunucu tarafı) validasyon kontrolüdür. Bu yöntem ile her submit işleminde sunucuya tüm form gönderilir ve sayfa yeniden yüklenir.
jQuery Unobtrusive Validation(jQuery Göze Batmayan Doğrulama)
Bu validasyon türü ise Microsoft tarafından geliştirilen bir front-end validasyon kütüphanesidir. Client-side(istemci tarafı) validasyonu kullanarak sunucu tarafına gidiş dönüş sayısını azaltabiliriz. Aynı zamanda sayfayı yenilemeden yapılan bir validasyon kontrolü de kullanıcı açısından kullanışlı bir görüntü sunabilir.
- Bu validasyon kontrolünün çoğu görevi Html Data Attributes(‘data-{xAttribute}’)’e bağlıdır.
- Yukarıda da gördüğümüz üzere bu validasyon kontrolünde data-attribute çözümlenir ve jQuery validasyon kısmına gönderilir. jQuery tarafından validasyon kontrolüne hazır olduğunu bildirmesi için ‘data-val’ attribute değeri ‘true’ yapılır. Ayrıca bu kısım ‘true’ olduğunda bu alan ayrıca eklediğimiz kuralı da içermelidir. Bunu da ‘data-val-ruleName’ ile sağlar. Bu kurallardan birine ‘data-val-required’ örneğini verebiliriz. Bu attribute ile bu kısmın boş geçilemeyeceğini ve doldurulması zorunlu olduğunu bildiririz.
- Yukarıda yaptığımız örnekte MVC Tag Helpers kullandığımız için data attributes(veri özellikleri) sayfa render edilirken dinamik olarak oluşturulur.
Eğer bu validasyon kontrolünü kullanmak istersek jQuery kütüphanelerini eklememiz gerekmektedir.
Şimdi bunu Create.cshtml sayfasına ekleyelim.
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
@model Customer
<div>
<form asp-action="Create">
<div class="form-group row">
<label asp-for="ID" class="col-sm-2 col-form-label">ID</label>
<div class="col-sm-10">
<input asp-for="ID" type="text" class="form-control" />
<span asp-validation-for="ID" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="Name" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input asp-for="Name" type="text" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
Models/Customer.cshtml
public class Customer
{
[Key]
public int ID { get; set; }
[Required]
public string Name { get; set; }
}
Eğer bu şekilde çalıştırırsak validasyon kontrolü tarayıcı tarafından gerçekleştirilecek. Şimdi Javascript’i devre dışı bırakalım ve sunucu tarafı validasyonu tamamlayalım.

Uygulamayı tekrar çalıştırdığımızda Javascript devre dışı bırakıldığından itibaren istemci tarafı validasyon başarısız olacaktır.
Custom Attribute Validation
Kendi istediğimiz validasyon ayarlarını kullanmak için Custom Attribute oluşturabiliriz. Bu ayarları server-side(sunucu tarafı) için gerçekleştiririz.
Attributes/TextAttribute.cs
public class TextAttribute : ValidationAttribute
{
public string ExpectedText {get;set;}
public TextAttribute(string expectedText)
{
ExpectedText = expectedText;
}
public string GetErrorMessage() => $"Enter value should always be {ExpectedText}";
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var customer = (Customer)validationContext.ObjectInstance;
if (string.IsNullOrEmpty(customer.Name) || customer.Name.ToLower() != ExpectedText.ToLower())
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
}
Yukarıda ValidationAttribute sınıfından kalıtım aldığımız TextAttribute sınıfı ile yeni bir Data Annotation(veri açıklaması) oluşturmuş olduk. Bu sınıf içerisinde IsValid() metodunu override ederiz ve ihtiyaç duyduğumuz validasyon ayarlarını bu kısımda yaparız. Burada input olarak string değer alırız ve eğer istenilen kurallara uygun şekilde değer girilmişse işlem başarılı şekilde tamamlanır. Aksi takdirde hata mesajını görürüz. Bizim istediğimiz ise Data Annotation kısmında girdiğimiz değer ile input değerinin birbiri ile aynı şekilde girilmiş olması.
Bunu uygulayacak olursak;
public class Customer
{
[Key]
public int ID { get; set; }
[Text("Serhat")]
public string Name { get; set; }
}

Yukarıda da görüldüğü üzere Text(“Serhat”) olarak bir Data Annotation belirttik. Ancak girdiğimiz değer ile istediğimiz değer uyuşmadığı için TextAttribute içerisinde oluşturduğumuz GetErrorMessage() metodu ile hata mesajını alırız.
Client-Side Custom Validation
Bu kısımda ise client-side(istemci tarafı) custom validation konusuna bakalım.
Hiçbir input içerisinde data-attribute(veri özelliği) iki farklı validasyon içermez. MVC Tag Helpers hangi veri özelliğini üreteceğini bilmez. jQuery Validasyon kütüphanesi ‘require’ veya ‘compare’ gibi tüm kuralları içerisinde barındırır. Yani buradan da anlarız ki Custom Attributes yani kendi oluşturduğumuz özellikler ile jQuery kütüphanesi bize bir sonuç döndürmez.
Şimdi ise Create.cshtml sayfasını kendi oluşturduğumuz data attribute için güncelleyelim.
@model Customer
<div>
<form asp-action="Create">
<div class="form-group row">
<label asp-for="ID" class="col-sm-2 col-form-label">ID</label>
<div class="col-sm-10">
<input asp-for="ID" type="text" class="form-control" />
<span asp-validation-for="ID" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="Name" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input asp-for="Name"
data-val="true"
data-val-text="Enter value should always be Serhat"
data-val-text-expectedtext="Serhat"
type="text" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
- data-val= ‘true’ validasyon için hazır olduğunu belirtir.
- data-val-text burada TextAttribute jQuery Unobstrusive Validation kütüphanesi içerisine eklenmesi gereken Text validasyon kuralını belirtir.
- data-val-text-expectedtext burada TextAttribute içerisinde yer alan expectedText parametresini belirtir.
Şimdi ise kendimiz javascript validasyonu yazacağız ve bu metodları data attribute kural isimleri olan jQuery Unobstrusive Validation ile kaydedeceğiz.
wwwroot/js/ içerisinde bir customValidation.js dosyası oluştururuz.
wwwroot/js/customValidation.js
$.validator.addMethod('text', function (value, element, params) {
var txtValue = $(params[0].val());
console.log(txtValue);
var expectedText = params[1];
console.log(expectedText);
if (!txtValue || !expectedText) {
return false
}
return txtValue.toLowerCase() === expectedText.toLowerCase();
});
$.validator.unobtrusive.adapters.add('text', ['expectedText'], function (options) {
var element = $(options.form).find('input#Name');
options.rules['text'] = [element, options.params['expectedText']];
options.messages['text'] = options.message;
});
bunu Create.cshtml içerisine ekleriz.
şimdi tekrar test edersek;

Sunucudaki Custom Validation Attributes için data-Html Attribute Oluşturma
Yukarıda istemci tarafı için Custom Validation oluşturduğumuzda data-Html attribute kısmını kendimiz manuel olarak eklemiştik. Bunu sunucu tarafında dinamik olarak 2 şekilde de yapabiliriz.
- AttributeAdapterBase<TAtrribute>
- IClientModelValidator
kullanarak.
AttributeAdapterBase kullanarak;
Attribute/TextAttributeAdapter.cs dosyasını oluştururuz.
public class TextAttributeAdapter : AttributeAdapterBase<TextAttribute>
{
public TextAttributeAdapter(TextAttribute attribute,IStringLocalizer stringLocalizer):base(attribute,stringLocalizer)
{
}
public override void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-text", GetErrorMessage(context));
MergeAttribute(context.Attributes, "data-val-text-expectedtext", Attribute.ExpectedText);
}
public override string GetErrorMessage(ModelValidationContextBase validationContext)
{
return Attribute.GetErrorMessage();
}
}
- AttributeAdapterBase<TAtrribute>, Microsoft.AspNetCore.Mvc.DataAnnotations Namespace’i içerisinde yer almaktadır.
- Burada yer alan TAttribute bizim custom validation attribute sınıfımız oluyor. Yani bu kısımda TextAttribute sınıfını ekledik.
- Override ettiğimiz AddValidation metodu sayfa yüklendiğinde veri özelliklerini(data attributes) dinamik olarak yükleyecek olan kısımdır.
Validation Attribute Adapter’dan bir örnek oluşturmak için IValidationAttributeAdapterProvider arayüzünden implemente edilen provider sınıfına onu kaydederiz.
Attribute/CustomValidationAttributeAdapterProvider
public class CustomValidationAttributeAdapterProvider : IValidationAttributeAdapterProvider
{
private readonly IValidationAttributeAdapterProvider baseProvider = new ValidationAttributeAdapterProvider();
public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
{
if (attribute is TextAttribute textAttribute)
{
return new TextAttributeAdapter(textAttribute,stringLocalizer);
}
return baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
}
}
Bu işlemlerden sonra ise bu provider Startup.cs içerisindeki ConfigureServices içerisine kaydedilir.
services.AddSingleton<IValidationAttributeAdapterProvider, CustomValidationAttributeAdapterProvider>();
Create.cshtml içerisinde manuel olarak eklenen tüm veri özellikleri(data-attributes) kaldıralım ve eski haline getirelim.
Views/Customer/Create.cshtml
@model Customer
<div>
<form asp-action="Create">
<div class="form-group row">
<label asp-for="ID" class="col-sm-2 col-form-label">ID</label>
<div class="col-sm-10">
<input asp-for="ID" type="text" class="form-control" />
<span asp-validation-for="ID" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="Name" class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input asp-for="Name" type="text" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
Şimdi uygulamayı çalıştıralım ve incele kısmına bakalım. Burada custom validation için oluşturulan veri özellikleri(data attributes) sunucudan alınmaktadır.

IClientModelValidator Kullanarak;
Bu kısım için Attribute dosyası içinde Text2Attribute.cs sınıfı oluşturalım.
Attribute/Text2Attribute.cs
public class Text2Attribute : ValidationAttribute, IClientModelValidator
{
public string ExpectedText { get; set; }
public Text2Attribute(string expectedText)
{
ExpectedText = expectedText;
}
public string GetErrorMessage() => $"Enter value should always be {ExpectedText}";
public void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-textMatch", GetErrorMessage());
MergeAttribute(context.Attributes, "data-val-textMatch-expectedText", ExpectedText);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var customer = (Customer)validationContext.ObjectInstance;
if (string.IsNullOrEmpty(customer.Name) || customer.Name.ToLower() != ExpectedText.ToLower())
{
return new ValidationResult(GetErrorMessage());
}
return ValidationResult.Success;
}
private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
Yukarıda Text2Attribute sınıfı ValidationAttribute sınıfından kalıtım alır. Ayrıca IClientModelValidator arayüzünü veri özelliklerini(data attributes) Html’e bağlamak için implemente ederiz.
Şimdi Customer modelini güncelleyerek bu oluşturduğumuz attribute buraya eklenir.
public class Customer
{
[Key]
public int ID { get; set; }
[Text2("Serhat2")]
public string Name { get; set; }
}
Uygulamayı çalıştırıp incele kısmına bakarsak veri özellikleri(data attributes) Html elementleri içerisine eklenmiş olacaktır.
jQuery Unobstrusive Validation Kullanarak Dinamik Form Validasyonu
Bazen bir formu dinamik olarak yüklememiz gerekebilir. Bu durumda gelen formlar için validasyon işlemi sayfa ile gelen formlardaki gibi çalışmaz. AJAX ile yüklenen formları jQuery Unobstrusive Validation ile ayrıştırarak validasyon işlemini ekleme gibi bir seçeneğimiz bulunmaktadır.
Şimdi CustomerController.cs içerisine yeni bir action method ekleriz.
[HttpGet]
[Route("test-sayfa")]
public IActionResult TestPage()
{
return View();
}
<div>
<div class="row">
<div class="col col-sm-12">
<button type="button" class="btn btn-primary" id="loadform">Load Form And Add Dynamic Validations</button>
</div>
</div>
<div class="row" style="margin-top:10px">
<div class="col col-sm-12">
<div id="form-container">
</div>
</div>
</div>
</div>
<script src="~/js/customValidation.js"></script>
<script src="~/js/dynamicForm.js"></script>
Bu sayfada bir buton var ve bu butona tıkladığımızda formu AJAX ile yükler. Daha sonra ‘form-container’ id’si ile div içerisinde render edilir.
$("#loadform").click(function () {
$("#form-container").html('');
$.get({
url: "https://localhost:44306/Customer/get-dynamic-form",
dataType: "html",
error: function (jqXHR, textStatus, errorThrown) {
alert(textStatus + ": Couldn't add form. " + errorThrown);
},
success: function (newFormHTML) {
var container = document.getElementById("form-container");
container.insertAdjacentHTML("beforeend", newFormHTML);
var forms = container.getElementsByTagName("form");
var newForm = forms[forms.length - 1];
$.validator.unobtrusive.parse(newForm);
}
})
})
Yukarıda MVC action method çağrılır ve bununla birlikte partial view geriye değer olarak formu döndürür.
[HttpGet]
[Route("get-dynamic-form")]
public IActionResult DynamicForm()
{
return PartialView("DynamicPartialForm");
}
Views/Customer/DynamicPartialForm.cshtml
<div>
<form>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Name</label>
<div class="col-sm-10">
<input id="Name"
name="Name"
data-val="true"
data-val-text="Enter value should always be Serhat"
data-val-text-expectedText="Serhat"
type="text" class="form-control" />
<span class="text-danger field-validation-valid" data-valmsg-for="Name" data-valmsg-replace="true"></span>
</div>
</div>
<div class="form-group row">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
</div>
Şimdi uygulamayı çalıştırırsak;

İncele kısmına bakacak olursak;

bunda da yukarıda olduğu şekilde client-side olarak validasyon işlemi yapılabilir.
Eksik gördüğünüz veya eklemek istediğiniz bilgiler olursa benimle de paylaşırsanız mutlu olurum.
Okuduğunuz için teşekkür ederim.