將驗(yàn)證添加到 ASP.NET Core MVC 應(yīng)用

2019-04-17 08:57 更新

本節(jié)內(nèi)容:

  • 向 Movie 模型添加了驗(yàn)證邏輯。
  • 確保每當(dāng)用戶創(chuàng)建或編輯電影時(shí),都會(huì)強(qiáng)制執(zhí)行驗(yàn)證規(guī)則。

堅(jiān)持 DRY 原則

MVC 的設(shè)計(jì)原則之一是 DRY(“不要自我重復(fù)”)。 ASP.NET Core MVC 支持你僅指定一次功能或行為,然后使它應(yīng)用到整個(gè)應(yīng)用中。 這可以減少所需編寫的代碼量,并使編寫的代碼更少出錯(cuò),更易于測試和維護(hù)。

MVC 和 Entity Framework Core Code First 提供的驗(yàn)證支持是 DRY 原則在實(shí)際操作中的極佳示例??梢栽谝粋€(gè)位置(模型類中)以聲明方式指定驗(yàn)證規(guī)則,并且在應(yīng)用中的所有位置強(qiáng)制執(zhí)行。

將驗(yàn)證規(guī)則添加到電影模型

打開 Movie.cs 文件。 DataAnnotations 提供一組內(nèi)置驗(yàn)證特性,可通過聲明方式應(yīng)用于任何類或?qū)傩浴?nbsp;(它還包含 DataType 等格式特性,這些特性可幫助進(jìn)行格式設(shè)置,但不提供任何驗(yàn)證。)

更新 Movie 類以使用內(nèi)置的 Required、StringLength、RegularExpression 和 Range 驗(yàn)證特性。

C#

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

驗(yàn)證特性指定要對應(yīng)用這些特性的模型屬性強(qiáng)制執(zhí)行的行為:

  • Required 和 MinimumLength 特性表示屬性必須有值;但用戶可輸入空格來滿足此驗(yàn)證。
  • RegularExpression 特性用于限制可輸入的字符。 在上述代碼中,Genre 和 Rating 必須使用純字母(禁用首字母大寫、空格、數(shù)字和特殊字符)。
  • Range 特性將值限制在指定范圍內(nèi)。
  • StringLength 特性使你能夠設(shè)置字符串屬性的最大長度,以及可選的最小長度。
  • 從本質(zhì)上來說,需要值類型(如 decimal、int、float、DateTime),但不需要 [Required] 特性。

讓 ASP.NET Core 強(qiáng)制自動(dòng)執(zhí)行驗(yàn)證規(guī)則有助于提升你的應(yīng)用的可靠性。 同時(shí)它能確保你無法忘記驗(yàn)證某些內(nèi)容,并防止你無意中將錯(cuò)誤數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫。

MVC 中的驗(yàn)證錯(cuò)誤 UI

運(yùn)行應(yīng)用并導(dǎo)航到電影控制器。

點(diǎn)擊“新建”連接添加新電影的鏈接。 使用無效值填寫表單。 當(dāng) jQuery 客戶端驗(yàn)證檢測到錯(cuò)誤時(shí),會(huì)顯示一條錯(cuò)誤消息。

帶有多個(gè) jQuery 客戶端驗(yàn)證錯(cuò)誤的電影視圖表單

 備注

可能無法在小數(shù)字段中輸入十進(jìn)制逗號。 若要使 jQuery 驗(yàn)證支持使用逗號(“,”)表示小數(shù)點(diǎn)的的非英語區(qū)域設(shè)置,以及支持非美國英語日期格式,必須執(zhí)行使應(yīng)用全球化的步驟。 有關(guān)添加十進(jìn)制逗號的說明,請參閱 GitHub 問題 4076。

請注意表單如何自動(dòng)呈現(xiàn)每個(gè)包含無效值的字段中相應(yīng)的驗(yàn)證錯(cuò)誤消息。 客戶端(使用 JavaScript 和 jQuery)和服務(wù)器端(若用戶禁用 JavaScript)都必定會(huì)遇到這些錯(cuò)誤。

明顯的好處在于不需要在 MoviesController 類或 Create.cshtml 視圖中更改單個(gè)代碼行來啟用此驗(yàn)證 UI。 在本教程前面創(chuàng)建的控制器和視圖會(huì)自動(dòng)選取驗(yàn)證規(guī)則,這些規(guī)則是通過在 Movie 模型類的屬性上使用驗(yàn)證特性所指定的。 使用 Edit 操作方法測試驗(yàn)證后,即已應(yīng)用相同的驗(yàn)證。

