In this article, we will learn about:
- Form Validations in .NET 6 MVC using Data Annotations.
Please check out the previous articles in the series, in order to get the chronology:
Form Validations using Data Annotations
Validations are a very important part of a web application.
.NET 6 MVC provides a very extensive built-in mechanism to achieve perfect validations.
We will learn about validations using data annotations in this section.
Let’s add validations to our Add Product form.
Required Validations on input controls
@model AddProductViewModel
<form method="post">
<div class="mb-3">
<label for="productName">Product Name</label>
<input type="text" class="form-control" placeholder="Enter Product Name"
asp-for="ProductName">
<span asp-validation-for="ProductName" class="text-danger"></span>
</div>
<div class="mb-3">
<label for="productCategory">Product Category</label>
<select class="form-control" placeholder="Select Product Category"
asp-for="ProductCategory"
asp-items="Html.GetEnumSelectList<Enumerations.ProductCategory>()">
<option value="">Please Select</option>
</select>
</div>
<div class="mb-3">
<label for="productPrice">Product Price</label>
<input type="text" class="form-control" placeholder="Enter Product Price"
asp-for="Price">
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="mb-3">
<label for="productPrice">Imported Date</label>
<input type="date" class="form-control" placeholder="Select Imported Date"
asp-for="ImportedDate">
<span asp-validation-for="ImportedDate" class="text-danger"></span>
</div>
<input type="submit" class="btn btn-primary" value="Submit" />
</form>
We have added asp-validation-for=”PropertyName” tag helpers to all the input elements in the span tags.
asp-validation-for tag helper adds the data-valmsg-for=”PropertyName” attribute to the span element which helps in the display of the error message.

We will talk about the select input control after a while, it would be a very good discussion.
Let’s add the [Required] data annotations to the model properties.
[Required]
public string ProductName { get; set; }
public int? ProductCategory { get; set; }
[Required]
public double? Price { get; set; }
[Required]
public DateTime? ImportedDate { get; set; }
We also need to check ModelState.IsValid in the controller action method to be certain about the validations and return the same view with the errors, if the validations fail.
[HttpPost]
public IActionResult AddProduct(AddProductViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var postedData = $"{model.ProductName} {model.ProductCategory} " +
$"{model.ProductName} {model.ImportedDate}";
return Content("Add Product Post Method");
}
ModelState is a dictionary that provides the readonly boolean IsValid property and other useful keys and values collection.

Let’s keep the form empty and click on the Submit button.

The required field validations trigger properly.
The default error message is: “The PropertyName field is required.“
We can always tweak the error messages, let’s see it in action.
RequiredAttribute provides an overload, via which we can add custom error messages.
[Required(ErrorMessage = "Product Name can not be empty.")]
public string ProductName { get; set; }
public int? ProductCategory { get; set; }
[Required(ErrorMessage = "Product Price can not be empty.")]
public double? Price { get; set; }
[Required(ErrorMessage = "Product Imported Date can not be empty.")]
public DateTime? ImportedDate { get; set; }
Let’s run the app and see the updated error message.

Required Validation on a select element
We will now learn how to properly deal with Required validation wrt a select element.
select element renders as a group of option elements.
Let’s create a new view (RenderDropDowns.cshtml) with just select elements to properly understand the approach of adding validations.
@using static FirstMVCProject.Enums.Enumerations
@model SelectElementViewModel
<form method="post">
<div class="container">
<div class="row">
<div class="col-sm">
<h3>Dropdown rendered via a list of SelectListItems</h3>
<select class="form-control-sm" asp-for="SelectedCountry"
asp-items="@Model.Countries"></select>
</div>
</div>
<div class="row">
<div class="col-sm">
<h3>Dropdown rendered via an Enum</h3>
<select class="form-control-sm" asp-for="Category"
asp-items="@Html.GetEnumSelectList<ProductCategory>()">
<option>Please Select</option>
</select>
</div>
</div>
<br />
<br />
<div class="row">
<div class="col-sm">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
</div>
</div>
</form>
The GET action method:
public IActionResult RenderDropDowns()
{
var model = new SelectElementViewModel
{
Countries = GetCountries()
};
return View(model);
}
private IEnumerable<SelectListItem> GetCountries()
{
return new List<SelectListItem>
{
new SelectListItem{Text="Please Select",Value=""},
new SelectListItem{Text="India",Value="1"},
new SelectListItem{Text="USA",Value="2"},
new SelectListItem{Text="Germany",Value="3"},
new SelectListItem{Text="China",Value="4"},
new SelectListItem{Text="UK",Value="5"}
};
}
The SelectElementViewModel:
public class SelectElementViewModel
{
public IEnumerable<SelectListItem> Countries { get; set; }
public string SelectedCountry { get; set; }
public ProductCategory Category { get; set; }
}
The view looks something like this:

