在本节中将会给`Movie`模型添加验证逻辑。并且确保这些验证规则在用户创建或编辑电影时被执行。
# 保持事情 DRY
ASP.NET MVC 的核心设计信条之一是DRY: "不要重复自己(Don’t Repeat Yourself)"。ASP.NET MVC鼓励您指定功能或者行为,只做一次,然后将它应用到应用程序的各个地方。这可以减少您需要编写的代码量,并减少代码出错率,易于代码维护。
给ASP.NET MVC 和 Entity Framework Code First 提供验证支持是 DRY 信条的一次伟大实践。您可以在一个地方 (模型类) 中以声明的方式指定验证规则,这个规则会在应用程序中的任何地方执行。
让我们看看您如何在本电影应用程序中,使用此验证支持。
#### 给电影模型添加验证规则
您将首先向`Movie`类添加一些验证逻辑。
打开*Movie.cs*文件。在文件的顶部添加`using`语句,从而引用[`System.ComponentModel.DataAnnotations`](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.aspx)命名空间:
using System.ComponentModel.DataAnnotations;
注意,该命名空间不包含`System.Web`。DataAnnotations 提供了一组内置的验证特性,您可以以声明的方式,应用于任何类或属性。
更新`Movie`类,以利用内置的[`Required`](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.requiredattribute.aspx)、[`StringLength`](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.stringlengthattribute.aspx)和[`Range`](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.rangeattribute.aspx)验证属性。以下面的代码为例,以应用验证属性。
~~~
public class Movie {
public int ID { get; set; }
[Required]
public string Title { get; set; }
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Required]
public string Genre { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
[StringLength(5)]
public string Rating { get; set; }
}
~~~
运行该应用程序,您会再次得到了以下的运行时错误:
*The model backing the 'MovieDBContext' context has changed since the database was created. Consider using Code First Migrations to update the database (*[*http://go.microsoft.com/fwlink/?LinkId=238269*](http://go.microsoft.com/fwlink/?LinkId=238269)*).*
我们将使用Migrations来更新 Schema。生成解决方案,然后打开**软件包管理器控制台**窗口,并输入以下命令:
`add-migration AddDataAnnotationsMig`
`update-database`
当此命令完后,Visual Studio会打开指定名称 (*AddDataAnnotationsMig*)的文件,其中定义了派生自`DbMIgration`的新类,并在`Up`方法中,您可以看到代码更新的Schema 和约束条件。`Title` 和`Genre` 字段不再可以为 null (即,您必须输入一个值) 并且`Rating` 字段具有最大长度是 5。
验证属性将指定一个验证行为,这样您可以指定模型中的那个属性需要被强制验证。[Required](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.requiredattribute(VS.110).aspx)属性指示该属性必须有一个值 ,在此示例中,一部电影必须要有`Title`、 `ReleaseDate`、`Genre`和`Price`属性的值,这样才有效。`Range`属性限制了一个指定范围内的值。`StringLength`属性允许您设置一个字符串属性的最大长度和其最小长度(可选)。内部类型 (例如`decimal, int, float, DateTime`)默认是必须的,所以不需要`Required` 属性。
Code First 确保您在模型类上所指定的验证规则,会在应用程序修改数据库之前执行。例如,下面的代码在调用`SaveChanges`方法时,将引发异常,因为缺失几个必需的`Movie`属性值,并且价格为零 (这在有效范围之外)。
~~~
MovieDBContext db = new MovieDBContext();
Movie movie = new Movie();
movie.Title = "Gone with the Wind";
movie.Price = 0.0M;
db.Movies.Add(movie);
db.SaveChanges(); // <= Will throw server side validation exception
~~~
验证规则会自动被 .NET Framework执行,这将有助于使您的应用程序更加的可靠。它还确保你不会因为忘了验证,无意中使得坏的数据也写入到了数据库。
下面是更新后的Movie.cs文件的完整代码清单:
~~~
using System;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
namespace MvcMovie.Models {
public class Movie {
public int ID { get; set; }
[Required]
public string Title { get; set; }
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Required]
public string Genre { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
[StringLength(5)]
public string Rating { get; set; }
}
public class MovieDBContext : DbContext {
public DbSet<Movie> Movies { get; set; }
}
}
~~~
# ASP.NET MVC 的验证错误UI
重新运行应用程序,浏览 */Movies*的 URL。
单击**Create New**链接,来添加一部新电影。在窗体中填写一些无效值,然后单击**Create**按钮。
[![image](https://box.kancloud.cn/2016-08-08_57a81c6a927bb.png "image")
](http://images.cnitblog.com/blog/139239/201303/05113832-863ed546f2f54053a5ecf932e40addbe.png)
**注意**,为了使jQuery支持使用逗号的非英语区域的验证 ,需要设置逗号(",")来表示小数点,你需要引入*globalize.js*并且你还需要具体的指定*cultures/globalize.cultures.js*文件 (地址在[https://github.com/jquery/globalize](https://github.com/jquery/globalize)) 在 JavaScript 中可以使用 `Globalize.parseFloat`。下面的代码展示了在"FR-FR" Culture下的Views\Movies\Edit.cshtml 视图:
~~~
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
<script src="~/Scripts/globalize.js"></script>
<script src="~/Scripts/globalize.culture.fr-FR.js"></script>
<script>
$.validator.methods.number = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseFloat(value));
}
$(document).ready(function () {
Globalize.culture('fr-FR');
});
</script>
<script>
jQuery.extend(jQuery.validator.methods, {
range: function (value, element, param) {
//Use the Globalization plugin to parse the value
var val = $.global.parseFloat(value);
return this.optional(element) || (
val >= param[0] && val <= param[1]);
}
});
</script>
}
~~~
为了使用这种用户验证界面,真正的好处是,您不需要修改`MoviesController`类或*Create.cshtml*视图中的任何一行代码。在本教程之前所生成的控制器和视图中,`Movie`模型类的属性上所指定的验证规则一样可以自动适用。
您可能已经注意到了`Title` 和`Genre`属性,在字段中输入文本或者删除文本,是不会执行所需的验证属性的,直到您提交表单 (点**Create**按钮)时才执行。对于字段是最初为空 (如创建视图中的字段) 和只有Required属性并没有其它验证属性的字段,您可以执行以下操作来触发验证:
1. Tab into the field.
2. Enter some text.
3. Tab out.
4. Tab back into the field.
5. Remove the text.
6. Tab out.
上面的顺序将触发必需的验证,而并不需要点击提交按钮。在不输入任何字段的情况下,直接点击提交按钮,将触发客户端验证。直到没有客户端验证错误的情况下,表单数据才会发送到服务器。您可以在服务器端HTTP Post 方法上加上断点来测试一下,或者使用[Fiddler tool](http://fiddler2.com/fiddler2/)或 IE 9 [F12](http://msdn.microsoft.com/en-us/ie/aa740478)Developer tools.
[![image](https://box.kancloud.cn/2016-08-08_57a8559c8e8f1.png "image")
](http://images.cnitblog.com/blog/139239/201303/05113836-12a8be71a45844d394368387917ebdee.png)
#### 如何验证创建视图和创建方法
您可能很想知道验证用户界面在没有更新控制器或视图代码的情况下是如何生成的。下面列出了`MovieController`类中的`Create`方法。它们是之前教程中自动生成的,并没有修改。
~~~
//
// GET: /Movies/Create
public ActionResult Create()
{
return View();
}
//
// POST: /Movies/Create
[HttpPost]
public ActionResult Create(Movie movie)
{
if (ModelState.IsValid)
{
db.Movies.Add(movie);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
~~~
第一种(HTTP GET)`Create` 方法用来显示初始的创建form。第二个 (`[HttpPost]`) 方法处理form的请求。第二种`Create`方法 (`HttpPost` 版本) 调用`ModelState.IsValid`来检查是否有的任何的Movie验证错误。调用此方法将验证对象上所有应用了验证约束的属性。如果对象含有验证错误,则`Create`方法会重新显示初始的form。如果没有任何错误,方法将保存信息到数据库。在我们的电影示例中,我们使用了验证,**当客户端检测到错误时,form不会被post到服务器;所以第二个Create方法永远不会被调用**。如果您在浏览器中禁用了 JavaScript,客户端验证也会被禁用,HTTP POST `Create`方法会调用 [ModelState.IsValid](http://msdn.microsoft.com/en-us/library/system.web.mvc.modelstatedictionary.isvalid.aspx)来检查影片是否含有任何验证错误。
您可以在`HttpPost Create`方法中设置一个断点,当客户端验证检测到错误时,不会post form数据,所以永远不会调用该方法。如果您在浏览器中禁用 JavaScript,然后提交具有错误信息的form,断点将会命中。您仍然得到充分的验证,即使在没有 JavaScript的情况下。下图显示了如何禁用 Internet Explorer 中的 JavaScript。
[![image](https://box.kancloud.cn/2016-08-08_57a8559cac88c.png "image")
](http://images.cnitblog.com/blog/139239/201303/05113838-fc830e28753e4eb3a06d222f65097743.png)
[![image](https://box.kancloud.cn/2016-08-08_57a8559ccb8ee.png "image")
](http://images.cnitblog.com/blog/139239/201303/05113841-ea030449706448aca07435ea47263d3e.png)
下图显示了如何在火狐浏览器中禁用 JavaScript。
[![image](https://box.kancloud.cn/2016-08-08_57a8559cde804.png "image")
](http://images.cnitblog.com/blog/139239/201303/05113843-b3454c17793c4350a4634823ce08248b.png)
下图显示了如何在 Chrome 浏览器中禁用 JavaScript。
[![image](https://box.kancloud.cn/2016-08-08_57a8559d01516.png "image")
](http://images.cnitblog.com/blog/139239/201303/05113846-efbc419dfda2400991d9e251fc9a013d.png)
下面是框架代码在之前的教程中生成的*Create.cshtml*视图模板。它用来为以上两个操作方法来显示初始的form,同时在验证出错时来重新显示视图。
请注意,代码如何使用`Html.EditorFor` helper 输出为`Movie`中的每个属性的`<input>`元素。此Helper旁边是对`Html.ValidationMessageFor`方法的调用。这两个Helper方法将处理由控制器传递到视图的模型对象(在这里是,`Movie`对象)。它们会自动查找模型中指定的验证属性,并显示适当的错误消息。
如果您想要在后面更改验证逻辑,您可以做在一个地方,将验证信息添加到模型上。 (此示例中,是`movie` 类)。您不必担心不符合规则 ,验证逻辑会在应用程序的不同部分执行——在一个地方定义验证逻辑将会被使用到各个地方。这使代码非常干净,并使它易于维护和扩展。它意味着您会完全遵守DRY原则。
#### 给影片模型添加Formatting
打开*Movie.cs*文件并查看`Movie` 类。[`System.ComponentModel.DataAnnotations`](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.aspx)命名空间提供了内置的验证特性集的格式属性。我们已经为发布日期和价格字段应用了[`DataType`](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.datatype.aspx)枚举值。下面的代码示例了`ReleaseDate`和`Price`属性与相应的[`DisplayFormat`](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayformatattribute.aspx)属性。
[DataType(DataType.Date)] public DateTime ReleaseDate { get; set; } [DataType(DataType.Currency)] public decimal Price { get; set; }
[`DataType`](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.datatype.aspx)属性不是验证特性,它们用来告诉视图引擎如何Render HTML 。在上面的示例中,`DataType.Date`属性将影片日期显示为日期,例如,下面的[`DataType`](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.datatype.aspx)属性不会验证数据的格式:
[DataType(DataType.EmailAddress)]
[DataType(DataType.PhoneNumber)]
[DataType(DataType.Url)]
上面列出的属性只提供视图引擎来显示数据的格式(如:<a> 为 URL ,< href="mailto:EmailAddress.com"> 为电子邮件。您可以使用[正则表达式](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.regularexpressionattribute.aspx)属性来验证数据的格式。)
另一种使用`DataType` 属性的方式,您可以显式设置[`DataFormatString`](http://msdn.microsoft.com/en-us/library/system.string.format.aspx)。下面的代码示例了具有一个日期格式字符串的Release Date属性 (即"d")。
[DisplayFormat(DataFormatString = "{0:d}")]
public DateTime ReleaseDate { get; set; }
下面的代码设置`Price`属性为货币格式。
[DisplayFormat(DataFormatString = "{0:c}")]
public decimal Price { get; set; }
完整的`Movie` 类如下所示。
~~~
@model MvcMovie.Models.Movie
@{
ViewBag.Title = "Create";
}
<h2>Create</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"></script>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Movie</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.ReleaseDate)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.ReleaseDate)
@Html.ValidationMessageFor(model => model.ReleaseDate)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Genre)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Genre)
@Html.ValidationMessageFor(model => model.Genre)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Price)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Price)
@Html.ValidationMessageFor(model => model.Price)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Rating)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Rating)
@Html.ValidationMessageFor(model => model.Rating)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
~~~
运行该应用程序并浏览到`Movies`控制器。很好的格式化了发布日期和价格。下图显示了Release Date和使用 "FR-FR" Culture 的Price。
[![image](https://box.kancloud.cn/2016-08-08_57a8559d1e3de.png "image")
](http://images.cnitblog.com/blog/139239/201303/05113849-0c0537b0a4df4b92824dcd45798d71dc.png)
下图为默认Culture的显示(English US) 。
[![image](https://box.kancloud.cn/2016-08-08_57a8559d3bac0.png "image")
](http://images.cnitblog.com/blog/139239/201303/05113853-93ebbb62af1d48f5baf93adf0e30845d.png)
在下一部分,我们先会看看代码,然后再改进一下自动生成的`Details` 和`Delete` 方法。
--------------------------------------------------------------------------------------------------------------------
译者注:
本系列共9篇文章,翻译自Asp.Net MVC4 官方教程,由于本系列文章言简意赅,篇幅适中,从一个示例开始讲解,全文最终完成了一个管理影片的小系统,非常适合新手入门Asp.Net MVC4,并由此开始开发工作。9篇文章为:
1. Asp.Net MVC4 入门介绍
· 原文地址:[http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/intro-to-aspnet-mvc-4](http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/intro-to-aspnet-mvc-4)
· 译文地址:[http://www.cnblogs.com/powertoolsteam/archive/2012/11/01/2749906.html](http://www.cnblogs.com/powertoolsteam/archive/2012/11/01/2749906.html)
2. 添加一个控制器
· 原文地址:[http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-controller](http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-controller)
· 译文地址:[http://www.cnblogs.com/powertoolsteam/archive/2012/11/02/2751015.html](http://www.cnblogs.com/powertoolsteam/archive/2012/11/02/2751015.html)
3. 添加一个视图
· 原文地址:[http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-view](http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-view)
· 译文地址:[http://www.cnblogs.com/powertoolsteam/archive/2012/11/06/2756711.html](http://www.cnblogs.com/powertoolsteam/archive/2012/11/06/2756711.html)
4. 添加一个模型
· 原文地址:[http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-model](http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-model)
· 译文地址:[http://www.cnblogs.com/powertoolsteam/archive/2012/12/17/2821495.html](http://www.cnblogs.com/powertoolsteam/archive/2012/12/17/2821495.html)
5. 从控制器访问数据模型
· 原文地址:[http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/accessing-your-models-data-from-a-controller](http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/accessing-your-models-data-from-a-controller)
· 译文地址:[http://www.cnblogs.com/powertoolsteam/archive/2013/01/11/2855935.html](http://www.cnblogs.com/powertoolsteam/archive/2013/01/11/2855935.html)
6. 验证编辑方法和编辑视图
· 原文地址:[http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/examining-the-edit-methods-and-edit-view](http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/examining-the-edit-methods-and-edit-view)
· 译文地址:[http://www.cnblogs.com/powertoolsteam/archive/2013/01/24/2874622.html](http://www.cnblogs.com/powertoolsteam/archive/2013/01/24/2874622.html)
7. 给电影表和模型添加新字段
· 原文地址:[http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-new-field-to-the-movie-model-and-table](http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-new-field-to-the-movie-model-and-table)
· 译文地址:[http://www.cnblogs.com/powertoolsteam/archive/2013/02/26/2933105.html](http://www.cnblogs.com/powertoolsteam/archive/2013/02/26/2933105.html)
8. 给数据模型添加校验器
· 原文地址:[http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-validation-to-the-model](http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-validation-to-the-model)
· 译文地址:[http://www.cnblogs.com/powertoolsteam/archive/2013/03/05/2944030.html ](http://www.cnblogs.com/powertoolsteam/archive/2013/03/05/2944030.html%20)
9. 查询详细信息和删除记录
· 原文地址:[http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/examining-the-details-and-delete-methods](http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/examining-the-details-and-delete-methods)
· 译文地址:
- 前言
- 入门介绍
- 添加一个控制器
- 添加一个视图
- 添加一个模型
- 从控制器访问数据模型
- 验证编辑方法和编辑视图
- 给电影表和模型添加新字段
- 给数据模型添加校验器
- 查询详细信息和删除记录
- 第三方控件Studio for ASP.NET Wijmo MVC4 工具应用
- ASP.NET MVC 5-开始MVC5之旅
- 控制器
- 视图
- 将数据从控制器传递给视图
- 添加一个模型
- 创建连接字符串(Connection String)并使用SQL Server LocalDB
- 从控制器访问数据模型
- 验证编辑方法(Edit method)和编辑视图(Edit view)
- 给电影表和模型添加新字段
- 给数据模型添加校验器
- 查询Details和Delete方法