Fluent API Validation Examples in ASP.NET Core MVC

Fluent API Validation Examples in ASP.NET Core MVC

In this article, I will discuss Different Types of Fluent API Validation Examples in ASP.NET Core MVC Applications. Please read our previous article discussing the basic concepts of Fluent API Validations in ASP.NET Core MVC. Please read our previous article, in which we discussed installing the Fluent API Package and configuring it to use Fluent Validation on both the Client and Server Sides. At the end of this article, you will understand examples using the following Validators.

  1. Fluent API Basic Validators Examples
  2. Fluent API Number Validators Examples
  3. String Validators in ASP.NET Core MVC
  4. Fluent API Date and Time Validators
  5. Conditional Validation using Fluent API in ASP.NET Core MVC
  6. Fluent API Collections Validator
  7. Fluent API Other Validators in ASP.NET Core MVC

Basic Fluent API Validators Example in ASP.NET Core MVC:

FluentValidation in ASP.NET Core MVC offers many built-in validators. The following are some of the most common basic Fluent API validators.

  • NotNull() / NotEmpty(): Check the property is not null or empty.
  • Equal(): Checks if the property equals another value or property.
  • NotEqual(): Checks the property is not equal to another value or property.
  • Length(): Checks the length of a string property that falls within the given min and max range.
  • Matches(): Checks the property matches a given regex pattern.

Let us see one real-time example to understand Fluent API NotNull(), NotEmpty(), Equal(), NotEqual(), Length(), and Matches() methods in ASP.NET Core MVC Application.

Define the Model:

Let’s assume we have the Registration model.

namespace FluentAPIDemo.Models
{
    public class RegistrationModel
    {
        public string? Username { get; set; }
        public string? Password { get; set; }
        public string? ConfirmPassword { get; set; }
        public string? Email { get; set; }
    }
}
Set Up the Validator:

Now, let’s define a validator for this model.

using FluentValidation;
namespace FluentAPIDemo.Models
{
    public class RegistrationValidator : AbstractValidator<RegistrationModel>
    {
        public RegistrationValidator()
        {
            RuleFor(r => r.Username)
            .NotNull().WithMessage("Username is Required")
            .NotEmpty().WithMessage("Username cannot be Empty")
            .Length(5, 30).WithMessage("Username must be between 5 and 30 characters");

            RuleFor(r => r.Password)
                .NotNull().WithMessage("Password is Required")
                .NotEmpty().WithMessage("Password cannot be Empty")
                .Length(6, 50).WithMessage("Password must be between 6 and 50 Characters.")
                .Matches("(?!^[0-9]*$)(?!^[a-zA-Z]*$)^([a-zA-Z0-9]{2,})$").WithMessage("Password can only contain alphanumeric characters");

            RuleFor(r => r.ConfirmPassword)
                .Equal(r => r.Password).WithMessage("Confirm Password must match Password");

            RuleFor(r => r.Email)
                .NotEmpty().WithMessage("Email is required")
                .EmailAddress().WithMessage("Invalid Email Format")
                .NotEqual("admin@example.com").WithMessage("This Email is Not Allowed");
        }
    }
}
Registering the Validator:

Add the following code to the Program class to register Fluent API Validation, the Model, and the corresponding validator.

//Enables integration between FluentValidation and ASP.NET MVC's automatic validation pipeline.
builder.Services.AddFluentValidationAutoValidation();

//Enables integration between FluentValidation and ASP.NET client-side validation.
builder.Services.AddFluentValidationClientsideAdapters();

//Registering Model and Validator to show the error message on client-side
builder.Services.AddTransient<IValidator<RegistrationModel>, RegistrationValidator>();
Using the Validator in ASP.NET Core MVC Actions:

Modify the Home Controller as follows:

using FluentAPIDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace FluentAPIDemo.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Register()
        {
            return View();
        }

        [HttpPost]
        public IActionResult Register(RegistrationModel model)
        {
            if (!ModelState.IsValid)
            {
                // Validation failed, return to the form with errors
                return View(model);
            }

            // Handle successful validation logic here
            return RedirectToAction("Success");
        }

        public string Success()
        {
            return "Registration Successful";
        }
    }
}
Displaying Errors in Views:

