1. 程式人生 > >Asp.Net MVC4入門指南(8):給資料模型新增校驗器

Asp.Net MVC4入門指南(8):給資料模型新增校驗器

在本節中將會給Movie模型新增驗證邏輯。並且確保這些驗證規則在使用者建立或編輯電影時被執行。

保持事情 DRY

ASP.NET MVC 的核心設計信條之一是DRY: "不要重複自己(Don’t Repeat Yourself)"。ASP.NET MVC鼓勵您指定功能或者行為,只做一次,然後將它應用到應用程式的各個地方。這可以減少您需要編寫的程式碼量,並減少程式碼出錯率,易於程式碼維護。

給ASP.NET MVC 和 Entity Framework Code First 提供驗證支援是 DRY 信條的一次偉大實踐。您可以在一個地方 (模型類) 中以宣告的方式指定驗證規則,這個規則會在應用程式中的任何地方執行。

讓我們看看您如何在本電影應用程式中,使用此驗證支援。

給電影模型新增驗證規則

您將首先向Movie類新增一些驗證邏輯。

using System.ComponentModel.DataAnnotations;

注意,該名稱空間不包含System.Web。DataAnnotations 提供了一組內建的驗證特性,您可以以宣告的方式,應用於任何類或屬性。

更新Movie類,以利用內建的RequiredStringLengthRange驗證屬性。以下面的程式碼為例,以應用驗證屬性。

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).

我們將使用Migrations來更新 Schema。生成解決方案,然後開啟軟體包管理器控制檯視窗,並輸入以下命令:

add-migration AddDataAnnotationsMig
update-database

當此命令完後,Visual Studio會開啟指定名稱 (AddDataAnnotationsMig)的檔案,其中定義了派生自DbMIgration的新類,並在Up方法中,您可以看到程式碼更新的Schema 和約束條件。TitleGenre 欄位不再可以為 null (即,您必須輸入一個值) 並且Rating 欄位具有最大長度是 5。

驗證屬性將指定一個驗證行為,這樣您可以指定模型中的那個屬性需要被強制驗證。Required屬性指示該屬性必須有一個值 ,在此示例中,一部電影必須要有TitleReleaseDateGenrePrice屬性的值,這樣才有效。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

注意,為了使jQuery支援使用逗號的非英語區域的驗證 ,需要設定逗號(",")來表示小數點,你需要引入globalize.js並且你還需要具體的指定cultures/globalize.cultures.js檔案 (地址在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模型類的屬性上所指定的驗證規則一樣可以自動適用。

您可能已經注意到了TitleGenre屬性,在欄位中輸入文字或者刪除文字,是不會執行所需的驗證屬性的,直到您提交表單 (點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或 IE 9 F12 Developer tools.

image

如何驗證建立檢視和建立方法

您可能很想知道驗證使用者介面在沒有更新控制器或檢視程式碼的情況下是如何生成的。下面列出了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來檢查影片是否含有任何驗證錯誤。

您可以在HttpPost Create方法中設定一個斷點,當客戶端驗證檢測到錯誤時,不會post form資料,所以永遠不會呼叫該方法。如果您在瀏覽器中禁用 JavaScript,然後提交具有錯誤資訊的form,斷點將會命中。您仍然得到充分的驗證,即使在沒有 JavaScript的情況下。下圖顯示瞭如何禁用 Internet Explorer 中的 JavaScript。

image

image

下圖顯示瞭如何在火狐瀏覽器中禁用 JavaScript。

image

下圖顯示瞭如何在 Chrome 瀏覽器中禁用 JavaScript。

image

下面是框架程式碼在之前的教程中生成的Create.cshtml檢視模板。它用來為以上兩個操作方法來顯示初始的form,同時在驗證出錯時來重新顯示檢視。

請注意,程式碼如何使用Html.EditorFor helper 輸出為Movie中的每個屬性的<input>元素。此Helper旁邊是對Html.ValidationMessageFor方法的呼叫。這兩個Helper方法將處理由控制器傳遞到檢視的模型物件(在這裡是,Movie物件)。它們會自動查詢模型中指定的驗證屬性,並顯示適當的錯誤訊息。

如果您想要在後面更改驗證邏輯,您可以做在一個地方,將驗證資訊新增到模型上。 (此示例中,是movie 類)。您不必擔心不符合規則 ,驗證邏輯會在應用程式的不同部分執行——在一個地方定義驗證邏輯將會被使用到各個地方。這使程式碼非常乾淨,並使它易於維護和擴充套件。它意味著您會完全遵守DRY原則。

給影片模型新增Formatting

開啟Movie.cs檔案並檢視Movie 類。 System.ComponentModel.DataAnnotations名稱空間提供了內建的驗證特性集的格式屬性。我們已經為釋出日期和價格欄位應用了DataType列舉值。下面的程式碼示例了ReleaseDatePrice屬性與相應的DisplayFormat屬性。

[DataType(DataType.Date)] public DateTime ReleaseDate { get; set; } [DataType(DataType.Currency)] public decimal Price { get; set; }

DataType屬性不是驗證特性,它們用來告訴檢視引擎如何Render HTML 。在上面的示例中, DataType.Date屬性將影片日期顯示為日期,例如,下面的DataType屬性不會驗證資料的格式:

[DataType(DataType.EmailAddress)]
[DataType(DataType.PhoneNumber)]
[DataType(DataType.Url)]

上面列出的屬性只提供檢視引擎來顯示資料的格式(如:<a> 為 URL ,< href="mailto:EmailAddress.com"> 為電子郵件。您可以使用正則表示式屬性來驗證資料的格式。)

另一種使用DataType 屬性的方式,您可以顯式設定DataFormatString。下面的程式碼示例了具有一個日期格式字串的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

下圖為預設Culture的顯示(English US) 。

image

在下一部分,我們先會看看程式碼,然後再改進一下自動生成的DetailsDelete 方法。有了本節介紹的內容,相信大家已經掌握了給資料模型新增校驗器的方法。後面大家在進行MVC開發時,一方面可以運用本節知識,一方面也可以藉助一些開發工具。ComponentOne Studio ASP.NET MVC 是一款針對 MVC 平臺的控制元件包,能提供從桌面到移動裝置的使用者體驗。

--------------------------------------------------------------------------------------------------------------------

譯者注:

本系列共9篇文章,翻譯自Asp.Net MVC4 官方教程,由於本系列文章言簡意賅,篇幅適中,從一個示例開始講解,全文最終完成了一個管理影片的小系統,非常適合新手入門Asp.Net MVC4,並由此開始開發工作。9篇文章為:

1. Asp.Net MVC4 入門介紹

2. 新增一個控制器

3. 新增一個檢視

4. 新增一個模型

5. 從控制器訪問資料模型

6. 驗證編輯方法和編輯檢視

7. 給電影表和模型新增新欄位

8. 給資料模型新增校驗器

9. 查詢詳細資訊和刪除記錄

 10.第三方控制元件Studio for ASP.NET Wijmo MVC4 工具應用

相關閱讀: