共用方式為


將驗證新增至模型 (C#)

作者:Rick Anderson

注意

這裡提供本教學課程的更新版本,其中使用 ASP.NET MVC 5 和 Visual Studio 2013。 新的教學指示更安全、更易於遵循且示範更多功能。

本教學課程將說明基本概念,簡介如何使用 Microsoft Visual Web Developer 2010 Express Service Pack 1 (Visual Studio Microsoft 的免費版本) 建置 ASP.NET MVC Web 應用程式。 開始之前,請確定您已安裝下列必要項目。 您可按以下連結安裝所有專案:Web Platform Installer。 或者可使用下列連結個別安裝必要條件:

如果您使用 Visual Studio 2010 而非 Visual Web Developer 2010,請按以下連結安裝必要條件:Visual Studio 2010 必要條件

本主題隨附內含 C# 原始程式碼的 Visual Web Developer 專案。 下載 C# 版本。 如果您偏好使用 Visual Basic,請切換至本教學課程的 Visual Basic version 版。

在本節中,您會將驗證邏輯新增至 Movie 模型,確保只要使用者嘗試以應用程式建立或編輯電影,驗證規則就會強制執行。

保持 DRY

ASP.NET MVC 的核心設計原則之一是「DRY」(「不要自我重複」)。 ASP.NET MVC 建議您只要指定一次功能或行為,再反映到應用程式的所有位置。 這可減少需要撰寫的程式碼、避免程式碼出錯,而且也更容易維護。

ASP.NET MVC 和 Entity Framework Core Code First 提供的驗證支援就是執行 DRY 準則的絶佳範例。 您可透過宣告的方式,在單一位置指定驗證規則 (在模型類別中),然後應用程式的所有位置即可強制執行這些規則。

以下說明如何在電影應用程式運用此驗證支援。

將驗證規則新增至電影模型

首先,將驗證邏輯新增至 Movie 類別。

開啟 Movie.cs 檔案。 在檔案頂端新增 using 陳述式,並參照 System.ComponentModel.DataAnnotations 命名空間:

using System.ComponentModel.DataAnnotations;

命名空間是 .NET Framework 的一部分。 提供一組內建的驗證屬性,您可透過宣告方式將其套用至類別或屬性。

現在,請更新 Movie 類別,以利用內建的 RequiredStringLengthRange 驗證屬性。 以下列程式碼作為套用屬性的位置範例。

public class Movie
{
    public int ID { get; set; }

    [Required(ErrorMessage = "Title is required")]
    public string Title { get; set; }

    [Required(ErrorMessage = "Date is required")]
    public DateTime ReleaseDate { get; set; }

    [Required(ErrorMessage = "Genre must be specified")]
    public string Genre { get; set; }

    [Range(1, 100, ErrorMessage = "Price must be between $1 and $100")]
    public decimal Price { get; set; }

    [StringLength(5)]
    public string Rating { get; set; }
}

驗證屬性會指定您想要強制執行模型屬性套用的行為。 Required 屬性指示,屬性必須有值;在本例中,電影必須有 TitleReleaseDateGenrePrice 屬性,才視為有效。 Range 屬性會將值限制在指定的範圍內。 StringLength 屬性可讓您設定字串屬性的最大長度,並選擇性設定其最小長度。

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 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(ErrorMessage = "Title is required")]
        public string Title { get; set; }

        public DateTime ReleaseDate { get; set; }

        [Required(ErrorMessage = "Genre must be specified")]
        public string Genre { get; set; }

        [Range(1, 100, ErrorMessage = "Price must be between $1 and $100")]
        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。

按一下 [建立電影] 連結以新增新電影。 以一些無效值填入表單,然後按一下 [建立] 按鈕。

8_validationErrors

請注意表單會自動使用背影色醒目提示包含無效資料的文字方塊,並在每個文字方塊旁顯示對應的驗證錯誤訊息。 錯誤訊息符合您在標註 Movie 類別時指定的錯誤字串。 用戶端 (使用 JavaScript) 與伺服器端 (若使用者已停用 JavaScript) 都會強制執行這些錯誤。

