Custom Validation in ASP.NET CORE Using Fluent Validation

In this article, we are going to learn how to write custom validation using FluentValidation.

  1. Predicate Validator (‘Must‘ Method)
  2. PropertyValidator  

Creating ASP.NET Core Application

We are going to create ASP.NET Core Web Application for that we are going to choose ASP.NET Core Web Application template.

Next, we are going to set Project Name WebApplication15 and location and in last part, we are going to choose .Net Core framework and ASP.NET Core Version 3.0 as framework for application and few advance settings for such as configuring https and enabling docker we are not going to enable both of settings for this project.

Now finally click on create button to create a project.
Project structure
The project structure generated according to the configuration.

After creating a project, next, we are first going to install ‘FluentValidation.AspNetCore‘ package from NuGet package.

Installing FluentValidation
Installing ‘FluentValidation.AspNetCore’ package from NuGet package.

After installing the package next, we are going configure FluentValidation.AspNetCore package in Startup class.

Configuring FluentValidation.AspNetCore package in Startup class

We are going to Registering FluentValidation Service inside ConfigureServices method as shown below.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews().AddFluentValidation();
}

After Registering service next, we are going to add employee and codes Model for adding validation.

Adding Model

using System.Collections.Generic;
using System.ComponentModel;

namespace WebApplication15.Models
{
    public class Employee
    {
        public string Name { get; set; }

        [DisplayName("Code")]
        public int? CountryCode { get; set; }
        public string MobileNo { get; set; }

        [DisplayName("Select Country")]
        public string SelectedCountry { get; set; }
        public List<Codes> ListofCountries { get; set; }
    }

    public class Codes
    {
        public string CountryId { get; set; }
        public string CountryCode { get; set; }
        public string CountryName { get; set; }
    }

}

After adding a model next, we are going to add Default Controller with Index action method and Index View.

Adding Controller, Action Method and View

We are going to add a controller with name Default along with index action one for handling GET and another for the handling POST request. In HTTP GET request we are going to return a list of countries to bind dropdown list.

using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using WebApplication15.Models;

namespace WebApplication15.Controllers
{
    public class DefaultController : Controller
    {
        public IActionResult Index()
        {
            List<Codes> ListofCountries = new List<Codes>()
            {
               new Codes { CountryId= "1",  CountryCode = "+91" , CountryName = "India"},
               new Codes { CountryId= "2",  CountryCode = "+1" , CountryName = "United States"},
               new Codes { CountryId= "3",  CountryCode = "+7" , CountryName = "Russia"},
               new Codes { CountryId= "4",  CountryCode = "+20" , CountryName = "Egypt"},
               new Codes { CountryId= "5",  CountryCode = "+39" , CountryName = "Italy"}
            };

            Employee employee = new Employee();
            employee.ListofCountries = ListofCountries;
            return View(employee);
        }

        [HttpPost]
        public IActionResult Index(Employee employee)
        {
            List<Codes> ListofCountries = new List<Codes>()
            {
               new Codes { CountryId= "1",  CountryCode = "+91" , CountryName = "India"},
               new Codes { CountryId= "2",  CountryCode = "+1" , CountryName = "United States"},
               new Codes { CountryId= "3",  CountryCode = "+7" , CountryName = "Russia"},
               new Codes { CountryId= "4",  CountryCode = "+20" , CountryName = "Egypt"},
               new Codes { CountryId= "5",  CountryCode = "+39" , CountryName = "Italy"}
            };

            employee.ListofCountries = ListofCountries;

            if (!ModelState.IsValid)
            {
                return View(employee);
            }
            return View(employee);
        }
    }
}

Index View Code Snippet

@model Employee
@{
    ViewData["Title"] = "Index";
}
<hr />
<div class="row">
    <div class="col-md-12">
        <form asp-action="Index">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="row">
                <div class="col-md-4">

                    <label asp-for="Name" class="control-label"></label>
                    <input asp-for="Name" class="form-control" />
                    <span asp-validation-for="Name" class="text-danger"></span>
                </div>
            </div>
            <div class="row">
                <div class="col-md-4">
                    <label asp-for="SelectedCountry" class="control-label"></label>
                    <select asp-for="SelectedCountry"
                            class="form-control"
                            asp-items="@(new SelectList(Model.ListofCountries,"CountryId", "CountryName"))">
                    </select>
                </div>
            </div>
            <div class="row">
                <div class="col-md-1">
                    <label asp-for="CountryCode" class="control-label"></label>
                    <input asp-for="CountryCode" maxlength="3" inputmode="numeric" class="form-control" />
                    <span asp-validation-for="CountryCode" class="text-danger"></span>
                </div>
                <div class="col-md-3">
                    <label asp-for="MobileNo" class="control-label"></label>
                    <input asp-for="MobileNo" maxlength="15" class="form-control" />
                    <span asp-validation-for="MobileNo" class="text-danger"></span>
                </div>
            </div>

            <div class="clearfix"></div>
            <div class="row">
                <div class="col-md-4">
                    <br />
                    <input type="submit" value="Create" class="btn btn-primary" />
                </div>
            </div>
        </form>
    </div>
</div>
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}

After adding the view and controller next, we are going to write validation.

Adding Custom Validation (Predicate Validator)

For writing custom validation, we are going to add EmployeeValidator class which inherit it with AbstractValidator class which is a generic abstract class there we are going to pass our model name ‘Employee‘.

Then in the constructor of that class, we are going to write validations.

  • First, we are going validate dropdown list (Required)
  • Second, we are going to validate Mobileno (Required)
  • Third, we are going validate Country code entered by the user should be valid according to country use has chosen in the dropdown list.

For writing custom validation which depends on other properties we are going to create a separate method with name ‘ValidateCountryCode‘ where we are going validate user-entered country code with the user-selected country. Inside ‘ValidateCountryCode‘ method we are calling another method ‘CheckCodes‘ which takes countryid as input and return country code of that country. Then entered country code value and (country code) of selected country value is matched then we are going return value as true else we are going to return false. True means valid and false means invalid.

To implement custom validator, we need to use ‘Must‘ method and then pass a method (‘ValidateCountryCode‘) as a parameter which will return either true or false and based on that it will show error message ‘Enter Valid CountryCode‘.

CodeSnippet

RuleFor(x => x.CountryCode).NotNull().WithMessage("Please Enter 'CountryCode'")
    .Must(ValidateCountryCode).WithMessage("Enter Valid CountryCode");

This part can be replaced with the database for the demo I have used hardcore values.

using FluentValidation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebApplication15.Models
{
    public class EmployeeValidator : AbstractValidator<Employee>
    {
        public EmployeeValidator()
        {
            RuleFor(x => x.SelectedCountry).NotNull()
                .WithMessage("Please Select Country");

            RuleFor(x => x.MobileNo).NotNull()
              .WithMessage("Please Entire MobileNo");

            RuleFor(x => x.CountryCode).NotNull().WithMessage("Please Enter 'CountryCode'")
                .Must(ValidateCountryCode).WithMessage("Enter Valid CountryCode");

        }

        private bool ValidateCountryCode(Employee employee, int? CountryCode)
        {
            if ((!string.IsNullOrEmpty(employee.SelectedCountry) && !string.IsNullOrEmpty(Convert.ToString(CountryCode))))
            {
                var result = CheckCodes(employee.SelectedCountry);
                if (result == Convert.ToString(CountryCode))
                { return true; }
                else
                { return false; }
            }
            else
            {
                return false;
            }
        }

        public static string CheckCodes(string CountryId)
        {
            var listofcodes = new List<Codes>()
            {
               new Codes { CountryId= "1",  CountryCode = "91" , CountryName = "India"},
               new Codes { CountryId= "2",  CountryCode = "1" , CountryName = "United States"},
               new Codes { CountryId= "3",  CountryCode = "7" , CountryName = "Russia"},
               new Codes { CountryId= "4",  CountryCode = "20" , CountryName = "Egypt"},
               new Codes { CountryId= "5",  CountryCode = "39" , CountryName = "Italy"}
            };

            var result = (from templist in listofcodes
                          where templist.CountryId == CountryId
                          select templist.CountryCode).FirstOrDefault();
            return result;
        }
    }
}

After writing validation rules next, we are going register EmployeeValidator class in ConfigureServices method in startup class.

Registering Using ‘RegisterValidatorsFromAssembly’ Method

public void ConfigureServices(IServiceCollection services)
{
    // Third way
    services.AddControllersWithViews().AddFluentValidation(fv =>
    {
        fv.RegisterValidatorsFromAssembly(Assembly.GetExecutingAssembly());
    });
}

Now, after Registering next, we are going to run an application and test.
In going to test entering invalid country code, the country code for India is ’91‘.

Debug View while executing the validation.

Custom validation is working properly; next, we are going to look at second custom validator ‘PropertyValidator‘.

PropertyValidator

Using Property validator, you can Validate property which you are going to apply it or set it.
For using Property validator, we are going to add a class with the name ‘EmployeeNameValidator‘ class which we need to inherit with ‘PropertyValidator‘ class and implement IsValid method of it.
All logic for validation should be written inside of IsValid method.
We are going to write validation for validating Employee Name it must Only have letters. For that, I have used a user-selected regular expression.
Using message placeholders {PropertyName} and {PropertyValue} we can get access to PropertyName and PropertyValue which we can use in writing Custom Error Message.

CodeSnippet

using FluentValidation.Validators;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace WebApplication15.Models
{
    public class EmployeeNameValidator : PropertyValidator
    {
        public EmployeeNameValidator() : base("'{PropertyName}' entered {PropertyValue} is not a valid")
        {

        }
        protected override bool IsValid(PropertyValidatorContext context)
        {
            string EmployeeName = (string)context.PropertyValue;
            if (Regex.IsMatch(EmployeeName, @"^[a-zA-Z]+$"))
            {return true;}
            else
            {return false;}
        }
    }
}

After creating validator next, we are going to set validator to Name property in EmployeeValidator class.

RuleFor(x => x.Name).NotNull().WithMessage("Please Enter 'Name'")
.SetValidator(new EmployeeNameValidator());

Code snippet after applying EmployeeValidator

using FluentValidation;
using FluentValidation.Validators;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebApplication15.Models
{
    public class EmployeeValidator : AbstractValidator<Employee>
    {
        public EmployeeValidator()
        {

            RuleFor(x => x.Name).NotNull().WithMessage("Please Enter 'Name'")
            .SetValidator(new EmployeeNameValidator());

            RuleFor(x => x.SelectedCountry).NotNull()
                .WithMessage("Please Select Country");

            RuleFor(x => x.MobileNo).NotNull()
              .WithMessage("Please Entire MobileNo");

        }
    }
}

After setting validator Let’s run and Test Validation is it working properly or not.

Debug View while executing EmployeeNameValidator validation.

In this part, we have learned how to write Custom Fluent Validation in ASP.NET CORE.

By