原文: "Adding Validation" 作者: "Rick Anderson" 翻譯: "謝煬(Kiler)" 校對: "孟帥洋(書緣)" 、 "婁宇(Lyrics)" 、 "許登洋(Seay)" 在本章節中你將為 模型類添加驗證邏輯,以確保在用戶試圖創建或編輯影片數據時強制執行驗證規則。 ...
原文:Adding Validation
作者:Rick Anderson
翻譯:謝煬(Kiler)
校對:孟帥洋(書緣)、婁宇(Lyrics)、許登洋(Seay)
在本章節中你將為 Movie
模型類添加驗證邏輯,以確保在用戶試圖創建或編輯影片數據時強制執行驗證規則。
保持DRY原則
ASP.NET MVC 的核心原則之一是 DRY (“不要重覆自己”)。ASP.NET MVC 鼓勵你只指定一次行為或者功能,然後可以在應用程式裡面到處使用,這樣大大的減少了需要編寫的代碼量,並且使你編寫不容易出錯,更容易測試,以及更容易維護的代碼。
ASP.NET MVC 和 Entity Framework Core Code First 中的驗證功能,是 DRY 原則實際應用的一個很好的實例。你可以在某個位置(模型類)聲明指定方式的驗證規則,驗證規則可以在整個應用程式中生效。
讓我們來看看如何在電影應用程式中利用驗證功能。
向 Movie 模型中添加驗證規則
打開 Movie.cs 文件。DataAnnotations 提供了內置的驗證屬性,你可以對任何類或屬性應用。(它也同時包含了一些格式化屬性比如 DataType
用來幫你格式化數據而非提供驗證功能。)
現在修改 Movie
類,利用內置的 Required
、 StringLength
、RegularExpression
以及 Range
驗證屬性。
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)] //手工高亮
public decimal Price { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")] //手工高亮
[StringLength(5)] //手工高亮
public string Rating { get; set; }
}
驗證特性指定了你想要應用到模型屬性上的驗證行為。Required
以及 MinimumLength
屬性表示屬性不能為空,但無法阻止用戶用填寫空格的方式來滿足此驗證條件。RegularExpression
屬性用來限制用戶輸入的文字類型。 在上面的例子中,Genre
以及 Rating
只能輸入字母(不允許輸入空格、數字以及特殊字元)。Range
屬性限制值在指定的範圍內。StringLength
屬性可讓你設定字元串最大長度,以及最小長度(可選)。值類型(如decimal, int, float, DateTime)預設情況下,並不需要 [Required]
屬性。
ASP.NET 自動執行的驗證規則將有助於使你的應用程式更加健壯。它還確保提醒你不要忘記驗證數據,讓非法數據進入到資料庫中。
MVC 中的驗證錯誤 UI
運行程式並導航到 Movies controller。
點擊 Create New 鏈接創建一個新的 Movie。在表單中填寫一些無效的數據,jQuery 客戶端驗證馬上就會發現錯誤,立刻呈現到界面上。
註意
你也許無法在Price
欄位中輸入小數點或者逗號。為了讓 jQuery validation 支持非英語環境使用逗號(",")代替小數點,以及非美式英語日期格式,你必須採取措施國際化你的應用程式。參考 附錄資源 獲取更多信息。 現在僅僅輸入整數,比如 10。
請註意,表單自動使用紅色邊框突出顯示包含無效的數據的文本框,併在每一個旁邊提示適當的驗證錯誤消息。錯誤包括客戶端(使用 JavaScript 和 jQuery )和伺服器端(以防用戶已禁用 JavaScript)。
一個顯而易見的好處是,你並不需要改變 MoviesController
類或者 Create.cshtml 視圖中的一行代碼,就可以實現驗證界面。你在本教程前面創建的控制器和視圖,自動使用你指定的 Movie
模型類的屬性上的驗證屬性的驗證規則。使用 Edit
Action 方法測試驗證,(與Create 方法)同樣的驗證生效。
表單數據不會被髮送到伺服器直到沒有客戶端驗證錯誤。你可以通過在 HTTP Post
方法中設置一個斷點來驗證這一點,通過使用 Fiddler 工具或者 F12 開發者工具。
創建視圖和創建方法中如何觸發驗證
也許你會好奇生成的控制器或視圖中的代碼沒有任何更新的情況下驗證界面是如何產生的。下一個清單顯示的是兩個 Create
方法。
// GET: Movies/Create
public IActionResult Create()
{
return View();
}
// POST: Movies/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ID,Genre,Price,ReleaseDate,Title,Rating")] Movie movie)
{
if (ModelState.IsValid)
{
_context.Add(movie);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(movie);
}
第一個(HTTP GET) Create
的操作方法顯示初始創建表單。第二個([HttpPost]
)版本的操作方法負責處理 post 請求。第二個 Create
的方法(HttpPost 版本)調用 ModelState.IsValid
檢查是否有任何驗證錯誤。調用該方法將檢查任何已應用到對象屬性上的驗證。如果對象的 Create
方法驗證錯誤,重新顯示表單。如果沒有錯誤,該方法在資料庫中保存新的 movie。在我們的 movie 例子中,在客戶端驗證檢測到的錯誤的時候,表單將不會發送到伺服器,第二個 Create
方法不會被調用。如果你在你的瀏覽器禁用了JavaScript,客戶端驗證被禁用,HTTP POST 版本的 Create
方法調用 ModelState.IsValid
,以檢查是否存在任何驗證錯誤。
你可以在 [HttpPost] Create
方法中設置一個斷點,用來驗證該方法不會被調用,客戶端驗證發現錯誤時將不提交表單數據。如果你在你的瀏覽器禁用了 JavaScript ,然後提交有錯誤的表單,斷點會命中。不支持 JavaScript 的情況下,你仍然可以得到充分驗證。下麵的圖片展示瞭如何在 IE 瀏覽器中禁用 JavaScript 腳本。
下麵的圖片展示瞭如何在 FireFox 瀏覽器中禁用 JavaScript 腳本。
下麵的圖片展示瞭如何在 Chrome 瀏覽器中禁用 JavaScript 腳本。
在禁用 JavaScript 腳本之後,post 非法數據併在調試器中單步調試。
下麵是你在本教程前面用基架生成的 Create.cshtml 視圖模板的一部分。它被上面兩種顯示的兩種 action 方法用來顯示初始化表單並且在錯誤事件中重新顯示。
<form asp-action="Create">
<div class="form-horizontal">
<h4>Movie</h4>
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Genre" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Genre" class="form-control" /> <!--手工高亮-->
<span asp-validation-for="Genre" class="text-danger" /> <!--手工高亮-->
</div>
</div>
@*Markup removed for brevity.*@ <!--手工高亮-->
<div class="form-group">
<label asp-for="Rating" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Rating" class="form-control" /> <!--手工高亮-->
<span asp-validation-for="Rating" class="text-danger" /> <!--手工高亮-->
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
</form>
Input Tag Helper 消費 DataAnnotations 屬性和產生jQuery驗證所需要在客戶端生成的HTML屬性。Validation Tag Helper 負責顯示錯誤信息。更多請參考:Validation 。
非常棒的是,控制器和 Create
視圖模板不知道實際執行的驗證規則或顯示的具體錯誤消息。只需要在 Movie
類里指定驗證規則和錯誤字元串,同樣的驗證規則會自動應用到 Edit
視圖和任何其他視圖模板,你可以創建、編輯您的模型。
如果你想更改驗證邏輯,你可以限定在一處地方(在本例中,是指 Movie 類),為模型添加驗證屬性。你不需要擔心應用程式的不同部分執行的規則不一致, 在應用在各個處的所有的驗證邏輯將被定義在一個地方。這樣可以使得代碼很乾凈,而且易於維護和改進。這意味著,你會充分遵循了 DRY 原則。
使用 DataType 屬性
打開 Movie.cs 文件,並查看 Movie
類。System.ComponentModel.DataAnnotations
命名空間提供了除了內置的驗證屬性以外的一套格式化屬性。我們已經應用了 DataType
枚舉值的到發佈日期和價格欄位。下麵的代碼顯示了 ReleaseDate
和 Price
欄位如何使用 DataType
屬性。
[Display(Name = "Release Date")]
[DataType(DataType.Date)] //手工高亮
public DateTime ReleaseDate { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)] //手工高亮
public decimal Price { get; set; }
DataType
屬性只用於視圖引擎對數據進行格式化(或者提供給諸如 <A>
標簽 的Url或者 <a href="mailto:EmailAddress.com">
提供的電子郵件),你可以使用 RegularExpression
屬性來驗證數據的格式,DataType
屬性用於指定比資料庫的自帶類型更為具體的數據類型,它們不驗證屬性,在本示例中我們只想跟蹤日期,而不需要具體時間。在 DataType
枚舉提供了許多數據類型,如日期、時間、手機號碼、貨幣、電子郵件地址等。DataType
屬性一樣可以讓應用程式具備自動提供特定數據類型的功能。例如,一個 mailto:
鏈接可以使用 DataType.EmailAddress
數據類型創建,在支持 Html5 的瀏覽器中並且日期選擇器可以提供 DataType.Date
的值。DataType
屬性迴向瀏覽器發送 HTML 5 標簽 data-
(pronounced data dash)。DataType
屬性 無法 提供任何驗證提供任何驗證。
DataType.Date
不指定日期的顯示格式。預設情況下,數據欄位的預設的顯示格式基於伺服器的 CultureInfo
設置來決定的。
DisplayFormat
屬性被用來格式化日期:
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }
ApplyFormatInEditMode
用來指定格式是否應用到文本框編輯。(你可能不希望在某些欄位中使用這個功能——例如,對於貨幣值,您可能不希望在文本框中對貨幣符號進行編輯。)
你可以直接使用 DisplayFormat
屬性本身,但是更好的方式是建議使用 DataType
, DataType
屬性僅僅傳遞數據的語義,並不會通知瀏覽器如何呈現,如果不使用 DisplayFormat
有以下好處:
- 瀏覽器可以啟用 HTML5 特性(例如顯示日曆控制項,設置本地化的貨幣符號,電子郵件中的鏈接,等等)。
- 預設情況下,瀏覽器將基於你的 本地環境 使用正確的格式渲染數據。
DataType
屬性可以使 MVC 選擇正確的欄位模板來呈現數據(比如DisplayFormat
如果單獨使用使用string模板)。更多信息,請參考 Brad Wilson 的 `ASP.NET MVC 2 模版。 (儘管是為 MVC 2編寫的, 文章依然適用於當前的 ASP.NET MVC 版本。)
註意
jQuery驗證當Range
屬性和DateTime
屬性同時使用的時候無法生效。例如,下麵的代碼將始終顯示客戶端驗證錯誤,即使日期在指定範圍內:
[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]
你需要禁用jQuery數據驗證日期驗證在 Range
屬性中使用 DateTime
。在編譯你的模型的具體日期的時候這通常不是一個好的做法,所以不建議在 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)] //手工高亮
public decimal Price { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$"), StringLength(5)] //手工高亮
public string Rating { get; set; }
}
在下一個系列裡面, 我們會重新審視應用程式,為自動生成的 Details
以及 Delete
方法做一些提升。