Click here to Skip to main content
15,868,141 members
Articles / Web Development / XHTML

Data Validation Using Annotations for jQuery AJAX Calls in MVC Applications

Rate me:
Please Sign up or sign in to vote.
4.76/5 (14 votes)
4 Jul 2011CPOL9 min read 114.1K   2K   38   7
This article presents an example of how to use data annotations to validate data received from jQuery AJAX calls and how to send validation result as well as HTML content from a partial view to the client in a JSON object in MVC.

Introduction

This article presents an example of how to use data annotations to validate 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, client side validation and server side validation.

  • Validating the data at the client side using JavaScript can give the user 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 results. 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 Data Annotation and MVC Model Binding to minimize the code that we need to write when validating data. The client and the server exchange information using JSON objects.

This article assumes that you have some basic knowledge of 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 in 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:

C#
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 students.

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

The View Page

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

ASP.NET
<%@ 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 properties in the Student class.
  • The list of 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 validation. I will take about this in detail 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":

ASP.NET
<%@ 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 students and displays it 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:

JavaScript
$(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:

  • Registers the click event for the "Add Student" button to send user input to the server. When the response comes back, the UI will be adjusted accordingly.
  • Registers the click event for the "Clear" button to clear the user input and possible error messages, if any.
  • Registers the "keyup" event for the textboxes. When users make changes to 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 which indicates if the validation of the user input was successful.
  • If there are any problems during 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 the controller. We will see how it validates 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:

C#
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 tries 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 a problems is 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 was 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 call two methods RenderPartialViewToString and GetErrorsFromModelState. These two methods are implemented in the BaseController class:

C#
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 reduce the payload in the JSON object.

Run the Application

We have finished 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 clicking 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 the appropriate information to all the three text boxes, the student is added to the repository and the list of students is refreshed in the browser.

Points of Interest

  • This article presented an example of how to use data annotations to validate 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 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 keeping both client side and server side validations.
  • From a technology perspective, this article should have answered the following questions:
    • How to bind data in the web browser to a data model object in MVC
    • How to use a Data Annotation to validate 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 as a JSON object
    • How to send information in the MVC ModelState object to the browser in a JSON object
  • Although using Data Annotations 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 not natively supported by Data Annotations, we can implement our own Custom Data Annotation.
  • In case client side validation is needed, you can take a look at my earlier post "An Example of Using the 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)


Written By
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

 
QuestionGreat Article Pin
Shailesh vora25-Jan-17 23:16
Shailesh vora25-Jan-17 23:16 
QuestionROFL VERY BAD WAY TO DO VALIDATION Pin
Member 1015903726-Jul-13 9:08
Member 1015903726-Jul-13 9:08 
AnswerRe: ROFL VERY BAD WAY TO DO VALIDATION Pin
Dr. Song Li26-Jul-13 9:15
Dr. Song Li26-Jul-13 9:15 
QuestionWhy not MVC3 unobstrusive validation? Pin
Hardy Wang25-Jul-11 9:36
Hardy Wang25-Jul-11 9:36 
AnswerRe: Why not MVC3 unobstrusive validation? Pin
Dr. Song Li28-Jul-11 17:47
Dr. Song Li28-Jul-11 17:47 
GeneralMy vote of 5 Pin
Member 34159529-Jun-11 17:34
Member 34159529-Jun-11 17:34 
Questionit just can statisfy simple requirement Pin
shenba200928-Jun-11 18:21
shenba200928-Jun-11 18:21 

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

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