You can use the tag helpers to bind and display errors. Add Register.cshtml view and copy and paste the following code.

@model FluentAPIDemo.Models.RegistrationModel

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

<h1>Register User</h1>

<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Register">
            <div class="form-group mt-3">
                <label asp-for="Username" class="control-label"></label>
                <input asp-for="Username" class="form-control" />
                <span asp-validation-for="Username" class="text-danger"></span>
            </div>
            <div class="form-group mt-3">
                <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 mt-3">
                <label asp-for="Password" class="control-label"></label>
                <input asp-for="Password" class="form-control" />
                <span asp-validation-for="Password" class="text-danger"></span>
            </div>

            <div class="form-group mt-3">
                <label asp-for="ConfirmPassword" class="control-label"></label>
                <input asp-for="ConfirmPassword" class="form-control" />
                <span asp-validation-for="ConfirmPassword" class="text-danger"></span>
            </div>
            <div class="form-group mt-3">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>
@section Scripts {
    @{
        await Html.RenderPartialAsync("_ValidationScriptsPartial");
    }
}

With the above changes, run the application, and you should get the validation error message as expected, as shown in the image below.

Basic Fluent API Validators Example in ASP.NET Core MVC

Fluent API Number Validators Example in ASP.NET Core MVC:

The Fluent API provides the following Number Validators:

  • InclusiveBetween(): Checks the property value falls between or equals to the specified limits.
  • ExclusiveBetween(): Checks the property value falls between but not equal to the specified limits.
  • LessThan() / LessThanOrEqualTo(): For comparison with another value.
  • GreaterThan() / GreaterThanOrEqualTo(): Same as above, but for greater values.

Let’s look at an example to understand the above Fluent API Number Validator methods in the ASP.NET Core MVC application.

Define the Model:

Let’s assume we have the following Product model with properties like Price, Discount, and StockCount.

namespace FluentAPIDemo.Models
{
    public class Product
    {
        public decimal Price { get; set; }
        public decimal Discount { get; set; } // Let's assume this is a percentage value.
        public int StockCount { get; set; }
    }
}
Set Up the Validator:

Now, let’s define a validator for this model:

using FluentValidation;
namespace FluentAPIDemo.Models
{
    public class ProductValidator : AbstractValidator<Product>
    {
        public ProductValidator()
        {
            // Validation rule for Price
            RuleFor(p => p.Price)
                .GreaterThan(0).WithMessage("Price must be greater than 0.")
                .LessThan(10000).WithMessage("Price must be less than 10000.");

            // Validation rule for Discount
            RuleFor(p => p.Discount)
                .InclusiveBetween(0, 100).WithMessage("Discount must be between 0% and 100% inclusive.");

            // Validation rule for StockCount
            RuleFor(p => p.StockCount)
                .GreaterThanOrEqualTo(0).WithMessage("Stock count shoud be between 1 and 500")
                .LessThanOrEqualTo(500).WithMessage("Stock count shoud be between 1 and 500");
        }
    }
}
In this Validator:
  • GreaterThan, LessThan, GreaterThanOrEqualTo, and LessThanOrEqualTo set specific bounds on numeric values.
  • InclusiveBetween ensures the value lies within (and including) the provided range.
  • ExclusiveBetween would ensure the value lies within the range but excluding the boundaries (this method is not used in the above example, but you could replace InclusiveBetween with it for a different behavior).
Registering the Validator:

Add the following code in the Program class to register the Model and corresponding validator.

//Registering Model and Validator to show the error message on the client side
builder.Services.AddTransient<IValidator<Product>, ProductValidator>();
Using the Validator in ASP.NET Core MVC Actions:

Modify the Home Controller as follows:

using FluentAPIDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace FluentAPIDemo.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult AddProduct()
        {
            return View();
        }

        [HttpPost]
        public IActionResult AddProduct(Product product)
        {
            if (!ModelState.IsValid)
            {
                // Validation failed, return to the form with errors
                return View(product);
            }

            // Handle successful validation logic here
            return RedirectToAction("Success");
        }

        public string Success()
        {
            return "Registration Successful";
        }
    }
}
Displaying Errors in Views:

You can use the tag helpers to bind and display errors. Add AddProduct.cshtml view and copy and paste the following code.

@{
    ViewData["Title"] = "Add Product";
}

<h1>Add Product</h1>

<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="AddProduct">
            <div class="form-group mt-3">
                <label asp-for="Price" class="control-label"></label>
                <input asp-for="Price" class="form-control" />
                <span asp-validation-for="Price" class="text-danger"></span>
            </div>
            <div class="form-group mt-3">
                <label asp-for="Discount" class="control-label"></label>
                <input asp-for="Discount" class="form-control" />
                <span asp-validation-for="Discount" class="text-danger"></span>
            </div>
            <div class="form-group mt-3">
                <label asp-for="StockCount" class="control-label"></label>
                <input asp-for="StockCount" class="form-control" />
                <span asp-validation-for="StockCount" class="text-danger"></span>
            </div>

            <div class="form-group mt-3">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

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

Now, run the application, and you should see the proper validation messages in the image below.

Fluent API Number Validators Example in ASP.NET Core MVC

String Fluent API Validators in ASP.NET Core MVC:

FluentValidation offers a range of string-specific validation methods used in ASP.NET Core MVC applications. They are as follows:

  • EmailAddress(): Ensures that the string is a valid email address format.
  • CreditCard(): Validates the string as a credit card number.
  • PrecisionScale: Defines a scale precision validator on the current rule builder that ensures a decimal of the specified precision and scale.
  • Matches: Ensures the property matches a specified regex pattern.

Let us see one example of understanding the string Fluent API Validation methods using a PaymentInfo model.

Define the Model:

Let’s assume we have the following PaymentInfo model, which has properties like CardHolderName, CardNumber, BillingEmail, and Amount. We will use this model to capture a user’s payment details.

namespace FluentAPIDemo.Models
{
    public class PaymentInfo
    {
        public string? CardHolderName { get; set; }
        public string? CardNumber { get; set; }
        public string? BillingEmail { get; set; }
        public decimal Amount { get; set; }
    }
}
Set Up the Validator:

Now, let’s define a validator for this model:

using FluentValidation;
namespace FluentAPIDemo.Models
{
    public class PaymentInfoValidator : AbstractValidator<PaymentInfo>
    {
        public PaymentInfoValidator()
        {
            //EmailAddress: Ensures that the string is a valid email address format
            RuleFor(p => p.BillingEmail)
                .EmailAddress().WithMessage("Please enter a valid email address.");

            //CreditCard: Validates the string as a credit card number.
            RuleFor(p => p.CardNumber)
                .CreditCard()
                .WithMessage("Please enter a valid credit card number.");

            //PrecisionScale: Checks the number of allowable decimal places and the overall precision of a number.
            RuleFor(p => p.Amount)
                .PrecisionScale(8, 2, true)
                .WithMessage("Amount can have up to 2 decimal places and up to 8 total digits.");

            //Matches: Ensures the property matches a specified regex pattern. 
            //We want the CardHolderName to only contain letters and spaces:
            RuleFor(p => p.CardHolderName).Matches(@"^[a-zA-Z\s]+$").WithMessage("Name can only contain letters and spaces.");
        }
    }
}
Registering the Validator:

Add the following code in the Program class to register the Model and corresponding validator.

//Registering Model and Validator to show the error message on the client side
builder.Services.AddTransient<IValidator<PaymentInfo>, PaymentInfoValidator>();
Using the Validator in ASP.NET Core MVC Actions:

Modify the Home Controller as follows:

using FluentAPIDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace FluentAPIDemo.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult ProcessPayment()
        {
            return View();
        }

        [HttpPost]
        public IActionResult ProcessPayment(PaymentInfo paymentInfo)
        {
            if (!ModelState.IsValid)
            {
                return View(paymentInfo); // Return with validation errors.
            }

            // Process payment logic...
            return RedirectToAction("Success");
        }

        public string Success()
        {
            return "Registration Successful";
        }
    }
}
Displaying Errors in Views:

You can use the tag helpers to bind and display errors. Add ProcessPayment.cshtml view and copy and paste the following code.

@model FluentAPIDemo.Models.PaymentInfo