最實際的好處是,您不需要為了啟用這項驗證 UI 而變更 MoviesController 類別或 Create.cshtml 檢視的任一行程式碼。 稍早在本教學課程中,您所建立的控制器和檢視會自動接取您在 Movie 模型類型中使用屬性指定的驗證規則。

Create View 和 Create Action 方法如何執行驗證

您可能奇怪如何在不更新控制器或檢視程式碼的狀況下產生驗證 UI。 下個清單顯示 Create 方法在 MovieController 類別中的外觀。 這些方法與您稍早在本教學課程建立的無異。

//
// 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);
}

第一個動作方法會顯示初始建立表單。 第二個方法會處理表單張貼。 第二個 Create 方法會呼叫 ModelState.IsValid 檢查電影是否有任何驗證錯誤。 呼叫此方法會評估已套用至物件的所有驗證屬性。 如果物件有驗證錯誤,則 Create 方法會再次顯示表單。 如果沒有任何錯誤,方法即會將新的電影儲存到資料庫。

以下是您稍早在本教學課程中建構的 Create.cshtml 檢視範本。 上述動作方法會使用它來顯示初始表單,以及在發生錯誤時重新顯示它。

@model MvcMovie.Models.Movie
@{
    ViewBag.Title = "Create";
}
<h2>
    Create</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></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>

請注意程式碼如何使用 Html.EditorFor 協助程序來輸出每個 Movie 屬性的 <input> 元素。 此協助程式旁邊是 Html.ValidationMessageFor 協助程式方法的呼叫。 這兩個協助程式方法會使用控制器傳遞給檢視的模型物件 (在此例中為 Movie 物件)。 這些方法會自動尋找模型指定的驗證屬性,並視情況顯示錯誤訊息。

這個方法最棒的是,控制器和 Create 檢視範本對要強制執行的實際驗證規則,或顯示的特定錯誤訊息,全都一無所知。 只有在 Movie 類別中才能指定驗證規則和錯誤字串。

如果您想要稍後再變更驗證邏輯,可在同一個位置執行。 您不必擔心應用程式的不同部分會與規則強制執行的方式不一致,所有的驗證邏輯都是在同一個地方定義,用於所有位置。 這會讓程式碼非常整齊乾淨,容易維護及發展。 也就是說,您完全彰顯了 DRY 原則。

新增格式設定至電影模型

開啟 Movie.cs 檔案。 除了一組內建的驗證屬性之外,System.ComponentModel.DataAnnotations 命名空間還提供了格式屬性。 您要將 DisplayFormat 屬性和 DataType 列舉值套用至發行日期和價格欄位。 下列程式碼會示範 ReleaseDatePrice 屬性 (具有適當的 DisplayFormat 屬性)。

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

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

或者可明確設定 DataFormatString 值。 下列程式碼顯示具有日期格式字串 (即「d」) 的版本日期屬性。 您可使用此方式指定不要在版本日期中顯示時間。

[DisplayFormat(DataFormatString = "{0:d}")]
public DateTime ReleaseDate { get; set; }

下列程式碼會將 Price 屬性格式化為貨幣。

[DisplayFormat(DataFormatString = "{0:c}")]
public decimal Price { get; set; }

完整的 Movie 類別顯示如下。

public class Movie
{
    public int ID { get; set; }

    [Required(ErrorMessage = "Title is required")]
    public string Title { get; set; }

    [DisplayFormat(DataFormatString = "{0:d}")]
    public DateTime ReleaseDate { get; set; }

    [Required(ErrorMessage = "Genre must be specified")]
    public string Genre { get; set; }

    [Range(1, 100, ErrorMessage = "Price must be between $1 and $100")]
    [DisplayFormat(DataFormatString = "{0:c}")]
    public decimal Price { get; set; }

    [StringLength(5)]
    public string Rating { get; set; }
}

執行應用程式並瀏覽至 Movies 控制器。

8_format_SM

在數列的下一個部分中,我們會檢閱應用程式,並對自動產生的 DetailsDelete 方法進行一些改良。