本节内容:
- 向
Movie模型添加了验证逻辑。 - 确保每当用户创建或编辑电影时,都会强制执行验证规则。
坚持 DRY 原则
这可以减少所需编写的代码量,并使编写的代码更少出错,更易于测试和维护。
可以在一个位置(模型类中)以声明方式指定验证规则,并且在应用中的所有位置强制执行。
将验证规则添加到电影模型
DataAnnotations 还包含 DataType 等格式特性,有助于格式设置但不提供任何验证。
更新 Movie 类以使用内置的 Required、StringLength、RegularExpression 和 Range 验证特性。
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特性表示属性必须有值;但用户可输入空格来满足此验证。 -
在上述代码中,即“Genre”(分类):
- 只能使用字母。
- 不允许使用空格、数字和特殊字符。
-
RegularExpression“Rating”(分级):- 要求第一个字符为大写字母。
- “PG-13”对“分级”有效,但对于“分类”无效。
-
Range特性将值限制在指定范围内。 -
StringLength特性使你能够设置字符串属性的最大长度,以及可选的最小长度。 -
从本质上来说,需要值类型(如
decimal、int、float、DateTime),但不需要[Required]特性。
同时它能确保你无法忘记验证某些内容,并防止你无意中将错误数据导入数据库。
验证错误 UI
运行应用并导航到电影控制器。
当 jQuery 客户端验证检测到错误时,会显示一条错误消息。
备注
请参阅 GitHub 问题 4076。
客户端(使用 JavaScript 和 jQuery)和服务器端(若用户禁用 JavaScript)都必定会遇到这些错误。
使用 Edit 操作方法测试验证后,即已应用相同的验证。
F12 开发人员工具在 HTTP Post 方法中设置断点来对此进行验证。
验证工作原理
下列代码显示两种 Create 方法。
// 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); }
如果在浏览器中禁用 JavaScript,客户端验证将被禁用,而你可以测试 HTTP POST Create 方法 ModelState.IsValid 检测任何验证错误。
在没有 JavaScript 的情况下仍然可以进行完整的验证。
以下图片显示如何在 FireFox 浏览器中禁用 JavaScript。
以下图片显示如何在 Chrome 浏览器中禁用 JavaScript。
禁用 JavaScript 后,发布无效数据并单步执行调试程序。
Create.cshtml 视图模板的一部分在以下标记中显示 :
<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.*@
操作方法使用上述标记来显示初始表单,并在发生错误时重新显示此表单。
验证。
这些相同的验证规则自动应用于 Edit 视图和可能创建用于编辑模型的任何其他视图模板。
这意味着对 DRY 原则的完全遵守。
使用 DataType 特性
以下代码显示具有适当 DataType 特性的 ReleaseDate 和 Price 属性。
[Display(Name = "Release Date")] [DataType(DataType.Date)] public DateTime ReleaseDate { get; set; } [Range(1, 100)] [DataType(DataType.Currency)] public decimal Price { get; set; }
DataType 特性不提供任何验证 。
默认情况下,数据字段根据基于服务器的 CultureInfo 的默认格式进行显示。
DisplayFormat 特性用于显式指定日期格式:
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)] public DateTime ReleaseDate { get; set; }
(你可能不想为某些字段执行此操作 — 例如对于货币值,你可能不希望文本框中的货币符号可编辑。)
DataType 特性传达数据的语义而不是传达如何在屏幕上呈现数据,并提供 DisplayFormat 不具备的以下优势:
-
浏览器可启用 HTML5 功能(例如显示日历控件、区域设置适用的货币符号、电子邮件链接等)
-
默认情况下,浏览器将根据区域设置采用正确的格式呈现数据。
-
DataType特性使 MVC 能够选择正确的字段模板来呈现数据(如果DisplayFormat由自身使用,则使用的是字符串模板)。
备注
例如,以下代码将始终显示客户端验证错误,即便日期在指定的范围内:
[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; } }
在本系列的下一部分中,我们将回顾应用,并对自动生成的 Details 和 Delete 方法进行一些改进。