@{
    ViewData["Title"] = "Process Payment";
}

<h1>Process Payment</h1>

<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="ProcessPayment">
            @* <div asp-validation-summary="All" class="text-danger"></div> *@
            <div class="form-group mt-3">
                <label asp-for="CardNumber" class="control-label"></label>
                <input asp-for="CardNumber" class="form-control" />
                <span asp-validation-for="CardNumber" class="text-danger"></span>
            </div>
            <div class="form-group mt-3">
                <label asp-for="CardHolderName" class="control-label"></label>
                <input asp-for="CardHolderName" class="form-control" />
                <span asp-validation-for="CardHolderName" class="text-danger"></span>
            </div>
            <div class="form-group mt-3">
                <label asp-for="Amount" class="control-label"></label>
                <input asp-for="Amount" class="form-control" />
                <span asp-validation-for="Amount" class="text-danger"></span>
            </div>
            <div class="form-group mt-3">
                <label asp-for="BillingEmail" class="control-label"></label>
                <input asp-for="BillingEmail" class="form-control" />
                <span asp-validation-for="BillingEmail" class="text-danger"></span>
            </div>

            <div class="form-group mt-3">
                <input type="submit" value="Process Payment" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

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

Now, run the application, and you should see the proper validation messages as expected, as shown in the below image.

Fluent API DateTime Validators in ASP.NET Core MVC:

Let’s understand Fluent API DateTime Validations in the ASP.NET Core MVC application context. We can use the following validator methods to validate the date and time.

  • Must: For more complex scenarios. For instance, to ensure the date is not a weekend
  • InclusiveBetween(): This can also be used for DateTime ranges.
  • LessThan() / LessThanOrEqualTo(): This can also be used for DateTime values.
  • GreaterThan() / GreaterThanOrEqualTo(): Same as above, but for future DateTime values.
Define the Model:

Let’s say we have an event scheduling application. Our model Event will have EventDate, as shown below.

namespace FluentAPIDemo.Models
{
    public class Event
    {
        public DateTime EventDate { get; set; }
    }
}
Set Up the Validator:

Now, let’s define a validator for this model:

using FluentValidation;
namespace FluentAPIDemo.Models
{
    public class EventValidator : AbstractValidator<Event>
    {
        public EventValidator()
        {
            // Ensure the event date is in the future
            RuleFor(e => e.EventDate)
                .GreaterThan(DateTime.Now).WithMessage("Event Date must be in the future");

            // For demonstration, let's assume we want events only within the next 30 days
            RuleFor(e => e.EventDate)
                .LessThan(DateTime.Now.AddDays(30)).WithMessage("Event Date must be within the next 30 days");

            // Ensure the event date is not on a weekend
            RuleFor(e => e.EventDate)
                .Must(date => date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday)
                .WithMessage("Events on weekends are not allowed");

            // InclusiveBetween example, ensuring date is within a specific range
            //RuleFor(e => e.EventDate)
            //    .InclusiveBetween(DateTime.Now, DateTime.Now.AddMonths(1))
            //    .WithMessage("Event Date must be within the next month");

        }
    }
}
Registering the Validator:

Add the following code in the Program class to register the Model and corresponding validator.

//Registering Model and Validator to show the error message on the client side
builder.Services.AddTransient<IValidator<Event>, EventValidator>();
Using the Validator in ASP.NET Core MVC Actions:

Modify the Home Controller as follows:

using FluentAPIDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace FluentAPIDemo.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult ScheduleEvent()
        {
            return View();
        }

        [HttpPost]
        public IActionResult ScheduleEvent(Event eventModel)
        {
            if (!ModelState.IsValid)
            {
                return View(eventModel); // Return with validation errors.
            }

            // Proceed with the event scheduling...
            return RedirectToAction("Success");
        }

        public string Success()
        {
            return "Registration Successful";
        }
    }
}
Displaying Errors in Views:

You can use the tag helpers to bind and display errors. Add ScheduleEvent.cshtml view and copy and paste the following code.

@model FluentAPIDemo.Models.Event
@{
    ViewData["Title"] = "ScheduleEvent";
}

<h1>Schedule Event</h1>