存在客戶端驗(yàn)證錯(cuò)誤時(shí),不會(huì)將表單數(shù)據(jù)發(fā)送到服務(wù)器。 可通過使用 Fiddler 工具或 F12 開發(fā)人員工具在 HTTP Post 方法中設(shè)置斷點(diǎn)來對此進(jìn)行驗(yàn)證。

驗(yàn)證工作原理

你可能想知道在不對控制器或視圖中的代碼進(jìn)行任何更新的情況下,驗(yàn)證 UI 是如何生成的。 下列代碼顯示兩種 Create 方法。

C#

// GET: Movies/Create
public IActionResult Create()
{
    return View();
}

// POST: Movies/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
    [Bind("ID,Title,ReleaseDate,Genre,Price, Rating")] Movie movie)
{
    if (ModelState.IsValid)
    {
        _context.Add(movie);
        await _context.SaveChangesAsync();
        return RedirectToAction("Index");
    }
    return View(movie);
}

第一個(gè) (HTTP GET) Create 操作方法顯示初始的“創(chuàng)建”表單。 第二個(gè) ([HttpPost]) 版本處理表單發(fā)布。 第二個(gè) Create 方法([HttpPost] 版本)調(diào)用 ModelState.IsValid 以檢查電影是否有任何驗(yàn)證錯(cuò)誤。 調(diào)用此方法將評估已應(yīng)用于對象的任何驗(yàn)證特性。 如果對象有驗(yàn)證錯(cuò)誤,則 Create 方法會(huì)重新顯示此表單。 如果沒有錯(cuò)誤,此方法則將新電影保存在數(shù)據(jù)庫中。 在我們的電影示例中,在檢測到客戶端上存在驗(yàn)證錯(cuò)誤時(shí),表單不會(huì)發(fā)布到服務(wù)器。當(dāng)存在客戶端驗(yàn)證錯(cuò)誤時(shí),第二個(gè) Create 方法永遠(yuǎn)不會(huì)被調(diào)用。 如果在瀏覽器中禁用 JavaScript,客戶端驗(yàn)證將被禁用,而你可以測試 HTTP POST Create 方法 ModelState.IsValid 檢測任何驗(yàn)證錯(cuò)誤。

可以在 [HttpPost] Create 方法中設(shè)置斷點(diǎn),并驗(yàn)證方法從未被調(diào)用,客戶端驗(yàn)證在檢測到存在驗(yàn)證錯(cuò)誤時(shí)不會(huì)提交表單數(shù)據(jù)。 如果在瀏覽器中禁用 JavaScript,然后提交錯(cuò)誤的表單,將觸發(fā)斷點(diǎn)。 在沒有 JavaScript 的情況下仍然可以進(jìn)行完整的驗(yàn)證。

以下圖片顯示如何在 FireFox 瀏覽器中禁用 JavaScript。

Firefox:在“選項(xiàng)”的“內(nèi)容”選項(xiàng)卡上,取消選中“啟用 Javascript”復(fù)選框。

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

Google Chrome:在“內(nèi)容”設(shè)置的 Javascript 部分中,選擇“不允許任何網(wǎng)站運(yùn)行 JavaScript”。

禁用 JavaScript 后,發(fā)布無效數(shù)據(jù)并單步執(zhí)行調(diào)試程序。

在對無效數(shù)據(jù)進(jìn)行調(diào)試時(shí),ModelState.IsValid 上的 Intellisense 顯示值為 false。

Create.cshtml 視圖模板的一部分在以下標(biāo)記中顯示:

HTML


<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>           
       
        @*Markup removed for brevity.*@

操作方法使用上述標(biāo)記來顯示初始表單,并在發(fā)生錯(cuò)誤時(shí)重新顯示此表單。

輸入標(biāo)記幫助程序使用 DataAnnotations 特性,并在客戶端上生成 jQuery 驗(yàn)證所需的 HTML 特性。驗(yàn)證標(biāo)記幫助程序用于顯示驗(yàn)證錯(cuò)誤。 有關(guān)詳細(xì)信息,請參閱驗(yàn)證。

此方法真正好的一點(diǎn)是:無論是控制器還是 Create 視圖模板都不知道強(qiáng)制實(shí)施的實(shí)際驗(yàn)證規(guī)則或顯示的特定錯(cuò)誤消息。 僅可在 Movie 類中指定驗(yàn)證規(guī)則和錯(cuò)誤字符串。 這些相同的驗(yàn)證規(guī)則自動(dòng)應(yīng)用于 Edit 視圖和可能創(chuàng)建用于編輯模型的任何其他視圖模板。

需要更改驗(yàn)證邏輯時(shí),可以通過將驗(yàn)證特性添加到模型在同一個(gè)位置實(shí)現(xiàn)此操作。(在此示例中為 Movie 類)。 無需擔(dān)心對應(yīng)用程序的不同部分所強(qiáng)制執(zhí)行規(guī)則的方式不一致 - 所有驗(yàn)證邏輯都將定義在一個(gè)位置并用于整個(gè)應(yīng)用程序。 這使代碼非常簡潔,并且更易于維護(hù)和改進(jìn)。 這意味著對 DRY 原則的完全遵守。

使用 DataType 特性

打開 Movie.cs 文件并檢查 Movie 類。 除了一組內(nèi)置的驗(yàn)證特性,System.ComponentModel.DataAnnotations 命名空間還提供格式特性。 我們已經(jīng)在發(fā)布日期和價(jià)格字段中應(yīng)用了 DataType 枚舉值。 以下代碼顯示具有適當(dāng) DataType 特性的 ReleaseDate 和 Price 屬性。

C#

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

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

DataType 屬性僅提供相關(guān)提示來幫助視圖引擎設(shè)置數(shù)據(jù)格式(并提供元素/屬性,例如向 URL 提供 <a> 和向電子郵件提供 <a href="mailto:EmailAddress.com">)。 可以使用 RegularExpression 特性驗(yàn)證數(shù)據(jù)的格式。 DataType 屬性用于指定比數(shù)據(jù)庫內(nèi)部類型更具體的數(shù)據(jù)類型,它們不是驗(yàn)證屬性。 在此示例中,我們只想跟蹤日期,而不是時(shí)間。 DataType 枚舉提供了多種數(shù)據(jù)類型,例如日期、時(shí)間、電話號碼、貨幣、電子郵件地址等。 應(yīng)用程序還可通過 DataType 特性自動(dòng)提供類型特定的功能。 例如,可以為 DataType.EmailAddress 創(chuàng)建 mailto: 鏈接,并且可以在支持 HTML5 的瀏覽器中為 DataType.Date 提供日期選擇器。 DataType 特性發(fā)出 HTML 5 data-(讀作 data dash)特性供 HTML 5 瀏覽器理解。 DataType 特性不提供任何驗(yàn)證。

DataType.Date 不指定顯示日期的格式。 默認(rèn)情況下,數(shù)據(jù)字段根據(jù)基于服務(wù)器的 CultureInfo 的默認(rèn)格式進(jìn)行顯示。

DisplayFormat 特性用于顯式指定日期格式:

C#

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

ApplyFormatInEditMode 設(shè)置指定在文本框中顯示值以進(jìn)行編輯時(shí)也應(yīng)用格式。 (你可能不想為某些字段執(zhí)行此操作 — 例如對于貨幣值,你可能不希望文本框中的貨幣符號可編輯。)

可以單獨(dú)使用 DisplayFormat 特性,但通常建議使用 DataType 特性。 DataType 特性傳達(dá)數(shù)據(jù)的語義而不是傳達(dá)如何在屏幕上呈現(xiàn)數(shù)據(jù),并提供 DisplayFormat 不具備的以下優(yōu)勢:

  • 瀏覽器可啟用 HTML5 功能(例如顯示日歷控件、區(qū)域設(shè)置適用的貨幣符號、電子郵件鏈接等)
  • 默認(rèn)情況下,瀏覽器將根據(jù)區(qū)域設(shè)置采用正確的格式呈現(xiàn)數(shù)據(jù)。
  • DataType 特性使 MVC 能夠選擇正確的字段模板來呈現(xiàn)數(shù)據(jù)(如果 DisplayFormat 由自身使用,則使用的是字符串模板)。

 備注

jQuery 驗(yàn)證不適用于 Range 屬性和 DateTime。 例如,以下代碼將始終顯示客戶端驗(yàn)證錯(cuò)誤,即便日期在指定的范圍內(nèi):

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

需要禁用 jQuery 日期驗(yàn)證才能使用具有 DateTime 的 Range 特性。 通常,在模型中編譯固定日期是不恰當(dāng)?shù)?,因此不推薦使用 Range 特性和 DateTime。

以下代碼顯示組合在一行上的特性:

C#

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

在本系列的下一部分中,我們將回顧應(yīng)用,并對自動(dòng)生成的 Details 和 Delete 方法進(jìn)行一些改進(jìn)。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號