Click here to Skip to main content
Click here to Skip to main content
Articles » Web Development » Ajax » Samples » Revisions
 

Data Validation Using Annotations for jQuery Ajax Calls in MVC Applications

, 26 Jun 2011
Rate this:
Please Sign up or sign in to vote.
This article presents an example on how to use data annotations to validate the data received from jQuery Ajax calls and how to send the validation result as well as the html content from a partial view to the client in a Json object in MVC.
This is an old version of the currently published article.

Download DataAnnotationAjax.zip - 50.63 KB

Introduction

This article presents an example on how to use data annotations to validate the data received from jQuery Ajax calls and how to send the validation result as well as the html content from a partial view to the client in a Json object in MVC.

Background

When writing a web application that takes data entries, validation is always the topic. There are two ways to validate the user input, the client side validation and the server side validation.

  • Validating the data at the client side using Javascript can give the user an immediate feedback, but we may not always have all the information at the client side to completely validate the data.
  • Validating the data at the server side can take advantage of all the information available, which can normally give us the most trusted result. But we will need to send the data to the server and sometimes we may need to refresh the page.

To take advantage of the both types of validation methods, some web applications have both client side and server side validations. But placing the validation logic at two different places makes the code difficult to maintain. Besides, if not managed well, having two pieces of validation logic at two different places can easily lead to duplication of efforts.

This article represents an effort to bridge the two validation methods. The example uses server side validation, but it makes the effort to reduce the network traffic by using "jQuery" Ajax calls to send the data to the server and retrieve the result and other useful information from the server. At the server side, it utilizes the "Data Annotation" and "MVC Model Binding" to minimize the code that we need to write when validating the data. The client and the server exchange information by "Json" objects.

This article assumes that you have some basic knowledge on "jQuery", "Ajax", "MVC", "Data Annotation", and "MVC Model Binding". If you are new to these subjects, you can easily find many references over the internet.

The Visual Studio Solution

The attached Visual Studio 2010 Solution is a simple MVC 2 web application:

VSSolution.jpg

  • The "Model" of the application is implemented in the "Models/Student.cs" file.
  • The "View" of the application is implemented in the "Views/Home/Index.aspx" file. The "Student.ascx" file implements a "Partial View" that helps rendering the "Index.aspx" page.
  • The "Controller" of the application is implemented in the "Controllers/HomeController.cs" file.

I will introduce each of building blocks in detail in this article. Let me start with the "Models/Student.cs" file.

The Application Model

The application's model is implemented in the "Models/Student.cs" file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
 
namespace DataAnnotationAjax.Models
{
    public class Student
    {
        public int Id { get; set; }
 
        [Required(ErrorMessage = "Last name is required")]
        public string LastName { get; set; }
        [Required(ErrorMessage = "First name is required")]
        public string FirstName { get; set; }
        [Required(ErrorMessage = "Score is required to add the student"),
            Range(60, 100, ErrorMessage="Score should be between 60 - 100")]
        public int Score { get; set; }
 
        public DateTime Enrollment { get; set; }
    }
 
    public static class StudentRepository
    {
        private static int IdSeed = 1;
        private static readonly List<Student> Students = new List<Student>();
 
        // Initiate a student list in the static constructor
        static StudentRepository()
        {
            Random rand = new Random();
            for (int i = 0; i < 3; i++)
            {
                var student = new Student();
                int id = IdSeed++;
                student.Id = id;
                student.LastName = "Last Name " + id.ToString();
                student.FirstName = "First Name " + id.ToString();
                student.Score = (60 + Convert.ToInt16(rand.NextDouble() * 40));
                student.Enrollment = DateTime.Now;
                Students.Add(student);
            }
        }
 
        // Add a single student
        public static void AddStudent(Student student)
        {
            student.Id = IdSeed++;
            student.Enrollment = DateTime.Now;
            Students.Add(student);
        }
 
        // Get the list of students
        public static List<Student> GetStudent()
        {
            return Students;
        }
 
    }
}

For simplicity reason, I put two classes in the "Models/Student.cs" file.

  • The "Student" class is the application's data model. I put some "Data Annotations" on the "LastName", "FirstName", and "Score" properties. These annotations will be used for data validation purposes using "MVC Model Binding", as you will see later in the application's controller.
  • The "StudentRepository" class will serve as the application's data repository. In the "Static Constructor", three randomly generated students are added to the repository. When the application starts, these three students will be shown to the user. You can use the "AddStudent" method to add a new student to the repository, and you can use the "GetStudent" method to retrieve the list of the students.

