Author : MD TAREQ HASSAN | Updated : 2020/06/23

Model binding Github repository: https://github.com/hovermind/AspNetCoreModelBinding

Validation Types

Client side validation

_ValidationScriptsPartial.cshtml includes validation related scripts

<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

The jQuery Unobtrusive Validation script is a custom Microsoft front-end library that builds on the popular jQuery Validate plugin. Without jQuery Unobtrusive Validation, you would have to code the same validation logic in two places: once in the server side validation attributes on model properties, and then again in client side scripts (the examples for jQuery Validate’s validate() method shows how complex this could become).

Instead, MVC’s Tag Helpers and HTML helpers are able to use the validation attributes and type metadata from model properties to render HTML 5 data- attributes in the form elements that need validation. MVC generates the data- attributes for both built-in and custom attributes. jQuery Unobtrusive Validation then parses the data- attributes and passes the logic to jQuery Validate, effectively “copying” the server side validation logic to the client. You can display validation errors on the client using the relevant tag helpers

Validation Message Tag Helper

There are two Validation Tag Helpers

<span asp-validation-for="Email"></span>

<div asp-validation-summary="ModelOnly"></div>

asp-validation-for

<h1>Create Hover Info</h1>

<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
		
		
			<!-- ... ... ... -->

            <div class="form-group">
                <label asp-for="Id" class="control-label"></label>
                <input asp-for="Id" class="form-control" />
                <span asp-validation-for="Id" class="text-danger"></span>
            </div>
			
			<!-- ... ... ... -->
			
			
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

sp-validation-summary

<h1>Create Hover Info</h1>

<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
		
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
			
			<!-- ... ... ... -->
			
            <div class="form-group">
                <label asp-for="Id" class="control-label"></label>
                <input asp-for="Id" class="form-control" />
                <span asp-validation-for="Id" class="text-danger"></span>
            </div>
			
			<!-- ... ... ... -->
			
			
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<!-- ... ... ... -->

Showing validation error

HoverInputModel.cs

public class HoverInputModel
{

	[Required]
	[Display(Name = "First Name")]
	[Range(5, 50)]
	public int FirstName { get; set; }


	[Required]
	[Display(Name = "Last Name")]
	[StringLength(50, MinimumLength = 5, ErrorMessage = "Last name must have at least 5 characters")]
	public int LastName { get; set; }


	[Required(ErrorMessage = "{0} is required")]
	[StringLength(100, MinimumLength = 10, ErrorMessage = "Name must have at least 10 characters")]
	[RegularExpression(@"^[a-zA-Z\s]+$", ErrorMessage = "Please, use letters in the name. Digits are not allowed.")]
	[Display(Name = "Full Name")]
	public string FullName { get; set; }


	[Required(ErrorMessage = "{0} is required")]
	[StringLength(100, MinimumLength = 1, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.")]
	public string Email { get; set; }


	// will be null
	public string Bar { get; set; }
}

HoverController.cs

public class HoverController : Controller
{

    // ... ... ...

	// GET: Hover/Create
	public IActionResult Create()
	{
		return View();
	}
	

	// POST: Hover/Create
	// To protect from overposting attacks, enable the specific properties you want to bind to, for 
	// more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
	[HttpPost]
	[ValidateAntiForgeryToken]
	public async Task<IActionResult> Create([FromFrom]HoverInputModel hoverInputModel)
	{
		if (ModelState.IsValid)
		{
			// perform EF core task here i.e. _context.Add(hoverInputModel);await _context.SaveChangesAsync();
			
			return RedirectToAction(nameof(Index));
		}
		
		return View(hoverInputModel);
	}
	
	// ... ... ...
}

Create.cshtml

@model ModelBindingAndFormValidation.Models.HoverInputModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create Hover Info</h1>

<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Id" class="control-label"></label>
                <input asp-for="Id" class="form-control" />
                <span asp-validation-for="Id" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="FirstName" class="control-label"></label>
                <input asp-for="FirstName" class="form-control" />
                <span asp-validation-for="FirstName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="LastName" class="control-label"></label>
                <input asp-for="LastName" class="form-control" />
                <span asp-validation-for="LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="FullName" class="control-label"></label>
                <input asp-for="FullName" class="form-control" />
                <span asp-validation-for="FullName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Email" class="control-label"></label>
                <input asp-for="Email" class="form-control" />
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

When form is posted to server, depending on annotations applied to input model, error messages will be shown.

Scaffolding view with client side validation

Create input model and add annotations to properties

Models/FooInputModel.cs

using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;


[Bind(nameof(FirstName), nameof(LastName), nameof(FullName), nameof(Email))]
public class FooInputModel
{

	[Required]
	[Display(Name = "First Name")]
	[StringLength(50, MinimumLength = 5)]
	public string FirstName { get; set; }


	[Required]
	[Display(Name = "Last Name")]
	[StringLength(50, MinimumLength = 5, ErrorMessage = "{0} must have at least {2} characters")]
	public string LastName { get; set; }


	[Required(ErrorMessage = "{0} is required")]
	[StringLength(100, MinimumLength = 10, ErrorMessage = "{0} must have at least {2} characters")]
	[RegularExpression(@"^[a-zA-Z\s]+$", ErrorMessage = "Please, use letters in the name. Digits are not allowed.")]
	[Display(Name = "Full Name")]
	public string FullName { get; set; }


	[Required(ErrorMessage = "{0} is required")]
	[StringLength(100, MinimumLength = 1, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.")]
	public string Email { get; set; }


	// ignored (value will be null after model binding)
	public string Bar { get; set; }
}

Scaffold controller with read/write actions

Controllers/FooController.cs

public class FooController : Controller
{
	// GET: FooController
	public ActionResult Index()
	{
		return View();
	}


	// GET: FooController/Create
	public ActionResult Create()
	{
		return View();
	}

	// POST: FooController/Create
	[HttpPost]
	[ValidateAntiForgeryToken]
	public ActionResult Create(FooInputModel fooInputModel)
	{
		if (ModelState.IsValid)
		{
			// perform EF core task here i.e. _context.Add(hoverInputModel);await _context.SaveChangesAsync();

			return RedirectToAction(nameof(Index));
		}

		return View(fooInputModel);
	}
}

Scaffold view based on input model

Views/Foo/Create.cshtml

@model ModelBindingAndFormValidation.Models.FooInputModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>FooInputModel</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Id" class="control-label"></label>
                <input asp-for="Id" class="form-control" />
                <span asp-validation-for="Id" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="FirstName" class="control-label"></label>
                <input asp-for="FirstName" class="form-control" />
                <span asp-validation-for="FirstName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="LastName" class="control-label"></label>
                <input asp-for="LastName" class="form-control" />
                <span asp-validation-for="LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="FullName" class="control-label"></label>
                <input asp-for="FullName" class="form-control" />
                <span asp-validation-for="FullName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Email" class="control-label"></label>
                <input asp-for="Email" class="form-control" />
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Bar" class="control-label"></label>
                <input asp-for="Bar" class="form-control" />
                <span asp-validation-for="Bar" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Startup.Configure()

Add Index view

Now run the application