共用方式為


Razor 頁面上教學課程系列的第 8 部分

注意

這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本

警告

不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前版本,請參閱本文的 .NET 8 版本

作者:Rick Anderson

在本節中將驗證邏輯新增至 Movie 模型。 使用者建立或編輯電影時,就會強制執行驗證規則。

驗證

軟體開發的核心原則稱為 DRY("Don't Repeat Yourself", 不重複原則)。 Razor 頁面可促進開發,只要指定功能一次,就能在整個應用程式中運用。 DRY 有助於:

  • 降低應用程式中的程式碼數量。
  • 使程式碼較少出現錯誤,而且更容易進行測試和維護。

Razor Pages 和 Entity Framework 所提供的驗證支援就是 DRY 準則的絶佳範例:

  • 驗證規則是在單一位置 (在模型類別中) 以宣告方式指定。
  • 規則會在應用程式中的任何位置強制執行。

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

System.ComponentModel.DataAnnotations 命名空間提供:

  • 一組內建的驗證屬性 (attribute),其以宣告方式套用至類別或屬性 (property)。
  • [DataType] 的格式化屬性,可協助進行格式化,但不提供驗證。

更新 Movie 類別,以充分利用內建的 [Required][StringLength][RegularExpression][Range] 驗證屬性。

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace RazorPagesMovie.Models;

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

    [StringLength(60, MinimumLength = 3)]
    [Required]
    public string Title { get; set; } = string.Empty;

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

    [Range(1, 100)]
    [DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
    [Required]
    [StringLength(30)]
    public string Genre { get; set; } = string.Empty;

    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
    [StringLength(5)]
    [Required]
    public string Rating { get; set; } = string.Empty;
}

驗證屬性會指定要對套用目標模型屬性強制執行的行為:

  • [Required][MinimumLength] 屬性 (attribute) 指出屬性 (property) 必須具有值。 無任何動作可防止使用者輸入空白字元來滿足此驗證。

  • [RegularExpression] 屬性則用來限制可輸入的字元。 在上述程式碼中,Genre

    • 必須指使用字母。
    • 第一個字母必須是大寫。 允許空白字元,但不允許數字及特殊字元。
  • RegularExpression Rating

    • 第一個字元必須為大寫字母。
    • 允許後續空格中的特殊字元和數位。 "PG-13" 對分級而言有效,但不適用於 Genre
  • [Range] 屬性會將值限制在指定的範圍內。

  • [StringLength] 屬性可設定字串屬性的最大長度,並選擇性設定其最小長度。

  • 實值型別 (如decimalintfloatDateTime) 原本就是必要項目,而且不需要 [Required] 屬性。

上述驗證規則用於示範,它們不適合用於生產系統。 例如,上述會防止輸入只有兩個字元的電影,而且不允許在 Genre 中使用特殊字元。

ASP.NET Core 自動強制執行驗證規則有助於:

  • 讓應用程式更強固。
  • 減少將無效資料儲存至資料庫的機會。

Razor Pages 中的驗證錯誤 UI

執行應用程式,並巡覽至 Pages/Movies。

選取 Create New 連結。 使用某些無效值完成表單。 當 jQuery 用戶端驗證偵測到錯誤時,它會顯示錯誤訊息。

有多個 jQuery 用戶端驗證錯誤的電影檢視表單

注意

您可能無法在小數欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期欄位支援 jQuery 驗證,您必須採取將應用程式全球化的步驟。 請參閱此 GitHub 問題 4076 \(英文\),以取得加入十進位逗號的指示。

請注意表單在包含無效值的每個欄位中自動呈現驗證錯誤訊息的方式。 用戶端 (使用 JavaScript 和 jQuery) 與伺服器端 (當使用者已停用 JavaScript 時) 都會強制執行這些錯誤。

明顯的好處是:需要在 Create 或 Edit 頁面中進行程式碼變更。 一旦 DataAnnotations 套用至模型,就會啟用驗證 UI。 本教學課程中建立的 Razor Pages 會自動拾取驗證規則 (在 Movie 模型類別的屬性 (property) 上使用驗證屬性 (attribute))。 使用 Edit 頁面測試驗證,會套用相同的驗證。

要一直到沒有任何用戶端驗證錯誤之後,才會將表單資料發佈到伺服器。 請確認表單資料不會經由下列一或多種方式發佈:

  • 將中斷點放置在 OnPostAsync 方法中。 選取 [建立] 或 [儲存],以提交表單。 永遠不會叫用中斷點。
  • 使用 Fiddler 工具
  • 使用瀏覽器開發人員工具來監視網路流量。

伺服器端驗證

在瀏覽器中停用 JavaScript 時,提交含有錯誤的表單將發佈到伺服器。

選擇性地測試伺服器端驗證:

  1. 在瀏覽器中停用 JavaScript。 您可以使用瀏覽器的開發人員工具停用 JavaScript。 如果瀏覽器中無法停用 JavaScript,請嘗試另一個瀏覽器。

  2. 在 Create 或 Edit 頁面的 OnPostAsync 方法中設定中斷點。

  3. 提交含有無效資料的表單。

  4. 確認模型狀態無效:

     if (!ModelState.IsValid)
     {
        return Page();
     }
    

或者,停用伺服器上的用戶端驗證

下列程式碼顯示稍早在此教學課程中包含 Scaffold 的部分 Create.cshtml 頁面。 建立和編輯頁面會使用它來:

  • 顯示初始表單。
  • 在發生錯誤時重新顯示表單。
<form method="post">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <div class="form-group">
        <label asp-for="Movie.Title" class="control-label"></label>
        <input asp-for="Movie.Title" class="form-control" />
        <span asp-validation-for="Movie.Title" class="text-danger"></span>
    </div>

輸入標記協助程式會使用 DataAnnotations 屬性,並產生在用戶端上進行 jQuery 驗證所需的 HTML 屬性。 驗證標記協助程式會顯示驗證錯誤。 如需詳細資訊,請參閱驗證

Create 和 Edit 頁面中沒有任何驗證規則。 只有在 Movie 類別中才能指定驗證規則和錯誤字串。 這些驗證規則會自動套用至編輯 Movie 模型的 Razor Pages。

當驗證邏輯需要變更時,它只會在模型中進行。 驗證會一致地套用在整個應用程式中,驗證邏輯定義於一個位置。 位於一個位置的驗證有助於讓程式碼保持整潔,並可讓您更容易進行維護和更新。

使用 DataType 屬性

檢查 Movie 類別。 除了一組內建的驗證屬性之外,System.ComponentModel.DataAnnotations 命名空間還提供了格式屬性。 [DataType] 屬性會套用到 ReleaseDatePrice 屬性。

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

[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }

[DataType] 屬性提供:

  • 檢視引擎格式化資料的提示。
  • 提供屬性,例如適用於 URL 的 <a>,以及適用於電子郵件的 <a href="mailto:EmailAddress.com">

使用 [RegularExpression] 屬性來驗證資料的格式。 [DataType] 屬性可用於指定比資料庫內建類型更特定的資料類型。 [DataType] 屬性不是驗證屬性。 在範例應用程式中,只會顯示日期,而不含時間。

DataType 列舉提供許多資料類型,例如 DateTimePhoneNumberCurrencyEmailAddress 等等。

[DataType] 屬性:

  • 可讓應用程式自動提供類型的特定功能。 例如,可針對 DataType.EmailAddress 建立 mailto: 連結。
  • 可以在支援 HTML5 的瀏覽器中提供日期選擇器 DataType.Date
  • 發出 HTML 5 data-,發音為「data dash」,這是 HTML 5 瀏覽器取用的屬性。
  • 請勿提供任何驗證。

DataType.Date 未指定顯示日期的格式。 根據預設,將依據以伺服器 CultureInfo 為基礎的預設格式顯示資料欄位。

[Column(TypeName = "decimal(18, 2)")] 資料註解為必要項,因此 Entity Framework Core 可將 Price 正確對應到資料庫中的貨幣。 如需詳細資訊,請參閱資料類型

[DisplayFormat] 屬性用來明確指定日期格式:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }

ApplyFormatInEditMode 設定可指定當顯示值以供編輯時,應該套用的格式。 某些欄位可能不想要該行為。 例如,在貨幣值中,通常不需要編輯 UI 中的貨幣符號。

[DisplayFormat] 屬性可以單獨使用,但通常最好是使用 [DataType] 屬性。 [DataType] 屬性會將資料的語意以其在螢幕上呈現方式的相反方式傳遞。 [DataType] 屬性提供了下列優點,並且這些優點在 [DisplayFormat] 中無法使用:

  • 瀏覽器可以啟用 HTML5 功能 (例如顯示日曆控制項、適合地區設定的貨幣符號、電子郵件連結等等)。
  • 根據預設,瀏覽器將根據地區設定,使用正確的格式呈現資料。
  • [DataType] 屬性可以啟用 ASP.NET Core 架構,來選擇用於呈現資料的正確欄位範本。 DisplayFormat 若是單獨使用,則會使用字串範本。

注意:jQuery 驗證不適用於 [Range] 屬性與 DateTime。 例如,下列程式碼一律會顯示用戶端驗證錯誤,即使當日期位在指定範圍中也一樣:

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]

最佳做法是避免在模型中編譯硬式日期,因此不建議使用 [Range] 屬性和 DateTime。 針對受限於頻繁變更的日期範圍和其他值使用組態,而不是在程式碼中指定。

下列程式碼會顯示一行上的結合屬性:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace RazorPagesMovie.Models;

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

    [StringLength(60, MinimumLength = 3)]
    public string Title { get; set; } = string.Empty;

    [Display(Name = "Release Date"), DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$"), Required, StringLength(30)]
    public string Genre { get; set; } = string.Empty;

    [Range(1, 100), DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$"), StringLength(5)]
    public string Rating { get; set; } = string.Empty;
}

開始使用 Razor Pages 和 EF Core 會顯示使用 Razor Pages 的進階 EF Core 作業。

套用移轉

套用至類別的 DataAnnotations 會變更結構描述。 例如,套用至 Title 欄位的 DataAnnotations:

[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; } = string.Empty;
  • 將字元限制為 60 個。
  • 不允許 null 值。

Movie 資料表目前具有下列結構描述:

CREATE TABLE [dbo].[Movie] (
    [ID]          INT             IDENTITY (1, 1) NOT NULL,
    [Title]       NVARCHAR (MAX)  NULL,
    [ReleaseDate] DATETIME2 (7)   NOT NULL,
    [Genre]       NVARCHAR (MAX)  NULL,
    [Price]       DECIMAL (18, 2) NOT NULL,
    [Rating]      NVARCHAR (MAX)  NULL,
    CONSTRAINT [PK_Movie] PRIMARY KEY CLUSTERED ([ID] ASC)
);

上述結構描述變更不會造成 EF 擲回例外狀況。 不過,請建立移轉,讓結構描述與模型一致。

從 [工具] 功能表中,選取 [NuGet 套件管理員] > [套件管理員主控台]。 在 PMC 中,輸入下列命令:

Add-Migration New_DataAnnotations
Update-Database

Update-Database 會執行 New_DataAnnotations 類別的 Up 方法。

檢查 Up 方法:

public partial class New_DataAnnotations : Migration
{
    /// <inheritdoc />
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AlterColumn<string>(
            name: "Title",
            table: "Movie",
            type: "nvarchar(60)",
            maxLength: 60,
            nullable: false,
            oldClrType: typeof(string),
            oldType: "nvarchar(max)");

        migrationBuilder.AlterColumn<string>(
            name: "Rating",
            table: "Movie",
            type: "nvarchar(5)",
            maxLength: 5,
            nullable: false,
            oldClrType: typeof(string),
            oldType: "nvarchar(max)");

        migrationBuilder.AlterColumn<string>(
            name: "Genre",
            table: "Movie",
            type: "nvarchar(30)",
            maxLength: 30,
            nullable: false,
            oldClrType: typeof(string),
            oldType: "nvarchar(max)");
    }

已更新的 Movie 資料表具有下列結構描述:

CREATE TABLE [dbo].[Movie] (
    [ID]          INT             IDENTITY (1, 1) NOT NULL,
    [Title]       NVARCHAR (60)   NOT NULL,
    [ReleaseDate] DATETIME2 (7)   NOT NULL,
    [Genre]       NVARCHAR (30)   NOT NULL,
    [Price]       DECIMAL (18, 2) NOT NULL,
    [Rating]      NVARCHAR (5)    NOT NULL,
    CONSTRAINT [PK_Movie] PRIMARY KEY CLUSTERED ([ID] ASC)
);

發佈至 Azure

如需部署至 Azure 的資訊,請參閱教學課程:使用 SQL Database 在 Azure 中建置 ASP.NET Core 應用程式

感謝您看完這份 Razor Pages 簡介。 完成本教學課程之後,非常建議您繼續參閱 Razor 頁面與 EF Core 使用者入門。

其他資源

下一步

在本節中將驗證邏輯新增至 Movie 模型。 使用者建立或編輯電影時,就會強制執行驗證規則。

驗證

軟體開發的核心原則稱為 DRY("Don't Repeat Yourself", 不重複原則)。 Razor 頁面可促進開發,只要指定功能一次,就能在整個應用程式中運用。 DRY 有助於:

  • 降低應用程式中的程式碼數量。
  • 使程式碼較少出現錯誤,而且更容易進行測試和維護。

Razor Pages 和 Entity Framework 所提供的驗證支援就是 DRY 準則的絶佳範例:

  • 驗證規則是在單一位置 (在模型類別中) 以宣告方式指定。
  • 規則會在應用程式中的任何位置強制執行。

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

System.ComponentModel.DataAnnotations 命名空間提供:

  • 一組內建的驗證屬性 (attribute),其以宣告方式套用至類別或屬性 (property)。
  • [DataType] 的格式化屬性,可協助進行格式化,但不提供驗證。

更新 Movie 類別,以充分利用內建的 [Required][StringLength][RegularExpression][Range] 驗證屬性。

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace RazorPagesMovie.Models;

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

    [StringLength(60, MinimumLength = 3)]
    [Required]
    public string Title { get; set; } = string.Empty;

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

    [Range(1, 100)]
    [DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
    [Required]
    [StringLength(30)]
    public string Genre { get; set; } = string.Empty;

    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
    [StringLength(5)]
    [Required]
    public string Rating { get; set; } = string.Empty;
}

驗證屬性會指定要對套用目標模型屬性強制執行的行為:

  • [Required][MinimumLength] 屬性 (attribute) 指出屬性 (property) 必須具有值。 無任何動作可防止使用者輸入空白字元來滿足此驗證。

  • [RegularExpression] 屬性則用來限制可輸入的字元。 在上述程式碼中,Genre

    • 必須指使用字母。
    • 第一個字母必須是大寫。 允許空白字元,但不允許數字及特殊字元。
  • RegularExpression Rating

    • 第一個字元必須為大寫字母。
    • 允許後續空格中的特殊字元和數位。 "PG-13" 對分級而言有效,但不適用於 Genre
  • [Range] 屬性會將值限制在指定的範圍內。

  • [StringLength] 屬性可設定字串屬性的最大長度,並選擇性設定其最小長度。

  • 實值型別 (如decimalintfloatDateTime) 原本就是必要項目,而且不需要 [Required] 屬性。

上述驗證規則用於示範,它們不適合用於生產系統。 例如,上述會防止輸入只有兩個字元的電影,而且不允許在 Genre 中使用特殊字元。

ASP.NET Core 自動強制執行驗證規則有助於:

  • 讓應用程式更強固。
  • 減少將無效資料儲存至資料庫的機會。

Razor Pages 中的驗證錯誤 UI

執行應用程式,並巡覽至 Pages/Movies。

選取 Create New 連結。 使用某些無效值完成表單。 當 jQuery 用戶端驗證偵測到錯誤時,它會顯示錯誤訊息。

有多個 jQuery 用戶端驗證錯誤的電影檢視表單

注意

您可能無法在小數欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期欄位支援 jQuery 驗證,您必須採取將應用程式全球化的步驟。 請參閱此 GitHub 問題 4076 \(英文\),以取得加入十進位逗號的指示。

請注意表單在包含無效值的每個欄位中自動呈現驗證錯誤訊息的方式。 用戶端 (使用 JavaScript 和 jQuery) 與伺服器端 (當使用者已停用 JavaScript 時) 都會強制執行這些錯誤。

明顯的好處是:需要在 Create 或 Edit 頁面中進行程式碼變更。 一旦 DataAnnotations 套用至模型,就會啟用驗證 UI。 本教學課程中建立的 Razor Pages 會自動拾取驗證規則 (在 Movie 模型類別的屬性 (property) 上使用驗證屬性 (attribute))。 使用 Edit 頁面測試驗證,會套用相同的驗證。

要一直到沒有任何用戶端驗證錯誤之後,才會將表單資料發佈到伺服器。 請確認表單資料不會經由下列一或多種方式發佈:

  • 將中斷點放置在 OnPostAsync 方法中。 選取 [建立] 或 [儲存],以提交表單。 永遠不會叫用中斷點。
  • 使用 Fiddler 工具
  • 使用瀏覽器開發人員工具來監視網路流量。

伺服器端驗證

在瀏覽器中停用 JavaScript 時,提交含有錯誤的表單將發佈到伺服器。

選擇性地測試伺服器端驗證:

  1. 在瀏覽器中停用 JavaScript。 您可以使用瀏覽器的開發人員工具停用 JavaScript。 如果瀏覽器中無法停用 JavaScript,請嘗試另一個瀏覽器。

  2. 在 Create 或 Edit 頁面的 OnPostAsync 方法中設定中斷點。

  3. 提交含有無效資料的表單。

  4. 確認模型狀態無效:

     if (!ModelState.IsValid)
     {
        return Page();
     }
    

或者,停用伺服器上的用戶端驗證

下列程式碼顯示稍早在此教學課程中包含 Scaffold 的部分 Create.cshtml 頁面。 建立和編輯頁面會使用它來:

  • 顯示初始表單。
  • 在發生錯誤時重新顯示表單。
<form method="post">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <div class="form-group">
        <label asp-for="Movie.Title" class="control-label"></label>
        <input asp-for="Movie.Title" class="form-control" />
        <span asp-validation-for="Movie.Title" class="text-danger"></span>
    </div>

輸入標記協助程式會使用 DataAnnotations 屬性,並產生在用戶端上進行 jQuery 驗證所需的 HTML 屬性。 驗證標記協助程式會顯示驗證錯誤。 如需詳細資訊,請參閱驗證

Create 和 Edit 頁面中沒有任何驗證規則。 只有在 Movie 類別中才能指定驗證規則和錯誤字串。 這些驗證規則會自動套用至編輯 Movie 模型的 Razor Pages。

當驗證邏輯需要變更時,它只會在模型中進行。 驗證會一致地套用在整個應用程式中,驗證邏輯定義於一個位置。 位於一個位置的驗證有助於讓程式碼保持整潔,並可讓您更容易進行維護和更新。

使用 DataType 屬性

檢查 Movie 類別。 除了一組內建的驗證屬性之外,System.ComponentModel.DataAnnotations 命名空間還提供了格式屬性。 [DataType] 屬性會套用到 ReleaseDatePrice 屬性。

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

[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }

[DataType] 屬性提供:

  • 檢視引擎格式化資料的提示。
  • 提供屬性,例如適用於 URL 的 <a>,以及適用於電子郵件的 <a href="mailto:EmailAddress.com">

使用 [RegularExpression] 屬性來驗證資料的格式。 [DataType] 屬性可用於指定比資料庫內建類型更特定的資料類型。 [DataType] 屬性不是驗證屬性。 在範例應用程式中,只會顯示日期,而不含時間。

DataType 列舉提供許多資料類型,例如 DateTimePhoneNumberCurrencyEmailAddress 等等。

[DataType] 屬性:

  • 可讓應用程式自動提供類型的特定功能。 例如,可針對 DataType.EmailAddress 建立 mailto: 連結。
  • 可以在支援 HTML5 的瀏覽器中提供日期選擇器 DataType.Date
  • 發出 HTML 5 data-,發音為「data dash」,這是 HTML 5 瀏覽器取用的屬性。
  • 請勿提供任何驗證。

DataType.Date 未指定顯示日期的格式。 根據預設,將依據以伺服器 CultureInfo 為基礎的預設格式顯示資料欄位。

[Column(TypeName = "decimal(18, 2)")] 資料註解為必要項,因此 Entity Framework Core 可將 Price 正確對應到資料庫中的貨幣。 如需詳細資訊,請參閱資料類型

[DisplayFormat] 屬性用來明確指定日期格式:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }

ApplyFormatInEditMode 設定可指定當顯示值以供編輯時,應該套用的格式。 某些欄位可能不想要該行為。 例如,在貨幣值中,通常不需要編輯 UI 中的貨幣符號。

[DisplayFormat] 屬性可以單獨使用,但通常最好是使用 [DataType] 屬性。 [DataType] 屬性會將資料的語意以其在螢幕上呈現方式的相反方式傳遞。 [DataType] 屬性提供了下列優點,並且這些優點在 [DisplayFormat] 中無法使用:

  • 瀏覽器可以啟用 HTML5 功能 (例如顯示日曆控制項、適合地區設定的貨幣符號、電子郵件連結等等)。
  • 根據預設,瀏覽器將根據地區設定,使用正確的格式呈現資料。
  • [DataType] 屬性可以啟用 ASP.NET Core 架構,來選擇用於呈現資料的正確欄位範本。 DisplayFormat 若是單獨使用,則會使用字串範本。

注意:jQuery 驗證不適用於 [Range] 屬性與 DateTime。 例如,下列程式碼一律會顯示用戶端驗證錯誤,即使當日期位在指定範圍中也一樣:

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]

最佳做法是避免在模型中編譯硬式日期,因此不建議使用 [Range] 屬性和 DateTime。 針對受限於頻繁變更的日期範圍和其他值使用組態,而不是在程式碼中指定。

下列程式碼會顯示一行上的結合屬性:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace RazorPagesMovie.Models;

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

    [StringLength(60, MinimumLength = 3)]
    public string Title { get; set; } = string.Empty;

    [Display(Name = "Release Date"), DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$"), Required, StringLength(30)]
    public string Genre { get; set; } = string.Empty;

    [Range(1, 100), DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$"), StringLength(5)]
    public string Rating { get; set; } = string.Empty;
}

開始使用 Razor Pages 和 EF Core 會顯示使用 Razor Pages 的進階 EF Core 作業。

套用移轉

套用至類別的 DataAnnotations 會變更結構描述。 例如,套用至 Title 欄位的 DataAnnotations:

[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; } = string.Empty;
  • 將字元限制為 60 個。
  • 不允許 null 值。

Movie 資料表目前具有下列結構描述:

CREATE TABLE [dbo].[Movie] (
    [ID]          INT             IDENTITY (1, 1) NOT NULL,
    [Title]       NVARCHAR (MAX)  NULL,
    [ReleaseDate] DATETIME2 (7)   NOT NULL,
    [Genre]       NVARCHAR (MAX)  NULL,
    [Price]       DECIMAL (18, 2) NOT NULL,
    [Rating]      NVARCHAR (MAX)  NULL,
    CONSTRAINT [PK_Movie] PRIMARY KEY CLUSTERED ([ID] ASC)
);

上述結構描述變更不會造成 EF 擲回例外狀況。 不過,請建立移轉,讓結構描述與模型一致。

從 [工具] 功能表中,選取 [NuGet 套件管理員] > [套件管理員主控台]。 在 PMC 中,輸入下列命令:

Add-Migration New_DataAnnotations
Update-Database

Update-Database 會執行 New_DataAnnotations 類別的 Up 方法。

檢查 Up 方法:

public partial class New_DataAnnotations : Migration
{
    /// <inheritdoc />
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AlterColumn<string>(
            name: "Title",
            table: "Movie",
            type: "nvarchar(60)",
            maxLength: 60,
            nullable: false,
            oldClrType: typeof(string),
            oldType: "nvarchar(max)");

        migrationBuilder.AlterColumn<string>(
            name: "Rating",
            table: "Movie",
            type: "nvarchar(5)",
            maxLength: 5,
            nullable: false,
            oldClrType: typeof(string),
            oldType: "nvarchar(max)");

        migrationBuilder.AlterColumn<string>(
            name: "Genre",
            table: "Movie",
            type: "nvarchar(30)",
            maxLength: 30,
            nullable: false,
            oldClrType: typeof(string),
            oldType: "nvarchar(max)");
    }

已更新的 Movie 資料表具有下列結構描述:

CREATE TABLE [dbo].[Movie] (
    [ID]          INT             IDENTITY (1, 1) NOT NULL,
    [Title]       NVARCHAR (60)   NOT NULL,
    [ReleaseDate] DATETIME2 (7)   NOT NULL,
    [Genre]       NVARCHAR (30)   NOT NULL,
    [Price]       DECIMAL (18, 2) NOT NULL,
    [Rating]      NVARCHAR (5)    NOT NULL,
    CONSTRAINT [PK_Movie] PRIMARY KEY CLUSTERED ([ID] ASC)
);

發佈至 Azure

如需部署至 Azure 的資訊,請參閱教學課程:使用 SQL Database 在 Azure 中建置 ASP.NET Core 應用程式

感謝您看完這份 Razor Pages 簡介。 完成本教學課程之後,非常建議您繼續參閱 Razor 頁面與 EF Core 使用者入門。

其他資源

下一步

在本節中將驗證邏輯新增至 Movie 模型。 使用者建立或編輯電影時,就會強制執行驗證規則。

驗證

軟體開發的核心原則稱為 DRY("Don't Repeat Yourself", 不重複原則)。 Razor 頁面可促進開發,只要指定功能一次,就能在整個應用程式中運用。 DRY 有助於:

  • 降低應用程式中的程式碼數量。
  • 使程式碼較少出現錯誤,而且更容易進行測試和維護。

Razor Pages 和 Entity Framework 所提供的驗證支援就是 DRY 準則的絶佳範例:

  • 驗證規則是在單一位置 (在模型類別中) 以宣告方式指定。
  • 規則會在應用程式中的任何位置強制執行。

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

System.ComponentModel.DataAnnotations 命名空間提供:

  • 一組內建的驗證屬性 (attribute),其以宣告方式套用至類別或屬性 (property)。
  • [DataType] 的格式化屬性,可協助進行格式化,但不提供驗證。

更新 Movie 類別,以充分利用內建的 [Required][StringLength][RegularExpression][Range] 驗證屬性。

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace RazorPagesMovie.Models;

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

    [StringLength(60, MinimumLength = 3)]
    [Required]
    public string Title { get; set; } = string.Empty;

    // [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [Range(1, 100)]
    [DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
    [Required]
    [StringLength(30)]
    public string Genre { get; set; } = string.Empty;

    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
    [StringLength(5)]
    [Required]
    public string Rating { get; set; } = string.Empty;
}

驗證屬性會指定要對套用目標模型屬性強制執行的行為:

  • [Required][MinimumLength] 屬性 (attribute) 指出屬性 (property) 必須具有值。 無任何動作可防止使用者輸入空白字元來滿足此驗證。

  • [RegularExpression] 屬性則用來限制可輸入的字元。 在上述程式碼中,Genre

    • 必須指使用字母。
    • 第一個字母必須是大寫。 允許空白字元,但不允許數字及特殊字元。
  • RegularExpression Rating

    • 第一個字元必須為大寫字母。
    • 允許後續空格中的特殊字元和數位。 "PG-13" 對分級而言有效,但不適用於 Genre
  • [Range] 屬性會將值限制在指定的範圍內。

  • [StringLength] 屬性可設定字串屬性的最大長度,並選擇性設定其最小長度。

  • 實值型別 (如decimalintfloatDateTime) 原本就是必要項目,而且不需要 [Required] 屬性。

上述驗證規則用於示範,它們不適合用於生產系統。 例如,上述會防止輸入只有兩個字元的電影,而且不允許在 Genre 中使用特殊字元。

ASP.NET Core 自動強制執行驗證規則有助於:

  • 讓應用程式更強固。
  • 減少將無效資料儲存至資料庫的機會。

Razor Pages 中的驗證錯誤 UI

執行應用程式,並巡覽至 Pages/Movies。

選取 Create New 連結。 使用某些無效值完成表單。 當 jQuery 用戶端驗證偵測到錯誤時,它會顯示錯誤訊息。

有多個 jQuery 用戶端驗證錯誤的電影檢視表單

注意

您可能無法在小數欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期欄位支援 jQuery 驗證,您必須採取將應用程式全球化的步驟。 請參閱此 GitHub 問題 4076 \(英文\),以取得加入十進位逗號的指示。

請注意表單在包含無效值的每個欄位中自動呈現驗證錯誤訊息的方式。 用戶端 (使用 JavaScript 和 jQuery) 與伺服器端 (當使用者已停用 JavaScript 時) 都會強制執行這些錯誤。

明顯的好處是:需要在 Create 或 Edit 頁面中進行程式碼變更。 一旦 DataAnnotations 套用至模型,就會啟用驗證 UI。 本教學課程中建立的 Razor Pages 會自動拾取驗證規則 (在 Movie 模型類別的屬性 (property) 上使用驗證屬性 (attribute))。 使用 Edit 頁面測試驗證,會套用相同的驗證。

要一直到沒有任何用戶端驗證錯誤之後,才會將表單資料發佈到伺服器。 請確認表單資料不會經由下列一或多種方式發佈:

  • 將中斷點放置在 OnPostAsync 方法中。 選取 [建立] 或 [儲存],以提交表單。 永遠不會叫用中斷點。
  • 使用 Fiddler 工具
  • 使用瀏覽器開發人員工具來監視網路流量。

伺服器端驗證

在瀏覽器中停用 JavaScript 時,提交含有錯誤的表單將發佈到伺服器。

選擇性地測試伺服器端驗證:

  1. 在瀏覽器中停用 JavaScript。 您可以使用瀏覽器的開發人員工具停用 JavaScript。 如果瀏覽器中無法停用 JavaScript,請嘗試另一個瀏覽器。

  2. 在 Create 或 Edit 頁面的 OnPostAsync 方法中設定中斷點。

  3. 提交含有無效資料的表單。

  4. 確認模型狀態無效:

     if (!ModelState.IsValid)
     {
        return Page();
     }
    

或者,停用伺服器上的用戶端驗證

下列程式碼顯示稍早在此教學課程中包含 Scaffold 的部分 Create.cshtml 頁面。 建立和編輯頁面會使用它來:

  • 顯示初始表單。
  • 在發生錯誤時重新顯示表單。
<form method="post">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <div class="form-group">
        <label asp-for="Movie.Title" class="control-label"></label>
        <input asp-for="Movie.Title" class="form-control" />
        <span asp-validation-for="Movie.Title" class="text-danger"></span>
    </div>

輸入標記協助程式會使用 DataAnnotations 屬性,並產生在用戶端上進行 jQuery 驗證所需的 HTML 屬性。 驗證標記協助程式會顯示驗證錯誤。 如需詳細資訊,請參閱驗證

Create 和 Edit 頁面中沒有任何驗證規則。 只有在 Movie 類別中才能指定驗證規則和錯誤字串。 這些驗證規則會自動套用至編輯 Movie 模型的 Razor Pages。

當驗證邏輯需要變更時,它只會在模型中進行。 驗證會一致地套用在整個應用程式中,驗證邏輯定義於一個位置。 位於一個位置的驗證有助於讓程式碼保持整潔,並可讓您更容易進行維護和更新。

使用 DataType 屬性

檢查 Movie 類別。 除了一組內建的驗證屬性之外,System.ComponentModel.DataAnnotations 命名空間還提供了格式屬性。 [DataType] 屬性會套用到 ReleaseDatePrice 屬性。

// [Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }

[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }

[DataType] 屬性提供:

  • 檢視引擎格式化資料的提示。
  • 提供屬性,例如適用於 URL 的 <a>,以及適用於電子郵件的 <a href="mailto:EmailAddress.com">

使用 [RegularExpression] 屬性來驗證資料的格式。 [DataType] 屬性可用於指定比資料庫內建類型更特定的資料類型。 [DataType] 屬性不是驗證屬性。 在範例應用程式中,只會顯示日期,而不含時間。

DataType 列舉提供許多資料類型,例如 DateTimePhoneNumberCurrencyEmailAddress 等等。

[DataType] 屬性:

  • 可讓應用程式自動提供類型的特定功能。 例如,可針對 DataType.EmailAddress 建立 mailto: 連結。
  • 可以在支援 HTML5 的瀏覽器中提供日期選擇器 DataType.Date
  • 發出 HTML 5 data-,發音為「data dash」,這是 HTML 5 瀏覽器取用的屬性。
  • 請勿提供任何驗證。

DataType.Date 未指定顯示日期的格式。 根據預設,將依據以伺服器 CultureInfo 為基礎的預設格式顯示資料欄位。

[Column(TypeName = "decimal(18, 2)")] 資料註解為必要項,因此 Entity Framework Core 可將 Price 正確對應到資料庫中的貨幣。 如需詳細資訊,請參閱資料類型

[DisplayFormat] 屬性用來明確指定日期格式:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }

ApplyFormatInEditMode 設定可指定當顯示值以供編輯時,應該套用的格式。 某些欄位可能不想要該行為。 例如,在貨幣值中,通常不需要編輯 UI 中的貨幣符號。

[DisplayFormat] 屬性可以單獨使用,但通常最好是使用 [DataType] 屬性。 [DataType] 屬性會將資料的語意以其在螢幕上呈現方式的相反方式傳遞。 [DataType] 屬性提供了下列優點,並且這些優點在 [DisplayFormat] 中無法使用:

  • 瀏覽器可以啟用 HTML5 功能 (例如顯示日曆控制項、適合地區設定的貨幣符號、電子郵件連結等等)。
  • 根據預設,瀏覽器將根據地區設定,使用正確的格式呈現資料。
  • [DataType] 屬性可以啟用 ASP.NET Core 架構,來選擇用於呈現資料的正確欄位範本。 DisplayFormat 若是單獨使用,則會使用字串範本。

注意:jQuery 驗證不適用於 [Range] 屬性與 DateTime。 例如,下列程式碼一律會顯示用戶端驗證錯誤,即使當日期位在指定範圍中也一樣:

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]

最佳做法是避免在模型中編譯硬式日期,因此不建議使用 [Range] 屬性和 DateTime。 針對受限於頻繁變更的日期範圍和其他值使用組態,而不是在程式碼中指定。

下列程式碼會顯示一行上的結合屬性:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace RazorPagesMovie.Models;

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

    [StringLength(60, MinimumLength = 3)]
    public string Title { get; set; } = string.Empty;

    [Display(Name = "Release Date"), DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$"), Required, StringLength(30)]
    public string Genre { get; set; } = string.Empty;

    [Range(1, 100), DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$"), StringLength(5)]
    public string Rating { get; set; } = string.Empty;
}

開始使用 Razor Pages 和 EF Core 會顯示使用 Razor Pages 的進階 EF Core 作業。

套用移轉

套用至類別的 DataAnnotations 會變更結構描述。 例如,套用至 Title 欄位的 DataAnnotations:

[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; } = string.Empty;
  • 將字元限制為 60 個。
  • 不允許 null 值。

Movie 資料表目前具有下列結構描述:

CREATE TABLE [dbo].[Movie] (
    [ID]          INT             IDENTITY (1, 1) NOT NULL,
    [Title]       NVARCHAR (MAX)  NULL,
    [ReleaseDate] DATETIME2 (7)   NOT NULL,
    [Genre]       NVARCHAR (MAX)  NULL,
    [Price]       DECIMAL (18, 2) NOT NULL,
    [Rating]      NVARCHAR (MAX)  NULL,
    CONSTRAINT [PK_Movie] PRIMARY KEY CLUSTERED ([ID] ASC)
);

上述結構描述變更不會造成 EF 擲回例外狀況。 不過,請建立移轉,讓結構描述與模型一致。

從 [工具] 功能表中,選取 [NuGet 套件管理員] > [套件管理員主控台]。 在 PMC 中,輸入下列命令:

Add-Migration New_DataAnnotations
Update-Database

Update-Database 會執行 New_DataAnnotations 類別的 Up 方法。

檢查 Up 方法:

public partial class NewDataAnnotations : Migration
{
    /// <inheritdoc />
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AlterColumn<string>(
            name: "Title",
            table: "Movie",
            type: "nvarchar(60)",
            maxLength: 60,
            nullable: false,
            oldClrType: typeof(string),
            oldType: "nvarchar(max)");

        migrationBuilder.AlterColumn<string>(
            name: "Rating",
            table: "Movie",
            type: "nvarchar(5)",
            maxLength: 5,
            nullable: false,
            oldClrType: typeof(string),
            oldType: "nvarchar(max)");

        migrationBuilder.AlterColumn<string>(
            name: "Genre",
            table: "Movie",
            type: "nvarchar(30)",
            maxLength: 30,
            nullable: false,
            oldClrType: typeof(string),
            oldType: "nvarchar(max)");
    }

已更新的 Movie 資料表具有下列結構描述:

CREATE TABLE [dbo].[Movie] (
    [ID]          INT             IDENTITY (1, 1) NOT NULL,
    [Title]       NVARCHAR (60)   NOT NULL,
    [ReleaseDate] DATETIME2 (7)   NOT NULL,
    [Genre]       NVARCHAR (30)   NOT NULL,
    [Price]       DECIMAL (18, 2) NOT NULL,
    [Rating]      NVARCHAR (5)    NOT NULL,
    CONSTRAINT [PK_Movie] PRIMARY KEY CLUSTERED ([ID] ASC)
);

發佈至 Azure

如需部署至 Azure 的資訊,請參閱教學課程:使用 SQL Database 在 Azure 中建置 ASP.NET Core 應用程式

感謝您看完這份 Razor Pages 簡介。 完成本教學課程之後,非常建議您繼續參閱 Razor 頁面與 EF Core 使用者入門。

其他資源

下一步

在本節中將驗證邏輯新增至 Movie 模型。 使用者建立或編輯電影時,就會強制執行驗證規則。

驗證

軟體開發的核心原則稱為 DRY("Don't Repeat Yourself", 不重複原則)。 Razor 頁面可促進開發,只要指定功能一次,就能在整個應用程式中運用。 DRY 有助於:

  • 降低應用程式中的程式碼數量。
  • 使程式碼較少出現錯誤,而且更容易進行測試和維護。

Razor Pages 和 Entity Framework 所提供的驗證支援就是 DRY 準則的絶佳範例:

  • 驗證規則是在單一位置 (在模型類別中) 以宣告方式指定。
  • 規則會在應用程式中的任何位置強制執行。

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

System.ComponentModel.DataAnnotations 命名空間提供:

  • 一組內建的驗證屬性 (attribute),其以宣告方式套用至類別或屬性 (property)。
  • [DataType] 的格式化屬性,可協助進行格式化,但不提供驗證。

更新 Movie 類別,以充分利用內建的 [Required][StringLength][RegularExpression][Range] 驗證屬性。

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace RazorPagesMovie.Models
{
    public class Movie
    {
        public int ID { get; set; }

        [StringLength(60, MinimumLength = 3)]
        [Required]
        public string Title { get; set; } = string.Empty;

        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }

        [Range(1, 100)]
        [DataType(DataType.Currency)]
        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
        [Required]
        [StringLength(30)]
        public string Genre { get; set; } = string.Empty;

        [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
        [StringLength(5)]
        [Required]
        public string Rating { get; set; } = string.Empty;
    }
}

驗證屬性會指定要對套用目標模型屬性強制執行的行為:

  • [Required][MinimumLength] 屬性 (attribute) 指出屬性 (property) 必須具有值。 無任何動作可防止使用者輸入空白字元來滿足此驗證。

  • [RegularExpression] 屬性則用來限制可輸入的字元。 在上述程式碼中,Genre

    • 必須指使用字母。
    • 第一個字母必須是大寫。 允許空白字元,但不允許數字及特殊字元。
  • RegularExpression Rating

    • 第一個字元必須為大寫字母。
    • 允許後續空格中的特殊字元和數位。 "PG-13" 對分級而言有效,但不適用於 Genre
  • [Range] 屬性會將值限制在指定的範圍內。

  • [StringLength] 屬性可設定字串屬性的最大長度,並選擇性設定其最小長度。

  • 實值型別 (如decimalintfloatDateTime) 原本就是必要項目,而且不需要 [Required] 屬性。

上述驗證規則用於示範,它們不適合用於生產系統。 例如,上述會防止輸入只有兩個字元的電影,而且不允許在 Genre 中使用特殊字元。

ASP.NET Core 自動強制執行驗證規則有助於:

  • 讓應用程式更強固。
  • 減少將無效資料儲存至資料庫的機會。

Razor Pages 中的驗證錯誤 UI

執行應用程式,並巡覽至 Pages/Movies。

選取 Create New 連結。 使用某些無效值完成表單。 當 jQuery 用戶端驗證偵測到錯誤時,它會顯示錯誤訊息。

有多個 jQuery 用戶端驗證錯誤的電影檢視表單

注意

您可能無法在小數欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期欄位支援 jQuery 驗證,您必須採取將應用程式全球化的步驟。 請參閱此 GitHub 問題 4076 \(英文\),以取得加入十進位逗號的指示。

請注意表單在包含無效值的每個欄位中自動呈現驗證錯誤訊息的方式。 用戶端 (使用 JavaScript 和 jQuery) 與伺服器端 (當使用者已停用 JavaScript 時) 都會強制執行這些錯誤。

明顯的好處是:需要在 Create 或 Edit 頁面中進行程式碼變更。 一旦 DataAnnotations 套用至模型,就會啟用驗證 UI。 本教學課程中建立的 Razor Pages 會自動拾取驗證規則 (在 Movie 模型類別的屬性 (property) 上使用驗證屬性 (attribute))。 使用 Edit 頁面測試驗證,會套用相同的驗證。

要一直到沒有任何用戶端驗證錯誤之後,才會將表單資料發佈到伺服器。 請確認表單資料不會經由下列一或多種方式發佈:

  • 將中斷點放置在 OnPostAsync 方法中。 選取 [建立] 或 [儲存],以提交表單。 永遠不會叫用中斷點。
  • 使用 Fiddler 工具
  • 使用瀏覽器開發人員工具來監視網路流量。

伺服器端驗證

在瀏覽器中停用 JavaScript 時,提交含有錯誤的表單將發佈到伺服器。

選擇性地測試伺服器端驗證:

  1. 在瀏覽器中停用 JavaScript。 您可以使用瀏覽器的開發人員工具停用 JavaScript。 如果您無法在該瀏覽器中停用 JavaScript,請嘗試另一個瀏覽器。

  2. 在 Create 或 Edit 頁面的 OnPostAsync 方法中設定中斷點。

  3. 提交含有無效資料的表單。

  4. 確認模型狀態無效:

     if (!ModelState.IsValid)
     {
        return Page();
     }
    

或者,停用伺服器上的用戶端驗證

下列程式碼顯示稍早在此教學課程中包含 Scaffold 的部分 Create.cshtml 頁面。 建立和編輯頁面會使用它來:

  • 顯示初始表單。
  • 在發生錯誤時重新顯示表單。
<form method="post">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <div class="form-group">
        <label asp-for="Movie.Title" class="control-label"></label>
        <input asp-for="Movie.Title" class="form-control" />
        <span asp-validation-for="Movie.Title" class="text-danger"></span>
    </div>

輸入標記協助程式會使用 DataAnnotations 屬性,並產生在用戶端上進行 jQuery 驗證所需的 HTML 屬性。 驗證標記協助程式會顯示驗證錯誤。 如需詳細資訊,請參閱驗證

Create 和 Edit 頁面中沒有任何驗證規則。 只有在 Movie 類別中才能指定驗證規則和錯誤字串。 這些驗證規則會自動套用至編輯 Movie 模型的 Razor Pages。

當驗證邏輯需要變更時,它只會在模型中進行。 驗證會一致地套用在整個應用程式中,驗證邏輯定義於一個位置。 位於一個位置的驗證有助於讓程式碼保持整潔,並可讓您更容易進行維護和更新。

使用 DataType 屬性

檢查 Movie 類別。 除了一組內建的驗證屬性之外,System.ComponentModel.DataAnnotations 命名空間還提供了格式屬性。 [DataType] 屬性會套用到 ReleaseDatePrice 屬性。

[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }

[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }

[DataType] 屬性提供:

  • 檢視引擎格式化資料的提示。
  • 提供屬性,例如適用於 URL 的 <a>,以及適用於電子郵件的 <a href="mailto:EmailAddress.com">

使用 [RegularExpression] 屬性來驗證資料的格式。 [DataType] 屬性可用於指定比資料庫內建類型更特定的資料類型。 [DataType] 屬性不是驗證屬性。 在範例應用程式中,只會顯示日期,而不含時間。

DataType 列舉提供許多資料類型,例如 DateTimePhoneNumberCurrencyEmailAddress 等等。

[DataType] 屬性:

  • 可讓應用程式自動提供類型的特定功能。 例如,可針對 DataType.EmailAddress 建立 mailto: 連結。
  • 可以在支援 HTML5 的瀏覽器中提供日期選擇器 DataType.Date
  • 發出 HTML 5 data-,發音為「data dash」,這是 HTML 5 瀏覽器取用的屬性。
  • 請勿提供任何驗證。

DataType.Date 未指定顯示日期的格式。 根據預設,將依據以伺服器 CultureInfo 為基礎的預設格式顯示資料欄位。

[Column(TypeName = "decimal(18, 2)")] 資料註解為必要項,因此 Entity Framework Core 可將 Price 正確對應到資料庫中的貨幣。 如需詳細資訊,請參閱資料類型

[DisplayFormat] 屬性用來明確指定日期格式:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }

ApplyFormatInEditMode 設定可指定當顯示值以供編輯時,應該套用的格式。 某些欄位可能不想要該行為。 例如,在貨幣值中,通常不需要編輯 UI 中的貨幣符號。

[DisplayFormat] 屬性可以單獨使用,但通常最好是使用 [DataType] 屬性。 [DataType] 屬性會將資料的語意以其在螢幕上呈現方式的相反方式傳遞。 [DataType] 屬性提供了下列優點,並且這些優點在 [DisplayFormat] 中無法使用:

  • 瀏覽器可以啟用 HTML5 功能 (例如顯示日曆控制項、適合地區設定的貨幣符號、電子郵件連結等等)。
  • 根據預設,瀏覽器將根據地區設定,使用正確的格式呈現資料。
  • [DataType] 屬性可以啟用 ASP.NET Core 架構,來選擇用於呈現資料的正確欄位範本。 DisplayFormat 若是單獨使用,則會使用字串範本。

注意:jQuery 驗證不適用於 [Range] 屬性與 DateTime。 例如,下列程式碼一律會顯示用戶端驗證錯誤,即使當日期位在指定範圍中也一樣:

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]

最佳做法是避免在模型中編譯硬式日期,因此不建議使用 [Range] 屬性和 DateTime。 針對受限於頻繁變更的日期範圍和其他值使用組態,而不是在程式碼中指定。

下列程式碼會顯示一行上的結合屬性:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace RazorPagesMovie.Models
{
    public class Movie
    {
        public int ID { get; set; }

        [StringLength(60, MinimumLength = 3)]
        public string Title { get; set; } = string.Empty;

        [Display(Name = "Release Date"), DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$"), Required, StringLength(30)]
        public string Genre { get; set; } = string.Empty;

        [Range(1, 100), DataType(DataType.Currency)]
        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$"), StringLength(5)]
        public string Rating { get; set; } = string.Empty;
    }
}

開始使用 Razor Pages 和 EF Core 會顯示使用 Razor Pages 的進階 EF Core 作業。

套用移轉

套用至類別的 DataAnnotations 會變更結構描述。 例如,套用至 Title 欄位的 DataAnnotations:

[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; } = string.Empty;
  • 將字元限制為 60 個。
  • 不允許 null 值。

Movie 資料表目前具有下列結構描述:

CREATE TABLE [dbo].[Movie] (
    [ID]          INT             IDENTITY (1, 1) NOT NULL,
    [Title]       NVARCHAR (MAX)  NULL,
    [ReleaseDate] DATETIME2 (7)   NOT NULL,
    [Genre]       NVARCHAR (MAX)  NULL,
    [Price]       DECIMAL (18, 2) NOT NULL,
    [Rating]      NVARCHAR (MAX)  NULL,
    CONSTRAINT [PK_Movie] PRIMARY KEY CLUSTERED ([ID] ASC)
);

上述結構描述變更不會造成 EF 擲回例外狀況。 不過,請建立移轉,讓結構描述與模型一致。

從 [工具] 功能表中,選取 [NuGet 套件管理員] > [套件管理員主控台]。 在 PMC 中,輸入下列命令:

Add-Migration New_DataAnnotations
Update-Database

Update-Database 會執行 New_DataAnnotations 類別的 Up 方法。

檢查 Up 方法:

public partial class New_DataAnnotations : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AlterColumn<string>(
            name: "Title",
            table: "Movie",
            type: "nvarchar(60)",
            maxLength: 60,
            nullable: false,
            oldClrType: typeof(string),
            oldType: "nvarchar(max)");

        migrationBuilder.AlterColumn<string>(
            name: "Rating",
            table: "Movie",
            type: "nvarchar(5)",
            maxLength: 5,
            nullable: false,
            oldClrType: typeof(string),
            oldType: "nvarchar(max)");

        migrationBuilder.AlterColumn<string>(
            name: "Genre",
            table: "Movie",
            type: "nvarchar(30)",
            maxLength: 30,
            nullable: false,
            oldClrType: typeof(string),
            oldType: "nvarchar(max)");
    }

已更新的 Movie 資料表具有下列結構描述:

CREATE TABLE [dbo].[Movie] (
    [ID]          INT             IDENTITY (1, 1) NOT NULL,
    [Title]       NVARCHAR (60)   NOT NULL,
    [ReleaseDate] DATETIME2 (7)   NOT NULL,
    [Genre]       NVARCHAR (30)   NOT NULL,
    [Price]       DECIMAL (18, 2) NOT NULL,
    [Rating]      NVARCHAR (5)    NOT NULL,
    CONSTRAINT [PK_Movie] PRIMARY KEY CLUSTERED ([ID] ASC)
);

發佈至 Azure

如需部署至 Azure 的資訊,請參閱教學課程:使用 SQL Database 在 Azure 中建置 ASP.NET Core 應用程式

感謝您看完這份 Razor Pages 簡介。 完成本教學課程之後,非常建議您繼續參閱 Razor 頁面與 EF Core 使用者入門。

其他資源

下一步

在本節中將驗證邏輯新增至 Movie 模型。 使用者建立或編輯電影時,就會強制執行驗證規則。

驗證

軟體開發的核心原則稱為 DRY("Don't Repeat Yourself", 不重複原則)。 Razor 頁面可促進開發,只要指定功能一次,就能在整個應用程式中運用。 DRY 有助於:

  • 降低應用程式中的程式碼數量。
  • 使程式碼較少出現錯誤,而且更容易進行測試和維護。

Razor Pages 和 Entity Framework 所提供的驗證支援就是 DRY 準則的絶佳範例:

  • 驗證規則是在單一位置 (在模型類別中) 以宣告方式指定。
  • 規則會在應用程式中的任何位置強制執行。

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

DataAnnotations 命名空間提供:

  • 一組內建的驗證屬性 (attribute),其以宣告方式套用至類別或屬性 (property)。
  • [DataType] 的格式化屬性,可協助進行格式化,但不提供驗證。

更新 Movie 類別,以充分利用內建的 [Required][StringLength][RegularExpression][Range] 驗證屬性。

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace RazorPagesMovie.Models
{
    public class Movie
    {
        public int ID { get; set; }

        [StringLength(60, MinimumLength = 3)]
        [Required]
        public string Title { get; set; }

        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }

        [Range(1, 100)]
        [DataType(DataType.Currency)]
        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
        [Required]
        [StringLength(30)]
        public string Genre { get; set; }

        [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
        [StringLength(5)]
        [Required]
        public string Rating { get; set; }
    }
}

驗證屬性會指定要對套用目標模型屬性強制執行的行為:

  • [Required][MinimumLength] 屬性 (attribute) 指出屬性 (property) 必須具有值。 無任何動作可防止使用者輸入空白字元來滿足此驗證。

  • [RegularExpression] 屬性則用來限制可輸入的字元。 在上述程式碼中,Genre

    • 必須指使用字母。
    • 第一個字母必須是大寫。 允許空白字元,但不允許數字及特殊字元。
  • RegularExpression Rating

    • 第一個字元必須為大寫字母。
    • 允許後續空格中的特殊字元和數位。 "PG-13" 對分級而言有效,但不適用於 Genre
  • [Range] 屬性會將值限制在指定的範圍內。

  • [StringLength] 屬性可設定字串屬性的最大長度,並選擇性設定其最小長度。

  • 實值型別 (如decimalintfloatDateTime) 原本就是必要項目,而且不需要 [Required] 屬性。

上述驗證規則用於示範,它們不適合用於生產系統。 例如,上述會防止輸入只有兩個字元的電影,而且不允許在 Genre 中使用特殊字元。

ASP.NET Core 自動強制執行驗證規則有助於:

  • 讓應用程式更強固。
  • 減少將無效資料儲存至資料庫的機會。

Razor Pages 中的驗證錯誤 UI

執行應用程式,並巡覽至 Pages/Movies。

選取 Create New 連結。 使用某些無效值完成表單。 當 jQuery 用戶端驗證偵測到錯誤時,它會顯示錯誤訊息。

有多個 jQuery 用戶端驗證錯誤的電影檢視表單

注意

您可能無法在小數欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期欄位支援 jQuery 驗證,您必須採取將應用程式全球化的步驟。 請參閱此 GitHub 問題 4076 \(英文\),以取得加入十進位逗號的指示。

請注意表單在包含無效值的每個欄位中自動呈現驗證錯誤訊息的方式。 用戶端 (使用 JavaScript 和 jQuery) 與伺服器端 (當使用者已停用 JavaScript 時) 都會強制執行這些錯誤。

明顯的好處是:需要在 Create 或 Edit 頁面中進行程式碼變更。 一旦 DataAnnotations 套用至模型,就會啟用驗證 UI。 本教學課程中建立的 Razor Pages 會自動拾取驗證規則 (在 Movie 模型類別的屬性 (property) 上使用驗證屬性 (attribute))。 使用 Edit 頁面測試驗證,會套用相同的驗證。

要一直到沒有任何用戶端驗證錯誤之後,才會將表單資料發佈到伺服器。 請確認表單資料不會經由下列一或多種方式發佈:

  • 將中斷點放置在 OnPostAsync 方法中。 選取 [建立] 或 [儲存],以提交表單。 永遠不會叫用中斷點。
  • 使用 Fiddler 工具
  • 使用瀏覽器開發人員工具來監視網路流量。

伺服器端驗證

在瀏覽器中停用 JavaScript 時,提交含有錯誤的表單將發佈到伺服器。

選擇性地測試伺服器端驗證:

  1. 在瀏覽器中停用 JavaScript。 您可以使用瀏覽器的開發人員工具停用 JavaScript。 如果瀏覽器中無法停用 JavaScript,請嘗試另一個瀏覽器。

  2. 在 Create 或 Edit 頁面的 OnPostAsync 方法中設定中斷點。

  3. 提交含有無效資料的表單。

  4. 確認模型狀態無效:

     if (!ModelState.IsValid)
     {
        return Page();
     }
    

或者,停用伺服器上的用戶端驗證

下列程式碼顯示稍早在此教學課程中包含 Scaffold 的部分 Create.cshtml 頁面。 建立和編輯頁面會使用它來:

  • 顯示初始表單。
  • 在發生錯誤時重新顯示表單。
<form method="post">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <div class="form-group">
        <label asp-for="Movie.Title" class="control-label"></label>
        <input asp-for="Movie.Title" class="form-control" />
        <span asp-validation-for="Movie.Title" class="text-danger"></span>
    </div>

輸入標記協助程式會使用 DataAnnotations 屬性,並產生在用戶端上進行 jQuery 驗證所需的 HTML 屬性。 驗證標記協助程式會顯示驗證錯誤。 如需詳細資訊,請參閱驗證

Create 和 Edit 頁面中沒有任何驗證規則。 只有在 Movie 類別中才能指定驗證規則和錯誤字串。 這些驗證規則會自動套用至編輯 Movie 模型的 Razor Pages。

當驗證邏輯需要變更時,它只會在模型中進行。 驗證會一致地套用在整個應用程式中,驗證邏輯定義於一個位置。 位於一個位置的驗證有助於讓程式碼保持整潔,並可讓您更容易進行維護和更新。

使用 DataType 屬性

檢查 Movie 類別。 除了一組內建的驗證屬性之外,System.ComponentModel.DataAnnotations 命名空間還提供了格式屬性。 [DataType] 屬性會套用到 ReleaseDatePrice 屬性。

[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }

[Range(1, 100)]
[DataType(DataType.Currency)]
public decimal Price { get; set; }

[DataType] 屬性提供:

  • 檢視引擎格式化資料的提示。
  • 提供屬性,例如適用於 URL 的 <a>,以及適用於電子郵件的 <a href="mailto:EmailAddress.com">

使用 [RegularExpression] 屬性來驗證資料的格式。 [DataType] 屬性可用於指定比資料庫內建類型更特定的資料類型。 [DataType] 屬性不是驗證屬性。 在範例應用程式中,只會顯示日期,而不含時間。

DataType 列舉提供許多資料類型,例如 DateTimePhoneNumberCurrencyEmailAddress 等等。

[DataType] 屬性:

  • 可讓應用程式自動提供類型的特定功能。 例如,可針對 DataType.EmailAddress 建立 mailto: 連結。
  • 可以在支援 HTML5 的瀏覽器中提供日期選擇器 DataType.Date
  • 發出 HTML 5 data-,發音為「data dash」,這是 HTML 5 瀏覽器取用的屬性。
  • 請勿提供任何驗證。

DataType.Date 未指定顯示日期的格式。 根據預設,將依據以伺服器 CultureInfo 為基礎的預設格式顯示資料欄位。

[Column(TypeName = "decimal(18, 2)")] 資料註解為必要項,因此 Entity Framework Core 可將 Price 正確對應到資料庫中的貨幣。 如需詳細資訊,請參閱資料類型

[DisplayFormat] 屬性用來明確指定日期格式:

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }

ApplyFormatInEditMode 設定可指定當顯示值以供編輯時,應該套用的格式。 某些欄位可能不想要該行為。 例如,在貨幣值中,通常不需要編輯 UI 中的貨幣符號。

[DisplayFormat] 屬性可以單獨使用,但通常最好是使用 [DataType] 屬性。 [DataType] 屬性會將資料的語意以其在螢幕上呈現方式的相反方式傳遞。 [DataType] 屬性提供了下列優點,並且這些優點在 [DisplayFormat] 中無法使用:

  • 瀏覽器可以啟用 HTML5 功能 (例如顯示日曆控制項、適合地區設定的貨幣符號、電子郵件連結等等)。
  • 根據預設,瀏覽器將根據地區設定,使用正確的格式呈現資料。
  • [DataType] 屬性可以啟用 ASP.NET Core 架構,來選擇用於呈現資料的正確欄位範本。 DisplayFormat 若是單獨使用,則會使用字串範本。

注意:jQuery 驗證不適用於 [Range] 屬性與 DateTime。 例如,下列程式碼一律會顯示用戶端驗證錯誤,即使當日期位在指定範圍中也一樣:

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]

最佳做法是避免在模型中編譯硬式日期,因此不建議使用 [Range] 屬性和 DateTime。 針對受限於頻繁變更的日期範圍和其他值使用組態,而不是在程式碼中指定。

下列程式碼會顯示一行上的結合屬性:

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

    [StringLength(60, MinimumLength = 3)]
    public string Title { get; set; }

    [Display(Name = "Release Date"), DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$"), Required, StringLength(30)]
    public string Genre { get; set; }

    [Range(1, 100), DataType(DataType.Currency)]
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }

    [RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$"), StringLength(5)]
    public string Rating { get; set; }
}

開始使用 Razor Pages 和 EF Core 會顯示使用 Razor Pages 的進階 EF Core 作業。

套用移轉

套用至類別的 DataAnnotations 會變更結構描述。 例如,套用至 Title 欄位的 DataAnnotations:

[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; }
  • 將字元限制為 60 個。
  • 不允許 null 值。

Movie 資料表目前具有下列結構描述:

CREATE TABLE [dbo].[Movie] (
    [ID]          INT             IDENTITY (1, 1) NOT NULL,
    [Title]       NVARCHAR (MAX)  NULL,
    [ReleaseDate] DATETIME2 (7)   NOT NULL,
    [Genre]       NVARCHAR (MAX)  NULL,
    [Price]       DECIMAL (18, 2) NOT NULL,
    [Rating]      NVARCHAR (MAX)  NULL,
    CONSTRAINT [PK_Movie] PRIMARY KEY CLUSTERED ([ID] ASC)
);

上述結構描述變更不會造成 EF 擲回例外狀況。 不過,請建立移轉,讓結構描述與模型一致。

從 [工具] 功能表中,選取 [NuGet 套件管理員] > [套件管理員主控台]。 在 PMC 中,輸入下列命令:

Add-Migration New_DataAnnotations
Update-Database

Update-Database 會執行 New_DataAnnotations 類別的 Up 方法。 檢查 Up 方法:

public partial class New_DataAnnotations : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AlterColumn<string>(
            name: "Title",
            table: "Movie",
            maxLength: 60,
            nullable: false,
            oldClrType: typeof(string),
            oldNullable: true);

        migrationBuilder.AlterColumn<string>(
            name: "Rating",
            table: "Movie",
            maxLength: 5,
            nullable: false,
            oldClrType: typeof(string),
            oldNullable: true);

        migrationBuilder.AlterColumn<string>(
            name: "Genre",
            table: "Movie",
            maxLength: 30,
            nullable: false,
            oldClrType: typeof(string),
            oldNullable: true);
    }

已更新的 Movie 資料表具有下列結構描述:

CREATE TABLE [dbo].[Movie] (
    [ID]          INT             IDENTITY (1, 1) NOT NULL,
    [Title]       NVARCHAR (60)   NOT NULL,
    [ReleaseDate] DATETIME2 (7)   NOT NULL,
    [Genre]       NVARCHAR (30)   NOT NULL,
    [Price]       DECIMAL (18, 2) NOT NULL,
    [Rating]      NVARCHAR (5)    NOT NULL,
    CONSTRAINT [PK_Movie] PRIMARY KEY CLUSTERED ([ID] ASC)
);

發佈至 Azure

如需部署至 Azure 的資訊,請參閱教學課程:使用 SQL Database 在 Azure 中建置 ASP.NET Core 應用程式

感謝您看完這份 Razor Pages 簡介。 完成本教學課程之後,非常建議您繼續參閱 Razor 頁面與 EF Core 使用者入門。

可靠的 Web 應用程式模式

請參閱 The Reliable Web App Pattern for.NET YouTube 影片文章,以取得從頭建立新式、可靠、效能強、可測試、具成本效益及可調整的 ASP.NET Core 應用程式或重構現有應用程式的指導。

其他資源

下一步