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");
}