<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="ScheduleEvent">
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="EventDate" class="control-label"></label>
                <input asp-for="EventDate" type="date" class="form-control" />
                <span asp-validation-for="EventDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Schedule Event" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

Now, run the application, and you should see the proper validation messages.

Conditional Validation using Fluent API in ASP.NET Core MVC:

Conditional Validation is one of FluentValidation’s powerful features. It allows us to perform validation rules based on certain conditions. This can be achieved using the When() and Unless() methods. The When and Unless methods in FluentValidation offer conditional validation. They allow you to specify under which conditions a particular validation rule should be executed.

  • When(): Apply rules conditionally based on properties or external factors.
  • Unless(): The opposite of When(), the rule is applied unless the condition is true.

Imagine an e-commerce application where users can register and, while creating an account, provide valid phone numbers to receive promotional offers.

Define the Model:

Here’s the Registration model:

namespace FluentAPIDemo.Models
{
    public class Registration
    {
        public bool WantsPromotions { get; set; }
        public string? PhoneNumber { get; set; }
    }
}
Set Up the Validator:

Now, let’s define a validator for this model. We want the Phone Number only mandatory if the WantsPromotions is “true.”

using FluentValidation;
namespace FluentAPIDemo.Models
{
    public class RegistrationValidator : AbstractValidator<Registration>
    {
        public RegistrationValidator()
        {
            RuleFor(r => r.PhoneNumber)
                .NotEmpty().WithMessage("Phone number is required if you want promotions.")
                .When(r => r.WantsPromotions);  // Conditionally applies the rule if WantsPromotions is true

            RuleFor(r => r.PhoneNumber)
                .Must(p => p.StartsWith("+") && p.Length > 10)
                .WithMessage("Phone number should start with '+' and be longer than 10 digits.")
                .Unless(r => string.IsNullOrEmpty(r.PhoneNumber));  // Applies the rule unless the condition is true (i.e., phone number is empty or null)
        }
    }
}

In this example:

  • The NotEmpty() validation for PhoneNumber only applies when WantsPromotions is true.
  • The Must() validation for PhoneNumber applies unless the phone number is null or empty.
Registering the Validator:

Add the following code in the Program class to register the Model and corresponding validator.

//Registering Model and Validator to show the error message on the client side
builder.Services.AddTransient<IValidator<Registration>, RegistrationValidator>();
Using the Validator in ASP.NET Core MVC Actions:

Modify the Home Controller as follows:

using FluentAPIDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace FluentAPIDemo.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Register()
        {
            return View();
        }

        [HttpPost]
        public IActionResult Register(Registration registration)
        {
            if (!ModelState.IsValid)
            {
                return View(registration); // Return with validation errors.
            }

            // Proceed with registration...
            return RedirectToAction("Success");
        }

        public string Success()
        {
            return "Registration Successful";
        }
    }
}
Displaying Errors in Views:

You can use the tag helpers to bind and display errors. Add Register.cshtml view and copy and paste the following code.

@model FluentAPIDemo.Models.Registration
@{
    ViewData["Title"] = "Register";
}

<h1>Register</h1>

<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Register">
            <div asp-validation-summary="All" class="text-danger"></div>
            
            <div class="form-group">
                <label asp-for="WantsPromotions">I want promotions</label>
                <input asp-for="WantsPromotions" type="checkbox" />
            </div>
            <div class="form-group">
                <label asp-for="PhoneNumber" class="control-label"></label>
                <input asp-for="PhoneNumber" class="form-control" />
                <span asp-validation-for="PhoneNumber" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Register" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

Now, run the application, and you should see the proper validation messages.

Fluent API Collections Validator in ASP.NET Core MVC:

Fluent API Provides Must and ForEach methods to validate collections and their individual items in ASP.NET Core MVC. The Must method in FluentValidation offers custom validation logic, while ForEach can be employed to validate individual items within a collection

  • Must(): Allows for more complex custom logic.
  • ForEach(): Apply a rule to each element in a collection.

Let’s say we have a Cart model representing a shopping cart containing a list of Product items:

Define the Model:

Let’s define a model for Book and Order:

namespace FluentAPIDemo.Models
{
    public class Product
    {
        public string? Name { get; set; }
        public int Quantity { get; set; }
    }

    public class Cart
    {
        public List<Product> Products { get; set; } = new List<Product>();
    }
}
Set Up the Validators:
Product Validator:

Firstly, define a validator for individual Product items:

using FluentValidation;
namespace FluentAPIDemo.Models
{
    public class ProductValidator : AbstractValidator<Product>
    {
        public ProductValidator()
        {
            RuleFor(p => p.Name).NotEmpty().WithMessage("Product name is required.");

            RuleFor(p => p.Quantity)
                .GreaterThan(0)
                .WithMessage("Product quantity must be greater than 0.");
        }
    }
}
Cart Validator:

Now, define a validator for the Cart, applying the ProductValidator to each item in the Products collection and ensuring the cart contains at least one product:

using FluentValidation;
namespace FluentAPIDemo.Models
{
    public class CartValidator : AbstractValidator<Cart>
    {
        public CartValidator()
        {
            RuleFor(c => c.Products)
                .NotEmpty()
                .WithMessage("Cart must contain at least one product.");

            RuleForEach(c => c.Products).SetValidator(new ProductValidator());

            RuleFor(c => c.Products)
                .Must(ContainUniqueProducts)
                .WithMessage("Products in the cart must be unique.");
        }

        private bool ContainUniqueProducts(List<Product> products)
        {
            var uniqueProductNames = new HashSet<string>();
            foreach (var product in products)
            {
                if (!uniqueProductNames.Add(product.Name))
                    return false;
            }
            return true;
        }
    }
}

In this CartValidator, we have a Must validator that ensures all products in the cart are unique, along with ForEach applying a ProductValidator to each product in the cart.

Registering the Validator:

Add the following code with the Program class to register Fluent API Validation and the Model and corresponding validator.

//Registering Model and Validator to show the error message on the client side
builder.Services.AddTransient<IValidator<Cart>, CartValidator>();
Using the Validator in ASP.NET Core MVC Actions:

Modify the Home Controller as follows:

using FluentAPIDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace FluentAPIDemo.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Checkout()
        {
            Cart cart = new Cart();
            List<Product> products = new List<Product>()
            {
                new Product(){Name = "Product1", Quantity=1},
                new Product(){Name = "Product2", Quantity=2},
                new Product(){Name = "Product2", Quantity=5},
                new Product(){Name = "Product3", Quantity=0},
            };
            cart.Products = products;
            return View(cart);
        }

        [HttpPost]
        public IActionResult Checkout(Cart cart)
        {
            if (!ModelState.IsValid)
            {
                return View(cart); // Return with validation errors.
            }

            // Continue with order placement...
            return RedirectToAction("Success");
        }

        public string Success()
        {
            return "Registration Successful";
        }
    }
}
Displaying Errors in Views:

You can use the tag helpers to bind and display errors. Add Checkout.cshtml view and copy and paste the following code.

@model FluentAPIDemo.Models.Cart
@{
    ViewData["Title"] = "Checkout";
}

<h1>Checkout</h1>

<form asp-action="Checkout" asp-controller="Home" method="post">
    <div asp-validation-summary="All" class="text-danger"></div>
    @for (int i = 0; i < Model.Products.Count; i++)
    {
        <!-- Product Name -->
        <label asp-for="@Model.Products[i].Name">Product Name</label>
        <input asp-for="@Model.Products[i].Name" />
        <span asp-validation-for="@Model.Products[i].Name" class="text-danger"></span>

        <!-- Quantity -->
        <label asp-for="@Model.Products[i].Quantity">Quantity</label>
        <input asp-for="@Model.Products[i].Quantity" type="number" />
        <span asp-validation-for="@Model.Products[i].Quantity" class="text-danger"></span>

        <br/>
    }
    <input type="submit" value="CheckOut" class="btn btn-primary" />
</form>

Now, run the application, and you should see the proper validation messages.

Fluent API Other Validators in ASP.NET Core MVC:

Let us understand the following Fluent API Validator Methods.

  • CascadeMode: CascadeMode Determines how the validator should continue its validation process once a validation rule has failed.
  • WithMessage(): WithMessage allows customizing the error message for a specific validation rule.
  • WithName(): WithName is used to specify a custom property name within error messages. This is especially useful if you want your error messages to display a friendlier or more descriptive property name than the actual property.
  • DependentRules(): Specifies that several rules should be grouped together as dependent rules. That means DependentRules allows you to define additional rules that will only be executed if the main rule passes.
Define the Model:

Consider the following UserRegistration model.

namespace FluentAPIDemo.Models
{
    public class UserRegistration
    {
        public string Email { get; set; }
        public string Password { get; set; }
        public string ConfirmPassword { get; set; }
    }
}
Set Up the Validator:

Now, let’s define a validator for this model:

using FluentValidation;
namespace FluentAPIDemo.Models
{
    public class UserRegistrationValidator : AbstractValidator<UserRegistration>
    {
        public UserRegistrationValidator()
        {
            //CascadeMode Determines how the validator should continue its validation process
            //once a validation rule has failed.
            //CascadeMode.Stop: Stop on the first failure
            //CascadeMode.Continue: Execution continues to the next rule/validator.
            //In the following validator, if Email is null,
            //then the EmailAddress validation won't even be checked because of the CascadeMode.Stop.
            ClassLevelCascadeMode = CascadeMode.Stop;

            //WithMessage(): Allows customizing the error message for a specific validation rule.
            RuleFor(x => x.Password)
                .NotNull()
                .MinimumLength(6).WithMessage("Password must be at least 6 characters long.");

            //WithName(): Overrides the display name of a property.
            //When a validation error occurs on the Email property,
            //the error message will refer to the property as "User Email Address".
            RuleFor(x => x.Email)
                .NotNull()
                .EmailAddress()
                .WithName("User Email Address");

            //In the following rule, the validator checks if ConfirmPassword is equal to Password.
            //If it is, the dependent rule for Password being not empty is checked.
            RuleFor(x => x.ConfirmPassword)
                .Equal(x => x.Password).WithMessage("Passwords must match.")
                .DependentRules(() =>
                {
                    RuleFor(x => x.Password).NotEmpty().WithMessage("Password cannot be empty when Confirm Password is provided.");
                });
        }
    }
}
Registering the Validator:

Add the following code with the Program class to register Fluent API Validation and the Model and corresponding validator.

//Registering Model and Validator to show the error message on the client side
builder.Services.AddTransient<IValidator<UserRegistration>, UserRegistrationValidator>();
Using the Validator in ASP.NET Core MVC Actions:

Modify the Home Controller as follows:

using FluentAPIDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace FluentAPIDemo.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Register()
        {
            return View();
        }

        [HttpPost]
        public IActionResult Register(UserRegistration registration)
        {
            if (!ModelState.IsValid)
            {
                return View(registration); // Return with validation errors.
            }

            // Continue with the registration process...
            return RedirectToAction("Success");
        }

        public string Success()
        {
            return "Registration Successful";
        }
    }
}
Displaying Errors in Views:

You can use the tag helpers to bind and display errors. Add Register.cshtml view and copy and paste the following code.

@model FluentAPIDemo.Models.UserRegistration
@{
    ViewData["Title"] = "Register";
}

<h1>User Register</h1>

<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Register" asp-controller="Home" method="post">
            <div asp-validation-summary="All" class="text-danger"></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="Password" class="control-label"></label>
                <input asp-for="Password" class="form-control" />
                <span asp-validation-for="Password" class="text-danger"></span>
            </div>

            <div class="form-group">
                <label asp-for="ConfirmPassword" class="control-label"></label>
                <input asp-for="ConfirmPassword" class="form-control" />
                <span asp-validation-for="ConfirmPassword" class="text-danger"></span>
            </div>

            <div class="form-group">
                <input type="submit" value="Register" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

With the above changes, run the application and check if everything works as expected.

In the next article, I will discuss Fluent API Async Validators in ASP.NET Core MVC Applications with Examples. In this article, I try to explain Fluent API Validation Examples in ASP.NET Core MVC with Examples. I hope you enjoy the Fluent API Validation Examples in the ASP.NET Core MVC Application article.

Leave a Reply

Your email address will not be published. Required fields are marked *