The purpose of this web application is to demonstrate how we can validate the user entries based on the "Data Annotations" placed on the properties in the "Student" class cross "jQuery" Ajax calls.

The View Page

The view page of this MVC application is implemented in the "Views/Home/Index.aspx" file:

<%@ Page Language="C#"
    Inherits="System.Web.Mvc.ViewPage<IEnumerable<DataAnnotationAjax.Models.Student>>" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Data Annotation & Ajax</title>
    <link rel="stylesheet"
        href="<%: Url.Content("~/Content/Site.css") %>"
        type="text/css" />
 
    <script src='<%: Url.Content("~/Scripts/jquery-1.6.1.min.js") %>'
        type="text/javascript">
    </script>
 
    <script src='<%: Url.Content("~/Scripts/Home.Index.js") %>'
        type="text/javascript">
    </script>
 
<script type="text/javascript">
    var addStudentUrl = '<%: Url.Action("AddStudent", "Home") %>';
</script>
 
</head>
<body>
<div>
<table cellpadding="0px">
    <tr>
        <td>Last Name</td>
        <td><input type="text" id="LastName" /></td>
    </tr>
    <tr>
        <td></td><td colspan="2" id="Err_LastName" class="errormsg"></td>
    </tr>
    <tr>
        <td>First Name</td>
        <td><input type="text" id="FirstName" /></td>
    </tr>
    <tr>
        <td></td><td colspan="2" id="Err_FirstName" class="errormsg"></td>
    </tr>
    <tr>
        <td>Score</td>
        <td><input type="text" id="Score" /></td>
        <td>
            <button id="btnAddStudent">Add Student</button>
            <button id="btnClear">Clear</button>
        </td>
    </tr>
    <tr>
        <td></td><td colspan="2" id="Err_Score" class="errormsg"></td>
    </tr>
</table>
</div>
 
<div id="divStudent"><%Html.RenderPartial("Students"); %></div>
</body>
</html>

This "ASPX" page has the following visual elements:

  • Three text boxes to take user input. Each text box corresponds to one of the annotated property in the "Student" class.
  • The list of the existing students in the data repository is rendered by the partial view "Students.ascx".
  • The "Add Student" button will initiate a "jQuery" Ajax call to send the user input to the server to start the validation. I will take about this in details when I introduce the "Scripts/"Home.Index.js" file.
  • The "Clear" button clears the user input.

Before taking a look at the Javascript code in the "Scripts/Home.Index.js" file, let us first check out the "Partial View" "Views/Home/Students.ascx":

<%@ Control Language="C#"
    Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<DataAnnotationAjax.Models.Student>>" %>
 
<table cellpadding="8px" rules="all">
    <tr>
        <th>Id</th><th>Last Name</th><th>First Name</th><th>Score</th><th>Enrollment Time</th>
    </tr>
 
<% foreach (var item in Model) { %>
    <tr>
        <td><%: item.Id %></td>
        <td><%: item.LastName %></td>
        <td><%: item.FirstName %></td>
        <td><%: item.Score %></td>
        <td><%: item.Enrollment.ToString("MM/dd/yyyy") %></td>
    </tr>
<% } %>
</table>

This "Partial View" takes the list of the students and displays them in an html table. It is used in two places in this application.

  • It is embedded in the "Views/Home/Index.aspx" page by the "RenderPartial" method.
  • It will be used in the application's controller to send the html content of the list of the students to the web browser in a "Json" object. We will see how to generate the html string from a "Partial View" in the MVC controllers when I introduce the application's controller.

The client Javascript code for the "Views/Home/Index.aspx" page is implemented in the "Scripts/"Home.Index.js" file: 

$(document).ready(function () {
 
    // Set up the click event on the button to make
    // the ajax call to add the student.
    $("#btnAddStudent").click(function () {
 
        // collect the data to a Json object.
        var data = {
            LastName: $.trim($("#LastName").val()),
            FirstName: $.trim($("#FirstName").val()),
            Score: $.trim($("#Score").val())
        };
 
        $.ajax({
            cache: false,
            type: "POST",
            url: addStudentUrl,
            data: data,
            dataType: "json",
            success: function (data) {
                // There is no problem with the validation
                if (data.Valid) {
                    $("#divStudent").html(data.StudentsPartial);
                    $("input").val("");
                    return;
                }
 
                // Problem happend during the validation, display
                // the messages. The following script will display the last
                // message related to the field.
                $.each(data.Errors, function (key, value) {
                    if (value != null) {
                        $("#Err_" + key).html(value[value.length - 1].ErrorMessage);
                    }
                });
            },
            error: function (xhr) {
                alert(xhr.responseText);
                alert("Critical Error!. Failed to call the server.");
            }
        });
    });
 
    $("#btnClear").click(function () {
        $(".errormsg").html("");
        $("input").val("");
    });
 
    // Set up the change event to the textboxes, so when user
    // makes changes, clear the error messages associated to the textbox.
    $("input").keyup(function () {
        var $errorDiv = $("#Err_" + this.id);
        if ($errorDiv.html() != "") {
            $errorDiv.html("");
        }
    });
});

