Validating Model Data in an MVC Application

Validating user input to make sure that it matches the data model in an ASP.NET MVC application can help you protect the application data from user input mistakes and from users who have malicious intent. There are many ways to incorporate input validation in an MVC application. For example, you can use a publicly available validation library, such as the Microsoft Enterprise Library Validation Block. This topic focuses on the MVC features that support validation and shows you a simple way to validate model data.

Basic Model Validation

When a user submits a form, the form data is passed to a controller action method by using the ViewDataDictionary collection. The ViewDataDictionary has a ModelState property that contains a collection of ModelState objects. For each model that is defined in an MVC application, the MVC framework creates a corresponding ModelState object and adds it to the collection.

The action method that receives the form data defines the validation rules that apply to the form data. Some validation rules are inferred from the model, such as a rule that specifies that a field must contain an integer. Other validation rules are defined in the action method, such as a rule that specifies that a field must match a given regular expression. If a rule is violated, the action method uses the ModelState property to pass the validation error information back to the view. You can then use HTML helper methods in the view to render a summary of error messages and indicate the form fields where errors were found.

Model Validation Example

The following example shows a Person class that defines properties for storing personal data.

                        Public Class Person
    Private _Name As String
    Public Property Name() As String
        Get
            Return _Name
        End Get
        Set(ByVal value As String)
            _Name = value
        End Set
    End Property

    Private _Age As Integer
    Public Property Age() As Integer
        Get
            Return _Age
        End Get
        Set(ByVal value As Integer)
            _Age = value
        End Set
    End Property

    Private _Street As String
    Public Property Street() As String
        Get
            Return _Street
        End Get
        Set(ByVal value As String)
            _Street = value
        End Set
    End Property

    Private _City As String
    Public Property City() As String
        Get
            Return _City
        End Get
        Set(ByVal value As String)
            _City = value
        End Set
    End Property

    Private _State As String
    Public Property State() As String
        Get
            Return _State
        End Get
        Set(ByVal value As String)
            _State = value
        End Set
    End Property

    Private _Zipcode As String
    Public Property Zipcode() As String
        Get
            Return _Zipcode
        End Get
        Set(ByVal value As String)
            _Zipcode = value
        End Set
    End Property

    Private _Phone As String
    Public Property Phone() As String
        Get
            Return _Phone
        End Get
        Set(ByVal value As String)
            _Phone = value
        End Set
    End Property

    Private _Email As String
    Public Property Email() As String
        Get
            Return _Email
        End Get
        Set(ByVal value As String)
            _Email = value
        End Set
    End Property
End Class
                        public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zipcode { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
}

The following markup defines a form for entering values to create a Person instance. If the user enters an invalid value for the Person instance, this view is rendered again, this time with error messages, which are passed to the view in the ModelState property.

At the top of the view, the ValidationSummary helper method renders a list of validation errors, if any are found. In addition, the ValidationMessage helper method renders a validation error message next to each form field for which an error is found.

<h2>Create</h2>

<%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>

<% Using Html.BeginForm()%>

    <fieldset>
        <legend>Fields</legend>
        <p>
            <label for="Name">Name:</label>
            <%= Html.TextBox("Name") %> Required
            <%= Html.ValidationMessage("Name", "*") %>
        </p>
        <p>
            <label for="Age">Age:</label>
            <%= Html.TextBox("Age") %> Required
            <%= Html.ValidationMessage("Age", "*") %>
        </p>
        <p>
            <label for="Street">Street:</label>
            <%= Html.TextBox("Street") %>
            <%= Html.ValidationMessage("Street", "*") %>
        </p>
        <p>
            <label for="City">City:</label>
            <%= Html.TextBox("City") %>
            <%= Html.ValidationMessage("City", "*") %>
        </p>
        <p>
            <label for="State">State:</label>
            <%= Html.TextBox("State") %>
            <%= Html.ValidationMessage("State", "*") %>
        </p>
        <p>
            <label for="Zipcode">Zipcode:</label>
            <%= Html.TextBox("Zipcode") %>
            <%= Html.ValidationMessage("Zipcode", "*") %>
        </p>
        <p>
            <label for="Phone">Phone:</label>
            <%= Html.TextBox("Phone") %> Required
            <%= Html.ValidationMessage("Phone", "*") %>
        </p>
        <p>
            <label for="Email">Email:</label>
            <%= Html.TextBox("Email") %> Required
            <%= Html.ValidationMessage("Email", "*") %>
        </p>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>

<% End Using %>

