ASP.NETCoreMVC中的模型验证MicrosoftLearn

请升级到MicrosoftEdge以使用最新的功能、安全更新和技术支持。

本文介绍如何在ASP.NETCoreMVC或RazorPages应用中验证用户输入。

模型绑定和模型验证都在执行控制器操作或RazorPages处理程序方法之前进行。Web应用负责检查ModelState.IsValid并做出相应响应。Web应用通常会重新显示包含错误消息的页面,如以下RazorPages示例所示:

publicasyncTaskOnPostAsync(){if(!ModelState.IsValid){returnPage();}_context.Movies.Add(Movie);await_context.SaveChangesAsync();returnRedirectToPage("./Index");}对于具有控制器和视图的ASP.NETCoreMVC,以下示例演示如何在控制器操作内部检查ModelState.IsValid:

publicclassMovie{publicintId{get;set;}[Required][StringLength(100)]publicstringTitle{get;set;}=null!;[ClassicMovie(1960)][DataType(DataType.Date)][Display(Name="ReleaseDate")]publicDateTimeReleaseDate{get;set;}[Required][StringLength(1000)]publicstringDescription{get;set;}=null!;[Range(0,999.99)]publicdecimalPrice{get;set;}publicGenreGenre{get;set;}publicboolPreorder{get;set;}}内置特性以下是一些内置验证特性:

通过验证特性可以指定要为无效输入显示的错误消息。例如:

[StringLength(8,ErrorMessage="{0}lengthmustbebetween{2}and{1}.",MinimumLength=6)]应用于Name属性时,上述代码创建的错误消息将为“名称长度必须介于6到8之间”。

builder.Services.AddControllers(options=>options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes=true);[必需]服务器上的验证在服务器上,如果属性为null,则认为所需值缺失。不可为null的字段始终有效,并且从不显示[Required]属性的错误消息。

但是,不可为null的属性的模型绑定可能会失败,从而导致Thevalue''isinvalid等错误消息。若要为不可为null的类型的服务器端验证指定自定义错误消息,可使用以下选项:

在客户端上处理不可为null类型和字符串的方式与在服务器上不同。在客户端上:

如前所述,将不可为null类型视为具有[Required(AllowEmptyStrings=true)]特性。这意味着即使不应用[Required(AllowEmptyStrings=true)]特性,也可进行客户端验证。但如果不使用该特性,将收到默认错误消息。若要指定自定义错误消息,使用该特性。

若要实现远程验证:

以下是返回自定义错误消息的操作方法示例:

[AcceptVerbs("GET","POST")]publicIActionResultVerifyName(stringfirstName,stringlastName){if(!_userService.VerifyName(firstName,lastName)){returnJson($"Ausernamed{firstName}{lastName}alreadyexists.");}returnJson(true);}用户输入名字或姓氏时,JavaScript会进行远程调用,查看该名称对是否已占用。

若要验证两个或更多附加字段,可将其以逗号分隔列表形式提供。例如,若要向模型中添加MiddleName属性,可按以下示例所示设置[Remote]特性:

如果需要并非由内置属性提供的验证,可以:

以下示例验证“经典”流派电影的发行日期是否不晚于指定年份。[ClassicMovie]属性:

publicclassValidatableMovie:IValidatableObject{privateconstint_classicYear=1960;publicintId{get;set;}[Required][StringLength(100)]publicstringTitle{get;set;}=null!;[DataType(DataType.Date)][Display(Name="ReleaseDate")]publicDateTimeReleaseDate{get;set;}[Required][StringLength(1000)]publicstringDescription{get;set;}=null!;[Range(0,999.99)]publicdecimalPrice{get;set;}publicGenreGenre{get;set;}publicboolPreorder{get;set;}publicIEnumerableValidate(ValidationContextvalidationContext){if(Genre==Genre.Classic&&ReleaseDate.Year>_classicYear){yieldreturnnewValidationResult($"Classicmoviesmusthaveareleaseyearnolaterthan{_classicYear}.",new[]{nameof(ReleaseDate)});}}}自定义验证以下代码演示如何在检查模型后添加模型错误:

if(Contact.Name==Contact.ShortName){ModelState.AddModelError("Contact.ShortName","Shortnamecan'tbethesameasName.");}以下代码在控制器中实现验证测试:

publicasyncTaskOnPostAsync(){//AttachValidationErrorMessagetotheModelonvalidationfailure.if(Contact.Name==Contact.ShortName){ModelState.AddModelError("Contact.ShortName","Shortnamecan'tbethesameasName.");}if(_context.Contact.Any(i=>i.PhoneNumber==Contact.PhoneNumber)){ModelState.AddModelError("Contact.PhoneNumber","ThePhonenumberisalreadyinuse.");}if(_context.Contact.Any(i=>i.Email==Contact.Email)){ModelState.AddModelError("Contact.Email","TheEmailisalreadyinuse.");}if(!ModelState.IsValid||_context.Contact==null||Contact==null){//ifmodelisinvalid,returnthepagewiththemodelstateerrors.returnPage();}_context.Contact.Add(Contact);await_context.SaveChangesAsync();returnRedirectToPage("./Index");}以下代码在控制器中实现验证测试:

请考虑以下自定义ValidateNameAttribute:

publicclassValidateNameAttribute:ValidationAttribute{publicValidateNameAttribute(){conststringdefaultErrorMessage="ErrorwithName";ErrorMessage=defaultErrorMessage;}protectedoverrideValidationResultIsValid(objectvalue,ValidationContextvalidationContext){if(value==null||string.IsNullOrWhiteSpace(value.ToString())){returnnewValidationResult("Nameisrequired.");}if(value.ToString()!.ToLower().Contains("zz")){returnnewValidationResult(FormatErrorMessage(validationContext.DisplayName));}returnValidationResult.Success;}}在以下代码中,应用自定义[ValidateName]属性:

顶级节点包括:

当提交查询字符串中格式设置正确的age参数时,表单将进行验证。

“检查年限”页面上的第二个表单提交请求正文中的Age值,验证失败。绑定失败,因为age参数必须来自查询字符串。

达到最大错误数(默认为200)时,验证停止。可以使用Program.cs中的以下代码配置该数字:

如果模型图不需要验证,验证将自动短路(跳过)。运行时为其跳过验证的对象包括基元集合(如byte[]、string[]、Dictionary)和不具有任何验证器的复杂对象图。

客户端验证将阻止提交,直到表单变为有效为止。“提交”按钮运行JavaScript:要么提交表单要么显示错误消息。

表单上存在输入错误时,客户端验证会避免到服务器的不必要往返。_Layout.cshtml和_ValidationScriptsPartial.cshtml中的以下脚本引用支持客户端验证_Layout.cshtml_ValidationScriptsPartial.cshtml:

上述标记帮助程序呈现以下HTML:

jQueryUnobtrusiveValidation会在当页面第一次加载时将验证逻辑和参数传递到jQueryValidation。因此,不会对动态生成的表单自动执行验证。若要启用验证,指示jQuery非介入式验证在创建动态表单后立即对其进行分析。例如,以下代码在通过AJAX添加的表单上设置客户端验证。

$.validator.unobtrusive.parse()方法适用于整个表单,而不是