In the "$(document).ready" event, the Javascript code does the following:

  • Register the click event for the "Add Student" button to send the user input to the server. When the respond comes back, the UI will be adjusted accordingly.
  • Register the click event for the "Clear" button to clear the user input and possible error messages, if any.
  • Register the "keyup" event for the textboxes. When users making changes to a text boxes, if the text box has an associated error message, it will be cleared.

The "jQuery" Ajax call in the click event of the "Add Student" button is pretty standard. It posts the user input to the server and expects a "Json" object. The "Json" object received from the server has three variables.

  • The variable "Valid" indicates if the validation of the user input is successful.
  • If there are any problems during the validation, the variable "Errors" has the detailed error messages.
  • If the validation is successful, the variable "StudentsPartial" is the html string generated from the "Partial View" "Views/Home/Students.ascx" in the controller, which includes the newly added student.

Now let us take a look at how the controller. We will see how it validates the data and how it generates the expected "Json" object for the client.

The Controller 

The controller of this application is implemented in the "Controllers/HomeController.cs" file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using DataAnnotationAjax.Models;
using DataAnnotationAjax.Controllers.ControllerBase;
 
namespace DataAnnotationAjax.Controllers
{
    public class HomeController : BaseController
    {
        [HttpGet]
        public ActionResult Index()
        {
            return View(StudentRepository.GetStudent());
        }
 
        [HttpPost]
        public ActionResult AddStudent()
        {
            var student = new Student();
 
            // The TryUpdateModel will use data annotation to validate
            // the data passed over from the client. If any validation
            // fails, the error will be written to the "ModelState".
            var valid = TryUpdateModel(student);
 
            string studentPartialViewHtml = null;
            if (valid)
            {
                // Add the student to the repository.
                // In any pratical application, exception handling should be used
                // when making data access. In this small example, I will just cross
                // my finger to say "there is no chance to encounter exeption" when
                // adding the student. Indeed, the chance of exception is very minimal
                // when adding students to my small repositoty. If the validation is
                // successful, I will simply tell the client that the student is added.
                StudentRepository.AddStudent(student);
 
                // Obtain the html string from the partial view "Students.ascx"
                var students = StudentRepository.GetStudent();
                studentPartialViewHtml = RenderPartialViewToString("Students", students);
            }
 
            return Json(new {Valid = valid,
                             Errors = GetErrorsFromModelState(), 
                StudentsPartial = studentPartialViewHtml
            });
        }
    }
}

This controller class implements two "Action" methods:

  • The "Index" method is used to load the "Index.aspx" page when the application starts.
  • The "AddStudent" method is called by the client Javascript code when the user clicks the "Add Student" button. It receives the input data from the user and validates it. If the validation is successful, the student is added to the repository. A "Json" object is properly formulated in the expected format and sent to the client.

If you are not familiar with "MVC Model Binding", you may be surprised that I did not write a single line of code to validate the data. The secret of this type of data validation is in the "TryUpdateModel" method.

  • The "TryUpdateModel" method takes a student object. It reads the data sent to the server from the "Request" object and try to update the properties in the student object by matching the property name and the name of the posted data.
  • After the student object is updated, it will check the "Data Annotations" in the "Student" class to validate the data. If any problems found, it will add the error message to the "ModelState" object of the controller. The return type of the "TryUpdateModel" method is Boolean indicating if the model is successfully validated. 
  • If you are still confused about how the data validation is done, you can take a look at this link, which has some more detailed explanations.

To formulate the "Json" object expected by the client, I called two methods "RenderPartialViewToString" and "GetErrorsFromModelState". These two methods are implemented in the "BaseController" class:

using System.Web.Mvc;
using System.IO;
using System.Collections.Generic;
 
