Welcome to RewaTechForge.com. Feel free to have a look around and leave a comment.

Meta
Validation cover

Validation Overview

In this comprehensive series, we’re diving deep into ASP.NET validation, covering everything you need to know to safeguard your web applications. Bookmark this page because we’re turning these blog posts into your ultimate resource for all things ASP.NET validation. A lot of the contents in this post can be learnt from just asking your favourite GPT app the right question. This series seeks to make provide you the big picture so you can ask the right questions.

After each post, we encourage you to share your thoughts and suggest topics you’d like us to cover in future discussions.

Validation is important to any web application, ensuring that user-entered data is precise, secure, and meets predefined standards. It acts as a gatekeeper, shielding your application from potential errors and enhancing the overall user experience.

Throughout this series, we’ll discuss the intricacies of both client-side and server-side validation. From exploring the functions of validation helpers to understanding the usage of data annotations, this post will provide you with a basic understanding of how implement robust validation mechanisms in your ASP.NET projects.

You’ll not only grasp the inner workings of ASP.NET validation but also feel empowered to apply these principles confidently to your own projects.

Prerequisites and Tools

First things first, make sure you have the following prerequisites and tools installed:

  • Basic understanding of C# programming language and ASP.NET Core MVC framework.
  • Visual Studio IDE or Visual Studio Code.
  • .NET Core SDK.
  • Basic knowledge of HTML, CSS, and JavaScript.

Validation in a typical asp.net core can occur in 2 different places: Client Side and Server Side.

Server-Side Validation

Custom validation attributes, or any validation logic implemented in the model or controller, typically occur during server-side validation, which happens after the form is submitted to the server.
Server-side validation is performed on the server, where the submitted data is processed and validated against the specified rules.

This type of validation ensures that all validation rules are enforced, even if client-side validation fails or is bypassed.

The reason why custom attributes validation checks happen at the server is because they are implemented as part of the server-side processing logic. When the form data is submitted to the server, ASP.NET Core MVC invokes the model binding process, which includes running validation checks on the model using the specified validation attributes. Any validation errors detected during this process are added to the ModelState, which is then used to render validation error messages in the view.

In summary, while client-side validation provides immediate feedback to users, server-side validation ensures that all validation rules are enforced and provides an additional layer of security and reliability. Custom validation attributes in ASP.NET Core MVC are designed to work seamlessly with server-side validation to enforce data integrity and maintain application consistency.

Client-Side Validation

On the other hand, Client-side validation plays a crucial role in enhancing the user experience by validating input data directly within the user’s browser before it’s submitted to the server. In other words, it prevents those annoying post backs to the server. This proactive approach utilizes default attributes like [Required] and [Range], among others, to trigger validation checks instantaneously.

Implemented using JavaScript, client-side validation offers immediate feedback to users, allowing them to correct errors on the spot without waiting for server responses. This real-time validation not only improves user satisfaction but also reduces the server load by minimizing unnecessary roundtrips for validation.

Furthermore, client-side validation contributes to the overall security of the application by filtering out invalid data before it reaches the server. By validating input fields locally, potential security vulnerabilities can be detected and mitigated early in the process, safeguarding the integrity of the application, and protecting against malicious attacks.

In summary, client-side validation is an integral component of web development, providing users with a seamless and responsive experience while optimizing server performance and enhancing security measures. Its ability to detect and address validation errors in real-time ensures a smoother user journey and a more robust application infrastructure.

How Model Validation Works

We will begin with Server-side validation but first let’s understand how validation in asp.net core generally work

Model Binding: When a form is submitted, ASP.NET Core MVC maps the form data to the corresponding properties of the model class specified in the action method parameter.

Model Validation: After model binding, ASP.NET Core MVC automatically validates the model instance using the validation rules specified in the model class. This validation process is based on data annotations, custom validation attributes, and other validation mechanisms applied to the model properties.