Let’s add span tags with asp-validation-for attributes to these 2 select elements.
<div class="row">
<div class="col-sm">
<h3>Dropdown rendered via a list of SelectListItems</h3>
<select class="form-control-sm" asp-for="SelectedCountry"
asp-items="@Model.Countries"></select>
<span class="text-danger" asp-validation-for="SelectedCountry"></span>
</div>
</div>
<div class="row">
<div class="col-sm">
<h3>Dropdown rendered via an Enum</h3>
<select class="form-control-sm" asp-for="Category"
asp-items="@Html.GetEnumSelectList<ProductCategory>()">
<option>Please Select</option>
</select>
<span class="text-danger" asp-validation-for="Category"></span>
</div>
</div>
Let’s add a POST action method to which the form will be posted.
[HttpPost]
public IActionResult RenderDropDowns(SelectElementViewModel model)
{
if (!ModelState.IsValid)
{
model.Countries = GetCountries();
return View(model);
}
var selectedCountry = model.SelectedCountry;
var selectedCategory = model.Category;
return Content("Form successfully posted!!!");
}
Let’s run the app, click the submit button without selecting any of the drop down items and observe. Please note that we have not added any [Required] attribute yet to the SelectElementViewModel properties.

We encounter an interesting problem
- The dropdown created using a list of SelectListItem does not throw any validation.
- The dropdown created using an Enum throws an error message which says that “The value ‘Please Select’ is not valid for Category“.
Let’s understand one by one.
In the first dropdown, the first option Please Select text is bind to an empty value (“”). Now the asp-for property SelectedCountry in the model is also a string, hence the empty value parses correctly, and there is no validation message.

In order to display a validation message in case of “Please Select“, let’s add a [Required] attribute to the SelectedCountry property and again run the app.
public class SelectElementViewModel
{
public IEnumerable<SelectListItem> Countries { get; set; }
[Required(ErrorMessage = "Please select a country.")]
public string SelectedCountry { get; set; }
public ProductCategory Category { get; set; }
}

Now, let’s approach the second dropdown. An enum’s underlying data type is integer.
The “Please Select” text value is “Please Select” itself, because we have not given any explicit value.
The asp-for property in this case is the enum ProductCategory itself which doesn’t understand the incoming string value “Please Select”. Hence we get an error message.
It needs to be understood that this error message is due to a data type mismatch and not a MVC validation error message.
Let’s fix this by explicitly assigning an empty/null value to option “Please Select” and making the asp-for property nullable and adding a Required attribute.

Let’s run the app and verify our changes.

- We are now proficient in validations through Data Annotations.
- There are a few other Data Annotations that the .NET framework provides.
- They can be learned here.
- We will do a full fledged project in the coming articles once the basics are covered, where in we will use few other data annotations.
Form Validations in case of jQuery POST
Till now, we have seen form validations when we have used the input type = “submit” button.
Let us now approach how to display validation messages when we perform form post using jQuery.
Let’s add validations to AddProductUsingJquery form.
@model AddProductViewModel
<h1>Form Post Using jQuery</h1>
<form id="ProductForm">
<div class="mb-3">
<label for="productName">Product Name</label>
<input type="text" class="form-control" placeholder="Enter Product Name" asp-for="ProductName">
<span asp-validation-for="ProductName" class="text-danger"></span>
</div>
<div class="mb-3">
<label for="productCategory">Product Category</label>
<select class="form-control" placeholder="Select Product Category" asp-for="ProductCategory"
asp-items="Html.GetEnumSelectList<Enumerations.ProductCategory>()">
<option value="">Please Select</option>
</select>
<span asp-validation-for="ProductCategory" class="text-danger"></span>
</div>
<div class="mb-3">
<label for="productPrice">Product Price</label>
<input type="text" class="form-control" placeholder="Enter Product Price" asp-for="Price">
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="mb-3">
<label for="productPrice">Imported Date</label>
<input type="date" class="form-control" placeholder="Select Imported Date" asp-for="ImportedDate">
<span asp-validation-for="ImportedDate" class="text-danger"></span>
</div>
<input type="button" class="btn btn-primary" value="Submit" id="btnSubmit" />
</form>
The POST action method needs to include the ModelState.IsValid check, let’s do that.
[HttpPost]
public IActionResult AddProductUsingJQuery(AddProductViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var postedData = $"{model.ProductName} {model.ProductCategory} " +
$"{model.ProductName} {model.ImportedDate}";
return Content("Add Product Using jQuery Post Method");
}
Let’s run the app, click on the Submit button without filling any input values and observe.
We do not get any validation error message on the view, however the ModelState is not valid. We console the response from the server side. The response is as below:

Since we have return View(model); in case of validation failures, this is what we get.
We need to some how grab the error messages and display them on the UI.
ValidateAJAX attribute
Let’s create a ValidateAJAX attribute which will help us to iterate on the ModelState keys/values and return a JSON collection back, that the AJAX error callback will render on the browser.
public class ValidateAJAXAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Request.Headers["x-requested-with"] != "XMLHttpRequest")
return;
var modelState = context.ModelState;
if (!modelState.IsValid)
{
var errors = modelState
.Where(x => x.Value.Errors.Count() > 0)
.Select(y => new BadRequestModel
{
Key = y.Key,
ErrorMessages = y.Value.Errors.Select(z => z.ErrorMessage).ToArray()
}).ToList();
context.Result = new JsonResult(errors);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
}
}
}
In the above code, we check if the incoming request is not an AJAX request, we just do a return. We do not want this code to be executed for non AJAX requests.
We have a request header with its value as: x-requested-with: XMLHttpRequest in case of an AJAX request, hence we check it.
Next we verify the invalid ModelState and iterate on the Error collection to return a JSON result back along with a BadRequest(400) HTTP status code.
We set the status code because the incoming request has bad data, that doesn’t suffice the validations, and we want the JSON result in the error callback function of the AJAX code.
Let’s run the app and observe.

We get the error messages and the keys in the console log of the error callback.
Let’s just now iterate on them with the help of jQuery and display the messages on the UI.
$(document).ready(() => {
$('#btnSubmit').on('click', () => {
$('span').text('');
$.ajax(
{
url: '/Product/AddProductUsingJQuery',
type: 'POST',
data: $('#ProductForm').serialize(),
success: function (response) {
console.log(response);
},
error: function (xhr) {
$.each(xhr.responseJSON, function (index, err) {
$('span[data-valmsg-for="' + err.key + '"]').text(err.errorMessages[0]);
});
}
}
);
});
});
Let’s run the app and verify the changes.

The validation messages appear properly in the case of jQuery AJAX post too.
Important Note
Whatever, we have learnt today in terms of validations are SERVER SIDE validations. We need to click on the Submit button and then only the validations are triggered.
The validations do not appear on the blur or focusout event of the input control.
We will learn about CLIENT SIDE validations in the next article.
Github Link
The updated code is available at: https://github.com/anurag1302/dotnet6-mvc
Conclusion
We have learnt about Form Validations in .NET 6 MVC in detail.
Stay tuned for the next article.
Pingback: .Net 6 MVC Series - Article 5 - Client Side Validation - TechnCode Tools
Pingback: .NET 6 MVC Series - Article 6 - IValidatableObject Interface - TechnCode Tools