在本節中將會給`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**按鈕。
[
](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.
[
](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。
[
](http://images.cnitblog.com/blog/139239/201303/05113838-fc830e28753e4eb3a06d222f65097743.png)
[
](http://images.cnitblog.com/blog/139239/201303/05113841-ea030449706448aca07435ea47263d3e.png)
下圖顯示了如何在火狐瀏覽器中禁用 JavaScript。
[
](http://images.cnitblog.com/blog/139239/201303/05113843-b3454c17793c4350a4634823ce08248b.png)
下圖顯示了如何在 Chrome 瀏覽器中禁用 JavaScript。
[
](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。
[
](http://images.cnitblog.com/blog/139239/201303/05113849-0c0537b0a4df4b92824dcd45798d71dc.png)
下圖為默認Culture的顯示(English US) 。
[
](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方法