Populating Validation Errors: If any validation errors are detected during the validation process, ASP.NET Core MVC populates the ModelState dictionary with error messages associated with the invalid model properties. Each error message is associated with the name of the corresponding model property.

Displaying Validation Errors: In the view, ASP.NET Core MVC provides tag helpers like asp-validation-for and asp-validation-summary to display validation error messages. These tag helpers are automatically populated with error messages from the ModelState dictionary.

Here’s a breakdown of how validation errors are populated and displayed:

ModelState Dictionary: The ModelState dictionary contains entries for each model property, with keys corresponding to property names and values containing validation information.

Error Messages: If a property fails validation, an error message is added to the ModelState dictionary entry for that property. The error message is typically generated based on the validation attributes applied to the property in the model class.

Tag Helpers: In the view, tag helpers like asp-validation-for are used to display validation error messages for specific model properties. These tag helpers automatically retrieve error messages from the ModelState dictionary based on property names.

Validation Summary: The asp-validation-summary tag helper displays a summary of all validation errors at the top of the form. It aggregates error messages from the ModelState dictionary and displays them in a user-friendly format.

Let’s begin with a straightforward example of a Registration Form designed for customers on a website. The form will include fields for username, email, password. Our objective is to implement a basic server-side validation to verify that all required fields are completed. Additionally, we’ll utilize model binding to map the form data to a model on the server.

In the event of validation failure, we’ll populate the model with validation errors and present them to the user for feedback. This ensures that users are promptly informed of any errors in their input and allows them to rectify the issues accordingly.