<div>
    <%=Html.ActionLink("Back to List", "Index") %>
</div>
<h2>Create</h2>

<%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>

<% using (Html.BeginForm()) {%>

    <fieldset>
        <legend>Fields</legend>
        <p>
            <label for="Name">Name:</label>
            <%= Html.TextBox("Name") %> Required
            <%= Html.ValidationMessage("Name", "*") %>
        </p>
        <p>
            <label for="Age">Age:</label>
            <%= Html.TextBox("Age") %> Required
            <%= Html.ValidationMessage("Age", "*") %>
        </p>
        <p>
            <label for="Street">Street:</label>
            <%= Html.TextBox("Street") %>
            <%= Html.ValidationMessage("Street", "*") %>
        </p>
        <p>
            <label for="City">City:</label>
            <%= Html.TextBox("City") %>
            <%= Html.ValidationMessage("City", "*") %>
        </p>
        <p>
            <label for="State">State:</label>
            <%= Html.TextBox("State") %>
            <%= Html.ValidationMessage("State", "*") %>
        </p>
        <p>
            <label for="Zipcode">Zipcode:</label>
            <%= Html.TextBox("Zipcode") %>
            <%= Html.ValidationMessage("Zipcode", "*") %>
        </p>
        <p>
            <label for="Phone">Phone:</label>
            <%= Html.TextBox("Phone") %> Required
            <%= Html.ValidationMessage("Phone", "*") %>
        </p>
        <p>
            <label for="Email">Email:</label>
            <%= Html.TextBox("Email") %> Required
            <%= Html.ValidationMessage("Email", "*") %>
        </p>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>

<% } %>

<div>
    <%=Html.ActionLink("Back to List", "Index") %>
</div>

When this form is submitted, the Create action method defines the following validation rules for the properties in the Person class:

  • The Name, Age, Phone, and Email properties are required.

  • The Age property must be an integer in the range between 1 and 200.

  • The Street, City, and State properties are optional.

  • The Zipcode property is optional, but it must be a valid postal code if it is entered.

  • The Phone property must be a valid telephone number.

  • The Email property must be a valid e-mail address.

If a validation error occurs, the action method calls the AddModelError method to add the error to the associated ModelState object. The AddModelError method accepts the name of the associated property and the error message to display. After the action method executes the validation rules, it uses the IsValid property of the ModelStateDictionary collection to determine whether the resulting data complies with the model.

<AcceptVerbs(HttpVerbs.Post)> _
Function Create(ByVal person As Person) As ActionResult
    If person.Name.Trim().Length = 0 Then
        ModelState.AddModelError("Name", "Name is required.")
    End If
    If ((person.Age < 1) Or (person.Age > 200)) Then
        ModelState.AddModelError("Age", "Age must be within range 1 to 200.")
    End If
    If ((person.Zipcode.Trim().Length > 0) And Not (Regex.IsMatch(person.Zipcode, "^\d{5}$|^\d{5}-\d{4}$"))) Then
        ModelState.AddModelError("Zipcode", "Zipcode is invalid.")
    End If
    If Not (Regex.IsMatch(person.Phone, "((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}")) Then
        ModelState.AddModelError("Phone", "Phone number is invalid.")
    End If
    If Not (Regex.IsMatch(person.Email, "^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$")) Then
        ModelState.AddModelError("Email", "Email format is invalid.")
    End If
    If Not ModelState.IsValid Then
        Return View("Create", person)
    End If

    people.Add(person)

    Return RedirectToAction("Index")
End Function
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Person person)
{
    if (person.Name.Trim().Length == 0)
    {
        ModelState.AddModelError("Name", "Name is required.");
    }
    if (person.Age < 1 || person.Age > 200)
    {
        ModelState.AddModelError("Age", "Age must be within range 1 to 200.");
    }
    if ((person.Zipcode.Trim().Length > 0) && (!Regex.IsMatch(person.Zipcode, @"^\d{5}$|^\d{5}-\d{4}$")))
    {
        ModelState.AddModelError("Zipcode", "Zipcode is invalid.");
    }
    if (!Regex.IsMatch(person.Phone, @"((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}"))
    {
        ModelState.AddModelError("Phone", "Phone number is invalid.");
    }
    if (!Regex.IsMatch(person.Email, @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
    {
        ModelState.AddModelError("Email", "Email format is invalid.");
    }
    if (!ModelState.IsValid)
    {
        return View("Create", person);
    }

    people.Add(person);

    return RedirectToAction("Index");
}

See Also

Other Resources

Models and Model Binders in MVC Applications