文章内容较长,用于记录自己学习。模型验证(Model Vaildation)是确保应用程序所接受的数据适合于绑定到模型,并且在不合适时给用户提供有用的信息,以帮助他们修正问题的过程。
准备示例项目
新建一个空的MVC项目,名叫ModelVaildation。
在Model文件夹中,新建一个Appointment.cs的文件,如下图所示:
public class Appointment{public string ClientName { get; set; }[DataType(DataType.Date)]public DateTime Date { get; set; }public bool TermsAccepted { get; set; }}
创建一个Home控制器,代码如下图所示:
public class HomeController : Controller{// GET: Homepublic ViewResult MakeBooking(){return View(new Appointment { Date = DateTime.Now});}[HttpPost]public ViewResult MakeBooking(Appointment appt){//在实际项目中,此处是在数据库中存储新的Appointment对象return View("Completed", appt);}}
创建布局页_Layout.cshtml,代码如下图所示:
<!DOCTYPE html><html>
<head><meta charset="utf-8"/><meta name="viewport" content="width=device-width" /><title>@ViewBag.Title</title><style type="text/css">.field-validation-error {color:#f00;}.validation-summary-errors {color:#f00; font-weight:bold;}.input-validation-error {border:2px solid #f00; background-color :#fee;}input[type="checkbox"].input-validation-error {outline:2px solid #f00;}</style>
</head>
<body><div>@RenderBody()</div>
</body>
</html>
创建一个视图启动文件_ViewStart.cshtml,以便布局页运用于视图,如下图所示:
@{ Layout = "~/Views/Shared/_Layout.cshtml";
}
创建MakeBooking.cshtml视图,代码如下图所示:
@model ModelVaildation.Models.Appointment
<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width" /><title>MakeBooking</title>
</head>
<body>
<h4>Book an Appointment</h4>@using (Html.BeginForm()) {<p>Your Name : @Html.EditorFor(m => m.ClientName)</p><p>Appointment Date: @Html.EditorFor(m => m.Date)</p><p>@Html.EditorFor(m => m.TermsAccepted)</p><input type="submit" value="Make Booking"/>}
</body>
</html>
创建Completed.cshtml文件,代码如如下图所示:
<html>
<head><meta name="viewport" content="width=device-width" /><title>Completed</title>
</head>
<body>
<h4> Your Name is confirmed</h4><p> Your Name :<b>@Html.EditorFor(m => m.ClientName)</b></p><p> The data of your appointment is :<b>@Html.DisplayFor(m => m.Date)</b></p>
</body>
</html>
运行程序并导航到/Home/MakeBooking ,输入文本数据,如下图所示:
点击按钮,结果如下:
现在,我们需要增加三个验证来确定用户提交的时候是可接受的数据:
1、用户必须提供一个姓名。
2、用户必须提供一个未来的可用的日期(以mm/dd/yyyy格式)。
3、用户必须选中复选框以接受条款和条件。
明确的验证模型
修改Home控制器代码如下图所示:
public class HomeController : Controller{// GET: Homepublic ViewResult MakeBooking(){return View(new Appointment { Date = DateTime.Now});}[HttpPost]public ViewResult MakeBooking(Appointment appt){if (string.IsNullOrEmpty(appt.ClientName)){ModelState.AddModelError("ClientName", "Please enter yourname");}if (ModelState.IsValidField("Date")){ModelState.AddModelError("Date", "Please enter a date in the future");}if (!appt.TermsAccepted){ModelState.AddModelError("TermsAccepted","You must accepted terms");}if (ModelState.IsValid){return View("Completed", appt);}else{return View();}}}
通过使用ModelState.IsValidField属性,来检查模型绑定器是否能够对一个属性赋值。对于Data属性就是这么做的,以确保模型绑定器能够解析用户所递交的值。如果无法从请求中解析到一个值,执行额外检查或者报告其他错误消息是没有意义的(当无法从请求数据为Date属性解析到一个值时,该错误会在ModelState.IsVaild中体现出来)。
在MakeBooking.cshtml视图中用来生成input元素的模板视图辅助器,会检查视图模型的验证错误。
如果相关属性有错误报告,那么辅助器对相应的Input元素添加一个CSS的class标签属性,其值为input-validation-error.这就是为什么一开始需要在布局页中设置CSS样式的原因。
在不输入任何数据的情况下,提交表单,页面如下所示:
显示验证消息
通过显示验证消息可以告诉用户提示的是什么问题,在MakeBooking.cshtml文件中,增加如下图代码:
@model ModelVaildation.Models.Appointment
<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width" /><title>MakeBooking</title>
</head>
<body>
<h4>Book an Appointment</h4>@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"/>}
</body>
</html>
Html.ValidationSummary() 辅助器给用户显示了验证错误的摘要,如果没有错误,则不会生成任何HTML代码。
这些错误被标识成div元素的一个列表,对该元素运用了validation-summary-errors类,如下图所示:
ValidationSummary还有一些重载版本,允许开发人员指定显示模型级错误。前面用的ModelState注册的错误都是属性级的错误。
重载方法 | 描述 |
Html.ValidationSummary() | 生成所有验证错误的摘要 |
Html.ValidationSummary(bool) | 如果bool参数为ture,那么只显示模型级别错误。如果为false,那么便显示所有错误。 |
Html.ValidationSummary(string) | 在所有验证错误摘要之前显示一条消息(包括在string参数中) |
Html.ValidationSummary(bool,string) | 在验证错误前显示一条消息。如果bool参数为true,只显示模型级别的错误 |
当存在由于两个或者多个属性值之间的相互作用而引发的错误时,可以使用模型级错误。举个例子:假设姓名为“Joe”的客户不能形成星期一的预约,修改Home控制器的代码如下图所示:
[HttpPost]public ViewResult MakeBooking(Appointment appt){if (string.IsNullOrEmpty(appt.ClientName)){ModelState.AddModelError("ClientName", "Please enter yourname");}if (ModelState.IsValidField("Date") && DateTime.Now > appt.Date){ModelState.AddModelError("Date", "Please enter a date in the future");}if (!appt.TermsAccepted){ModelState.AddModelError("TermsAccepted","You must accepted terms");}if (ModelState.IsValidField("ClientName") && ModelState.IsValidField("Date") && appt.ClientName == "Joe" && appt.Date.DayOfWeek == DayOfWeek.Monday){ModelState.AddModelError("", "Joe Cannot book appointments OnMondays");}if (ModelState.IsValid){return View("Completed", appt);}else{return View();}}
修改MakeBooking.cshtml中代码如下:
@Html.ValidationSummary(true);
运行情况如下:
在查看Joe是否企图预约在星期一之前,用ModelState.IsValidField方法来确认,已经有了合法的ClientName 和Date 值。这意味着,除非前面在属性上的检查已经成功,否则是不会生成模板级错误的。给ModelState.AddModelError方法的第一个参数传递一个空字符串“ ”,便可以注册一条模型级错误。 但是同样带来了另一个问题,用户无法看到单选框未勾选的属性级错误了。
显示属性级验证消息
为了能看到属性级错误,修改MakeBooking.cshtml代码如下所示:
@model ModelVaildation.Models.Appointment
<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width" /><title>MakeBooking</title>
</head>
<body>
<h4>Book an Appointment</h4>@using (Html.BeginForm()) {@Html.ValidationSummary(true);<p>@Html.ValidationMessageFor(m => m.ClientName)</p><p>Your Name : @Html.EditorFor(m => m.ClientName)</p>@Html.ValidationMessageFor(m => m.Date)<p>Appointment Date: @Html.EditorFor(m => m.Date)</p>@Html.ValidationMessageFor(m => m.TermsAccepted)<p>@Html.EditorFor(m => m.TermsAccepted) I accept the terms &conditions</p><input type="submit" value="Make Booking"/>}
</body>
</html>
运行结果如下图所示:
如果所运用的属性有验证错误,辅助器会插入HTML到响应中,并生成像这样的元素:
用元数据指定验证规则
MVC框架也支持用源数据来表达模型验证规则。使用元数据的优点是,整个应用程序中运用绑定过程的任何地方,都会强制执行验证规则,而不只存在于个别动作方法中。
修改Appointment.cs文件中的代码:
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; }}
以上代码中,Required注解属性指明如果用户未递交一个属性的值,便是一个验证错误。Range注解属性指明可接受值的一个子集 。
属性 | 示例 | 描述 |
Compare | [ Compare("Myother Property") ] | 两个属性必须有同样的值。当你要求一个用户对一个属性提供两次同样的值的时候,这个是有用的。例如:一个邮件地址或者一个口令 |
Range | [ Range(10,20) ] | 一个数字值(或实现了IComparable 的任何属性类型),必须不超出指定的最小值和最大值。为了指定只要一端的边界,可以用一个MinValue或者MaxValue常数,如[ Range(int.MaxValue,50)] |
RegularExpression | [ RegularExprssion(" pattern") ] | 一个字符串值,必须匹配指定的正则表达式模式。注意,该模式必须匹配用户提供的所有值。而不只是其中一个子串。磨人的,它是大小写敏感的,但是你可以通过运用(?!)修饰符,是大小写不敏感——[RegularExpression("(?!)mypattern")] |
Required | [Required] | 必须是一个非空值。或一个不是只含空格的字符串。如果你希望空格作为可接受值,可以用 [ Required(AllowEmtptyString = true)] |
StringLength | [StringLength(10)] | 一个字符串值。必须不超过指定的最大长度。也可以指定一个最小长度,[StringLength (10, MinimumLength = 2)] |
创建自定义的属性验证注解属性
通过从ValidationAttribtue类进行派生,并实现自定义验证逻辑,也可以添加自己的注解属性。创建一个Infrastructure文件夹,并在其中创建一个名称为MustBeTrueAttribute.cs的类文件。如下图所示:
public class MustBeTrueAttribute:ValidationAttribute{public override bool IsValid(object value){return value is bool && (bool)value;}}
使用方法如下图所示:
[MustBeTrue(ErrorMessage ="You must accept the terms")]public bool TermsAccepted { get; set; }
通过内建的验证注解属性进行派生
也可以通过内建的验证注解属性来派生新类,这为我们提供了扩展内建验证注释属性的能力。代码如下图所示:
public class FutrueDateAttribute:RequiredAttribute{public override bool IsValid(object value){return base.IsValid(value) && ((DateTime)value) > DateTime.Now;}}
使用方法如下图所示:
[FutrueDate(ErrorMessage ="Please enter a date")]public DateTime Date { get; set; }
创建模型验证注解属性
到目前为止,所创建的自定义验证注解属性都是运用于个别模型属性的,这意味着他们只能触发属性级别的验证错误。也可以自定义注解属性来验证整个模型,它们将引发模型级错误。代码如下图所示:
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){//还没有正确类型的模型要验证,或者还没有所需要的ClientName 和 Date 属性的值return true;}else{return !(app.ClientName == "Joe" && app.Date.DayOfWeek == DayOfWeek.Monday);}}}
使用方式如下图所示:
[NoJoeOnMondays]public class Appointment{[Required]public string ClientName { get; set; }[DataType(DataType.Date)][FutrueDate(ErrorMessage ="Please enter a date")]public DateTime Date { get; set; }[MustBeTrue(ErrorMessage ="You must accept the terms")]public bool TermsAccepted { get; set; }}
使用客户端验证
到目前为止,所使用的验证技术都是服务端验证。这意味着,用户把他们的数据递交给服务器,服务器验证这些数据,并返回验证结果(要么数据处理成功,要么列出需要修正的错误)。
在Web应用程序中,用户会期望得到及时的反馈——对服务器不作任何递交。这称为客户端验证,而且,这些通常是用JavaScript实现的。用户输入的数据在被放送给服务器之前就进行验证,给用户提供即时反馈并修正的机会。
客户端验证是由Web.Config文件中的两个设置来控制的。为了确保验证生效,必须确保两个设置都必须为true如下图所示:
添加验证库JS文件到_Layout.cshtml,顺序是很重要的,必须先添加jQuery库然后是微软验证库文件。
一旦启用了客户端验证,并确保在布局中引用了JavaScript库,就可以执行客户端验证了。最简单的方式是运用前面服务端验证所使用的注解属性,如Required,Range以及StringLength等。 修复Appointment模型类如下图所示:
public class Appointment{[Required][StringLength(10,MinimumLength =3)]public string ClientName { get; set; }[DataType(DataType.Date)]public DateTime Date { get; set; }public bool TermsAccepted { get; set; }}
运行程序并导航到/Home/MakeBooking,并在name字段中输入字母X,便可以看到客户端验证的效果:
在浏览器中呈现的反馈是即时的,不需要形成对服务器的请求。事实上,执行验证的JavaScript代码会阻止递交表单,直到不再出现验证错误为止。
当用户更改错误时,反馈是即时的。如果你返回到name属性,并不断输入,当输入了三个或者更长的字符时,验证错误便会消失。但是如果继续输入,直到达到十一个字符长度时,便能看到错误消息再次提示。