In this example, we will not persist or store our information in the database but instead store it in a simple generic list. If you want to understand how to persist information to a database, you can click on my previous post here.

  1. Start Visual Studio 2022
  2. Click on Create a New Project
  3. In the Create A New Project Dialog, search for the Asp.Net Core Web App (Model-View-Controller
  4. In the Configure your new project dialog, enter a name for you project name and click on Next. I called my project CustomerRegistrationApp.
  5. Follow the prompt and click on Create

Add the Model

We will create a very simple Customer model with the basic validations. Right click on the Model Folder and add a new class called Customer. Define the following field for the customer.

USING SYSTEM.COMPONENTMODEL.DATAANNOTATIONS;

NAMESPACE CUSTOMERREGISTRATIONAPP.MODELS
{
	PUBLIC CLASS CUSTOMER
	{
        [KEY]
        PUBLIC INT32 ID { GET; SET; }
        [REQUIRED]
        PUBLIC STRING USERNAME { GET; SET; }
        [REQUIRED]
        PUBLIC STRING EMAILADDRESS { GET; SET;}
        [REQUIRED]
        PUBLIC STRING PASSWORD { GET; SET;}
    }
}

The above code defines a simple model class named Customer in ASP.NET, using C# and data annotations for validation and database configuration. The class contains the following properties and data annotations.

Data annotation are metadata classes include built-in validation attributes which are used to enforce rules for properties, such as ensuring values are present or conforming to a specific format and are present in the System.ComponentModel.DataAnnotations namespace

[Key] – This annotation indicates that the Id property is a primary key in the database table that corresponds to this model. This is essential for the Entity Framework to identify the unique identifier for each Customer record.

[Required] – This annotation is used on the Username, EmailAddress, and Password properties to indicate that these fields cannot be null. This means when creating or updating a Customer instance, values must be provided for these properties to pass validation; otherwise, the model will be considered invalid.

Add the Controller

We will now proceed to add a Controller. Right click on the Controller folder, point to Add and click on controller.

In the Add New Scaffolded Item dialog, select MVC Controller-with read/write actions and click on Add. In the next prompt, enter CustomerController as the name. This will create a customer class with methods for CRUD operation.

The controller provides CRUD functionality for managing customers in the application.

In the Controller, create a generic static list as shown below immediately after the CustomerController Class declaration:

USING CUSTOMERREGISTRATIONAPP.MODELS;
USING MICROSOFT.ASPNETCORE.HTTP;
USING MICROSOFT.ASPNETCORE.MVC;

NAMESPACE CUSTOMERREGISTRATIONAPP.CONTROLLERS {
  PUBLIC CLASS CUSTOMERCONTROLLER : CONTROLLER {
    PUBLIC ILIST<CUSTOMER> CUSTOMERS = NEW LIST<CUSTOMER>{
        NEW CUSTOMER(){ID = 1, USERNAME = "JOHNX",
                       EMAILADDRESS = "JOHNX@GMAIL.COM", PASSWORD = "JOHNRTY"},
        NEW CUSTOMER(){ID = 2, USERNAME = "MARKDOW",
                       EMAILADDRESS = "MARKORANGE@GMAIL.COM",
                       PASSWORD = "MARKDOWPDD"}};

Next, in the Index Action Method, Modify the return view statement by entering the Customers List as below:

// GET: CUSTOMERCONTROLLER
PUBLIC ACTIONRESULT INDEX() { RETURN VIEW(CUSTOMERS); }

Implement the Index View

Right click on the View method and select Add View. In the Add New Scaffolded Item, Select Razor View and click on Add. In the Add Razor View Prompt, select the List template, Select Customer in the Model Class option and click on Add.

We will first create a list page that displays the List of Customers that we have and subsequently create and add more customers.

Challenge: Do you want to try and add the delete method?

In the view page, edit the Table tag and remove all references for the password. We do not want to display the password.

Your Table tag should resemble the following:

<TABLE CLASS="TABLE">
    <THEAD>
        <TR>
            <TH>
                @HTML.DISPLAYNAMEFOR(MODEL => MODEL.ID)
            </TH>
            <TH>
                @HTML.DISPLAYNAMEFOR(MODEL => MODEL.USERNAME)
            </TH>
            <TH>
                @HTML.DISPLAYNAMEFOR(MODEL => MODEL.EMAILADDRESS)
            </TH>
          
            <TH></TH>
        </TR>
    </THEAD>
    <TBODY>
@FOREACH (VAR ITEM IN MODEL) {
        <TR>
            <TD>
                @HTML.DISPLAYFOR(MODELITEM => ITEM.ID)
            </TD>
            <TD>
                @HTML.DISPLAYFOR(MODELITEM => ITEM.USERNAME)
            </TD>
            <TD>
                @HTML.DISPLAYFOR(MODELITEM => ITEM.EMAILADDRESS)
            </TD>
           
            <TD>
                @HTML.ACTIONLINK("EDIT", "EDIT", NEW { /* ID=ITEM.PRIMARYKEY */ }) |
                @HTML.ACTIONLINK("DETAILS", "DETAILS", NEW { /* ID=ITEM.PRIMARYKEY */ }) |
                @HTML.ACTIONLINK("DELETE", "DELETE", NEW { /* ID=ITEM.PRIMARYKEY */ })
            </TD>
        </TR>
}
    </TBODY>
</TABLE>

Open the Layout Page in Views>>Shared and edit the global list by including an entry in the list as below: Now when you run the app, you can click on the Customer Link to navigate to the Customer page.

<HEADER>
        <NAV CLASS="NAVBAR NAVBAR-EXPAND-SM NAVBAR-TOGGLEABLE-SM NAVBAR-LIGHT BG-WHITE BORDER-BOTTOM BOX-SHADOW MB-3">
            <DIV CLASS="CONTAINER-FLUID">
                <A CLASS="NAVBAR-BRAND" ASP-AREA="" ASP-CONTROLLER="HOME" ASP-ACTION="INDEX">CUSTOMERREGISTRATIONAPP</A>
                <BUTTON CLASS="NAVBAR-TOGGLER" TYPE="BUTTON" DATA-BS-TOGGLE="COLLAPSE" DATA-BS-TARGET=".NAVBAR-COLLAPSE" ARIA-CONTROLS="NAVBARSUPPORTEDCONTENT"
                        ARIA-EXPANDED="FALSE" ARIA-LABEL="TOGGLE NAVIGATION">
                    <SPAN CLASS="NAVBAR-TOGGLER-ICON"></SPAN>
                </BUTTON>
                <DIV CLASS="NAVBAR-COLLAPSE COLLAPSE D-SM-INLINE-FLEX JUSTIFY-CONTENT-BETWEEN">
                    <UL CLASS="NAVBAR-NAV FLEX-GROW-1">
                        <LI CLASS="NAV-ITEM">
                            <A CLASS="NAV-LINK TEXT-DARK" ASP-AREA="" ASP-CONTROLLER="HOME" ASP-ACTION="INDEX">HOME</A>
                        </LI>
                        <LI CLASS="NAV-ITEM">
                            <A CLASS="NAV-LINK TEXT-DARK" ASP-AREA="" ASP-CONTROLLER="HOME" ASP-ACTION="PRIVACY">PRIVACY</A>
                        </LI>
                        <LI CLASS="NAV-ITEM">
                            <A CLASS="NAV-LINK TEXT-DARK" ASP-AREA="" ASP-CONTROLLER="CUSTOMER" ASP-ACTION="INDEX">CUSTOMER</A>
                        </LI>
                    </UL>
                </DIV>
            </DIV>
        </NAV>
    </HEADER>

Run the app and make sure that you can get to the Customer Page.

Implement the Create View

Next, we will implement the Create New Link where we will learn about Validation.

Model Binding and Model Validation in Action

Open the controller tab and look for the Create action method. Notice that this method takes an input of an IFormcollection. With this parameter, there will be no way to trigger validation using the annotations set in our Model. In order to trigger the validation, we will need to change the parameters to reflect our model which in this case is Customer.

Remember that when the form is submitted, ASP.NET Core MVC maps the form data to the corresponding properties of the model class specified in the action method parameter. ASP.NET Core MVC automatically validates the model instance using the validation rules specified in the model class.

// POST: CUSTOMERCONTROLLER/CREATE
[HTTPPOST]
[VALIDATEANTIFORGERYTOKEN] PUBLIC ACTIONRESULT
CREATE(IFORMCOLLECTION COLLECTION) {
 TRY { RETURN REDIRECTTOACTION(NAMEOF(INDEX)); }
 CATCH { RETURN VIEW(); }
}

ModelState.isValid

We will write an If statement to check if the Model state dictionary contains errors. Note that the ModelState dictionary contains entries for each model property, with keys corresponding to property names and values containing validation information. The ModelState dictionary is a base property of the controller class. Your resulting code should look like the following.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Customer NewCustomer) {
 try {
  if (ModelState.IsValid) {
   Customers.Add(NewCustomer);
   return View("Index", Customers);
  }

  else {
   return View();
  }

 } catch {
  return View();
 }
}

In the true part, we simply added the NewCustomer Parameter to the list, set the updated list and return the view.

Right click on the Create Action Method to create the view. Follow the same process for creating the Index view. However, this time, select Create as the template in the Add Razor View prompt.

Run the app and test by entering information for the Id, Username, EmailAddress and Password respectively. Confirm that your entry is successful by checking that your entry exists in the list.

Now click on the Create button without entering any information, you will notice some random errors next to each field similar to below

Create form with default validation errors

ASP.NET Core MVC has automatically mapped the form data to the corresponding properties of the model class specified in the action method parameter, validated the model using the rules specified in in the model class and displayed the errors using tag helpers like asp-validation-for and asp-validation-summary that have been populated from ModelState dictionary.

You will learn about tag helpers in our next post. For now let’s inspect the ModelState dictionary and retrieve the errors after postback.

ModelState Structure

Comment out or remove the _ValidationScriptsPartial partial view render from the script section. This will allow us post back the code

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

We will now add code to retrieve the errors that were generated during model bindings in the false part of the ModelState.IsValid conditional statement. Your Create action method should now look like below

public ActionResult Create(Customer NewCustomer) {
 try {
  if (ModelState.IsValid) {
   Customers.Add(NewCustomer);
   return View("Index", Customers);
  }

  else {
   var errorList = new List<string>();

   var modelErrors = ModelState.Values.Where(e => e.Errors.Any()).ToList();

   foreach (var entry in ModelState) {
    // Check each entry for errors
    if (entry.Value.Errors.Count > 0) {
     // Each entry can have multiple errors
     foreach (var error in entry.Value.Errors) {
      // Append error description to the StringBuilder
      errorList.Add($"{entry.Key}: {error.ErrorMessage}");
     }
    }
   }

   ViewData["ModelErrors"] = errorList;
   return View();
  }

 } catch {
  return View();
 }
}

The false part of the model.isvalid condition demonstrates how to collect and handle model validation errors in an ASP.NET MVC application. It retrieves error messages from the ModelState object and then present these messages to the user via ViewData. This approach is helpful for debugging and user feedback in scenarios where data submitted by the user fails validation checks. We created a list of strings named errorList. This list is intended to store the error messages from the ModelState.

Structure of modelstate object

The ForEach loop iterates through each entry in the ModelState. Each entry represents a key-value pair where the key is the name of the model property and the value is a ModelStateEntry class containing the value submitted and any associated errors as a ModelErrorCollection. Inside the loop, there’s a check to see if the current ModelStateEntry has any errors using entry.Value.Errors.Count > 0. For entries with errors, a nested loop iterates through each error. The error messages are formatted with the key (property name) and the error message and added to errorList.

To view the error, Update the Create view with the following code. Paste it just above the div containing the link for going back to the index page.

@if(ModelErrors != null && ModelErrors.Any()) {
 <p><h4> Errors</ h4></ p> < table class
 = "table" > <thead><tr><th> Error</ th></ tr></ thead>
   <tbody> @ foreach (var item in ModelErrors){<tr><td> @item</ td>
                                               </ tr>}</ tbody></ table>
}

Run the application and click on the submit button without entering any values, you will get a table of errors as the image.

Customer Create Form

Other Useful Methods of ModelState

Adding Errors: You can manually add errors to the ModelState using methods like AddModelError. This is useful for adding custom validation logic that isn’t covered by your data annotations or custom validators.

ModelState.AddModelError("PropertyName", "Error message");

Removing Entries: To clear entries or remove specific entries (e.g., if you want to ignore errors under certain conditions), you can use Remove or Clear methods.

ModelState.Remove("PropertyName"); // Removes a specific property
ModelState.Clear(); // Clears all entries

Conclusion

In conclusion, as we started our conversation about ASP.NET validation, building the basic block for understanding both client-side and server-side validation. This understanding is paramount for enhancing security, improving user experience, and ensuring data integrity in your web applications. In this blog series, it is our aim to equip you with the knowledge and tools necessary to implement robust validation mechanisms effectively within your ASP.NET projects or position yourself to prompt ChatGPT efficiently and effectively in the very least.

Through the examples and discussions presented, we’ve only scratched the surface of what’s possible with ASP.NET validation. We encourage you to experiment with these concepts and integrate them into your own projects.

We value your input and would love to hear your thoughts on this post. Feel free to comment below with feedback or suggest topics that you’d like us to cover in future posts. Your insights and experiences are crucial in shaping our content and making this series a comprehensive resource for all developers.

If you’re interested in the source code for the examples discussed or have specific questions about implementation, do not hesitate to signify your interest in the comments. We’re here to help and provide additional resources to further your understanding.

In our next post, where we will talk about data annotation, an essential aspect of model validation in ASP.NET.

By Admin

2 thoughts on “Understanding ASP.NET Validation: Your Comprehensive Guide to Securing Web Applications”

Leave a Reply

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