Explicitly Validating a Model
[HttpPost] public ViewResult MakeBooking(Appointment appt) { if (string.IsNullOrEmpty(appt.ClientName)) { ModelState.AddModelError("ClientName", "Please enter your name"); } if (ModelState.IsValidField("Date") && DateTime.Now > appt.Date) { ModelState.AddModelError("Date", "Please enter a future date"); } if (!appt.TermsAccepted) { ModelState.AddModelError("TermsAccepted", "You must accept the terms"); } if (ModelState.IsValid) { // Statement to store new appointment in a repository would go here in real project return View("Completed", appt); } else { return View(); } }
Displaying Validation Errors to the User
The helpers add a CSS class called "input-validation-error" to the input elements if an error has been reported for the corresponding properties
Displaying Validation Messages (Error CSS class used is "validation-summary-errors")
@model ModelValidation.Models.Appointment @{ ViewBag.Title = "Make an Appointment"; } <h2>Make an Appointment</h2> @using (Html.BeginForm()) { @Html.ValidationSummary() <p>Your name: @Html.EditorFor(m => m.ClientName)</p> <p>Appointment Date: @Html.EditorFor(m => m.Date)</p> <p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms & conditions</p> <input type="submit" value="Make Booking" /> }
Displaying Property-Level Validation Messages (Error CSS class used is "field-validation-error")
@model ModelValidation.Models.Appointment @{ ViewBag.Title = "Make an Appointment"; } <h2>Make an Appointment</h2> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <p>@Html.ValidationMessageFor(m => m.ClientName)</p> <p>Your name: @Html.EditorFor(m => m.ClientName)</p> <p>@Html.ValidationMessageFor(m => m.Date)</p> <p>Appointment Date: @Html.EditorFor(m => m.Date)</p> <p>@Html.ValidationMessageFor(m => m.TermsAccepted)</p> <p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms & conditions</p> <input type="submit" value="Make Booking" /> }
Performing Validation in the Model Binder
The default model binder performs validation as part of the binding process. The model binder performs basic validation for each of the properties in the model object. The built-in default model binder class, DefaultModelBinder, provides some useful methods that can be overridden to add validation to a binder.
Specifying Validation Rules Using Metadata
The advantage of using metadata is that the validation rules are enforced anywhere that the binding process is applied throughout the application, not just in a single action method. The validation attributes are detected and enforced by the built-in default model binder class, DefaultModelBinder.
public class Appointment { [Required] public string ClientName { get; set; } [DataType(DataType.Date)] [Required(ErrorMessage="Please enter a date")] public DateTime Date { get; set; } [Range(typeof(bool),"true","true",ErrorMessage="You must accept the terms")] public bool TermsAccepted { get; set; } }
Creating a Custom Property Validation Attribute
I can also create my own by deriving from the ValidationAttribute class and implementing custom validation logic.
public class MustBeTrueAttribute: ValidationAttribute { public override bool IsValid(object value) { return value is bool && (bool)value; } }
Deriving from the Built-In Validation Attributes
public class FutureDateAttribute:RequiredAttribute { public override bool IsValid(object value) { return base.IsValid(value) && ((DateTime)value) > DateTime.Now; } }
Creating a Model Validation Attribute
public class NoJoeOnMondaysAttribute: ValidationAttribute { public NoJoeOnMondaysAttribute() { ErrorMessage = "Joe cannot book appointments on Mondays"; } public override bool IsValid(object value) { Appointment app = value as Appointment; if (app == null || string.IsNullOrEmpty(app.ClientName) || app.Date == null) { // I don‘t have a model of right type to validate, or I don‘t have the values for ClientName or Date properties I require return true; } else { return !(app.ClientName == "Joe" && app.Date.DayOfWeek == DayOfWeek.Monday); } } }
Defining Self-Validating Models
Another validation technique is to create self-validating models, where the validation logic is part of the model class. A self-validating model implements the IValidatableObject interface.
Performing Client-Side Validation
The MVC Framework supports unobtrusive client-side validation. The term unobtrusive means that validation rules are expressed using attributes added to the HTML elements that views generate. These attributes are interpreted by a JavaScript library that is included as part of the MVC Framework that, in turn, configures the jQuery Validation library, which does the actual validation work.
Enabling Client-Side Validation
Client-side validation is controlled by two settings in the Web.config file.
<appSettings> <add key="webpages:Version" value="3.0.0.0" /> <add key="webpages:Enabled" value="false" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> </appSettings>
You can also configure client-side validation on a per-view basis by setting the HtmlHelper.ClientValidationEnabled and HtmlHelper.UnobtrusiveJavaScriptEnabled in a Razor code block.
Adding the NuGet Packages
Install-Package jQuery –version 1.10.2 Install-Package jQuery.Validation –version 1.11.1 Install-Package Microsoft.jQuery.Unobtrusive.Validation –version 3.0.0
Using Client-Side Validation
Firstly you need to add the javascript liabaries to the _Layout.cshtml page, then apply the metadata attributes that I previously used for server-side validation, such as Required, Range, and StringLength to model.
Performing Remote Validation
This is a client-side validation technique that invokes an action method on the server to perform validation.(A common example of remote validation is to check whether a username is available in applications when such names must be unique)
The first step toward using remote validation is to create an action method that can validate one of the model properties. Action method must return JsonResult type. (The MVC Framework disallows GET requests that produce JSON by default)
public JsonResult ValidateDate(string Date) { DateTime parsedDate; if (!DateTime.TryParse(Date, out parsedDate)) { return Json("Please enter a valid date", JsonRequestBehavior.AllowGet); } else if (parsedDate < DateTime.Now) { return Json("Please enter a date in the future", JsonRequestBehavior.AllowGet); } else { return Json(true, JsonRequestBehavior.AllowGet); } }
To use the remote validation method, I apply the Remote attribute to the property I want to validate in the model class.