namespace DataAnnotationAjax.Controllers.ControllerBase
{
    public class BaseController : Controller
    {
        // This method helps to render a partial view into html string.
        // http://craftycodeblog.com/2010/05/15/asp-net-mvc-render-partial-view-to-string/
        // Credit: Kevin Craft
        public string RenderPartialViewToString(string viewName, object model)
        {
            ViewData.Model = model;
            using (var sw = new StringWriter())
            {
                var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
                var viewContext = new ViewContext(ControllerContext,
                    viewResult.View, ViewData, TempData, sw);
                viewResult.View.Render(viewContext, sw);
 
                return sw.GetStringBuilder().ToString();
            }
        }
 
        // This method helps to get the error information from the MVC "ModelState".
        // We can not directly send the ModelState to the client in Json. The "ModelState"
        // object has some circular reference that prevents it to be serialized to Json.
        public Dictionary<string, object> GetErrorsFromModelState()
        {
            var errors = new Dictionary<string, object>();
            foreach (var key in ModelState.Keys)
            {
                // Only send the errors to the client.
                if (ModelState[key].Errors.Count > 0)
                {
                    errors[key] = ModelState[key].Errors;
                }
            }
 
            return errors;
        }
    }
}
  • The "RenderPartialViewToString" method is to generate a string representation of a "Partial View", so we can send the html content to the client in a "Json" object. MVC does not natively provide this function. This method is borrowed from "Kevin Craft". If you are interested, you can take a look at his post.
  • The "GetErrorsFromModelState" method is to formulate a dictionary to hold the validation errors in the "ModelState" object, since we cannot directly serialize the "ModelState" object in "Json" format in MVC. Putting the validation errors in a simpler data object can also help us reduced the payload in the "Json" object.

Run the Application

Now we finish the application and we can test run it. We can press "F5" to start this application in debug mode or press "Ctrl + F5" to start it without debugging.

RunAppStart.jpg

When the application launches, we can see the three pre-generated students and the three text boxes.

RunAppAllError.jpg

Without typing anything and click the "Add Student" button, the validation fails our input and the corresponding messages are displayed next to the text boxes.

RunAppPartialError.jpg

If we type in the last name and the first name, but give a wrong score. The validation will fail the score and the message is displayed next to the score.

RunAppSuccess.jpg

If we give appropriate information to all the three text boxes, the student is added to the repository and the list of the students is refreshed in the browser.

Points of Interest

  • This article presented an example on how to use data annotations to validate the data received from jQuery Ajax calls and how to send the validation result as well as the html content from a partial view to the client in a Json object in MVC.
  • This article represents an effort to maximize the truthfulness of the validation result using the information available at the server, and also minimize the network traffic by using the "jQuery" Ajax calls. For most web applications, this method can provide sufficient performance. But if you do have a big data set to validate, you may still consider to keep both client side and server side validations.
  • From technology perspective, this article should have answered the following questions:
    • How to bind the data in the web browser to a data model object in MVC. 
    • How to use "Data Annotation" to validate the data in MVC.
    • How to generate a string representation of the html content from a "Partial View" and how to send it to the web browser in a "Json" object.
    • How to send the information in the MVC "ModelState" object to the browser in a "Json" object. 
  • Although using "Data Annotation" to validate data can save us a lot of typing, its capability is limited. For complex data validation, we may still need to write our own validation code. If the type of data validation is natively supported by "Data Annotation", we can implement our own "Custom Data Annotation"
  • In case if the client side validation is needed, you can take a look at my earlier post "An Example to Use jQuery Validation Plugin".
  • I hope you like my postings and I hope this article can help you one way or the other.

History

  • First Revision - 6/26/2011.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Dr. Song Li

United States United States
I have been working in the IT industry for some time. It is still exciting and I am still learning. I am a happy and honest person, and I want to be your friend.

Comments and Discussions


Discussions posted for the Published version of this article. Posting a message here will take you to the publicly available article in order to continue your conversation in public.
 
QuestionROFL VERY BAD WAY TO DO VALIDATION PinmemberMember 1015903726-Jul-13 9:08 
AnswerRe: ROFL VERY BAD WAY TO DO VALIDATION PinmemberDr. Song Li26-Jul-13 9:15 
QuestionWhy not MVC3 unobstrusive validation? PinmemberHardy Wang25-Jul-11 9:36 
AnswerRe: Why not MVC3 unobstrusive validation? PinmemberDr. Song Li28-Jul-11 17:47 
GeneralMy vote of 5 PinmemberMember 34159529-Jun-11 17:34 
Questionit just can statisfy simple requirement Pinmembershenba200928-Jun-11 18:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140709.1 | Last Updated 26 Jun 2011
Article Copyright 2011 by Dr. Song Li
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid