1 Model级别错误消息
Model层错误消息被用到整个实体,而不是单个属性,我们只需要给AddModelError() 方法第一个参数中提供一个空值,如下代码:
ModelState.AddModelError("", "Some Model-Level error");
采用一个例子,假如我们不想让名字为Osama Bin的这个人申请Job,为了实现这个,我们添加一个新的else if 语句块检查名字是否是‘Osama Bin Laden’,然后提供一个实体类级别的错误:
[HttpPost]
public IActionResult Index(JobApplication jobApplication)
{
if (string.IsNullOrEmpty(jobApplication.Name))
ModelState.AddModelError(nameof(jobApplication.Name), "请输入用户名");
else if (jobApplication.Name == "Osama Bin Laden")
ModelState.AddModelError("", "你不能申请工作");
// removed for clarity
}
我们把 asp-validation-summary属性修改为ModelOnly
@model JobApplication
@{
ViewData["Title"] = "Job Application";
}
<style>
.input-validation-error {
border-color: red;
}
</style>
<h2>Job Application</h2>
@*<div asp-validation-summary="All" class="text-danger"></div>*@
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<form class="m-1 p-1" method="post">
<div class="mb-3 row">
<div class="col-sm-1">
<label asp-for="Name" class="control-label"></label>
</div>
<div class="col-sm-11">
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-1">
<label asp-for="DOB" class="control-label"></label>
</div>
<div class="col-sm-11">
<input asp-for="DOB" type="text" asp-format="{0:d}" class="form-control" />
<span asp-validation-for="DOB" class="text-danger"></span>
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-1">
<label asp-for="Sex" class="control-label"></label>
</div>
<div class="col-sm-1">
<input asp-for="Sex" type="radio" value="M" class="form-check" />男
</div>
<div class="col-sm-1">
<input asp-for="Sex" type="radio" value="F" class="form-check" />女
</div>
<div class="col-sm-9">
<span asp-validation-for="Sex" class="text-danger"></span>
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-1">
<label asp-for="Experience" class="control-label"></label>
</div>
<div class="col-sm-11">
<select asp-for="Experience" class="form-control">
<option value="选择">选择</option>
<option value="0">新手</option>
<option value="1">0-1 年</option>
<option value="2">1-2 年</option>
<option value="3">2-3 年</option>
<option value="4">3-4 年</option>
<option value="5">4-5 年</option>
</select>
<span asp-validation-for="Experience" class="text-danger"></span>
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-1">
</div>
<div class="col-sm-1">
<input asp-for="TermsAccepted" class="form-label" />
</div>
<div class="col-sm-10">
<label asp-for="TermsAccepted" class="form-check-label">
我接受条款 & 条件
</label>
<span asp-validation-for="TermsAccepted" class="text-danger"></span>
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-11 offset-sm-1">
<button type="submit" class="btn btn-primary">提交</button>
</div>
</div>
</form>
我们在名字控件中输入Osama Bin Laden值,我们仅仅看到了错误消息-你不能申请工作看下面图片:

2 模型验证中使用Data Annotations
ASP.NET Core 内置了大量的验证特性,我们可以在实体类上应用这些规则来指定验证规则,这些特性位于该命名空间(System.ComponentModel.DataAnnotations),验证处理过程是即简单又快,也可以少些一些验证的代码
微软在该命名空间下提供了大量的验证特性,我们也可以在应用程序中使用他们来做验证,可以在模型类的属性指定特性来验证属性,特性中定义了一系列的验证规则
下面列出了常用的特性:
名称 | 描述 |
[Required(ErrorMessage = “Some Message”)] | 确保值不为空,默认值为null的类型指定一个值 例如:int?, float?, string |
[StringLength(max,min)] | 给字符串长度指定一个范围(包含最小值和最大值).例如[StringLeght(2,5)] 允许字符串的长度是2到5 |
[Compare(“OtherProperty”)] | 确保应用这个特性的属性和指定的属性(即OtherProperty) 有相同的值 |
[Range(min,max)] | 确保数字的值位于最小值和最大值之间(包含最大和最小值) 例如:[Range(2,5)] 包含的值 2,3,4,5 |
[RegularExpression(“pattern”)] | 给指定的属性设置正则表单时,例如:邮件地址 |
现在我们进入JobApplication类并且引用命名空间System.ComponentModel.DataAnnotations,将特性添加到属性上:
using System.ComponentModel.DataAnnotations;
namespace ModelBindingValidation.Models
{
public class JobApplication
{
[Required]
[Display(Name = "Job applicant name")]
public string Name { get; set; }
public DateTime DOB { get; set; }
[Required(ErrorMessage = "Please select your sex")]
public string Sex { get; set; }
[Range(0, 5)]
public string Experience { get; set; }
[Range(typeof(bool), "true", "true", ErrorMessage = "You must accept the Terms")]
public bool TermsAccepted { get; set; }
}
}
名字上我们应用了2个特性,分别是:Display Required
[Required][DisplayName("姓名")]public string Name { get; set; }
[DisplayName(“姓名”)]特性 指定属性在视图上显示的名字, [Required]特性指定属性必须有一个值我们注意到[Required]特性没有指定ErrorMessage的值,该属性在视图上显示的名称是姓名,系统会自动采用这个名称的值作为错误消息不需要指定任何验证特性针对时间类型的DOB字段,这是因为DateTime字段有一个01-01-0001 00:00:00 的默认值,因此用户在没有填充任何值的情况下,会采用默认值,因此使用[Required] 特性是不起作用如果想要在属性上指定一个[Required]特性,我们必须该类型设置为nullable类型:
[Required(ErrorMessage = "你输入你的出生日期")]
public DateTime? DOB { get; set; }
在上面的代码中,我们应用了[Required(ErrorMessage = “”)] 特性,确保DOB字段的值不能为空,并且有正确的时间格式,以相同的方式我们把[Required]特性应用到Sex字段在Experience属性上指指定了一个Range特性,这是因为我们想要经验0到5
[Range(0, 5)]public int Experience { get; set; }
最后我们在TermsAccepted字段应用一个[Range]特性:
[Range(typeof(bool), "true", "true", ErrorMessage = "你必须接受条款")]
[DisplayName("条款")]
public bool TermsAccepted { get; set; }
我们不能在用户选择条款上应用[Required]特性,这是因为一个bool类型的字段,如果不选择的话,默认值是false,因此Required 对该属性不起任何作用我们把[Range]特性应用到bool类型的属性上面,设置最大和最小值为true,强迫用户选择checkbox最后,我们把action方法中验证的代码移除掉,修改JobController的Index方法:
using AspNetCore.ModelValidation.Models;
using Microsoft.AspNetCore.Mvc;
namespace AspNetCore.ModelValidation.Controllers
{
public class JobController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Index(JobApplication jobApplication)
{
if (ModelState.IsValid)
return View("Accepted", jobApplication);
else
return View();
}
}
}
现在我们仅仅验证ModelState对象的IsValid属性,重新运行应用程序并且看到表单和之前一样工作使用简单的代码

下面代码包含action方法的验证:
[HttpPost]
public IActionResult Index(JobApplication jobApplication)
{
if (jobApplication.Name == "Osama Bin Laden")
ModelState.AddModelError(nameof(jobApplication.Name), "You cannot apply for the Job");
if (jobApplication.DOB > DateTime.Now)
ModelState.AddModelError(nameof(jobApplication.DOB), "Date of Birth cannot be in the future");
else if (jobApplication.DOB < new DateTime(1980, 1, 1))
ModelState.AddModelError(nameof(jobApplication.DOB), "Date of Birth should not be before 1980");
if (ModelState.IsValid)
return View("Accepted", jobApplication);
else
return View();
}
2.1 模型验证Email地址
为了确保邮件地址是正确的格式,我们使用[RegularExpression]特性进行验证,我们可以给该特性提供一个正则表达式
^[a-zA-Z0-9_\.-]+@([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,6}$
因此我们在JobApplication.cs 类中,添加一个新的email字段,代码如下:
using System.ComponentModel.DataAnnotations;
namespace ModelBindingValidation.Models
{
public class JobApplication
{
[Required]
[Display(Name = "Job applicant name")]
public string Name { get; set; }
public DateTime DOB { get; set; }
[Required(ErrorMessage = "Please select your sex")]
public string Sex { get; set; }
[Range(0, 5)]
public string Experience { get; set; }
[Range(typeof(bool), "true", "true", ErrorMessage = "You must accept the Terms")]
public bool TermsAccepted { get; set; }
[RegularExpression("^[a-zA-Z0-9_\.-]+@([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,6}$", ErrorMessage = "E-mail is not valid")]
public string Email { get; set; }
}
}
进入View 添加如下代码
@model JobApplication
@{
ViewData["Title"] = "Job Application";
}
<style>
.input-validation-error {
border-color: red;
}
</style>
<h2>Job Application</h2>
@*<div asp-validation-summary="All" class="text-danger"></div>*@
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<form class="m-1 p-1" method="post">
<div class="mb-3 row">
<div class="col-sm-1">
<label asp-for="Name" class="control-label"></label>
</div>
<div class="col-sm-11">
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-1">
<label asp-for="DOB" class="control-label"></label>
</div>
<div class="col-sm-11">
<input asp-for="DOB" type="text" asp-format="{0:d}" class="form-control" />
<span asp-validation-for="DOB" class="text-danger"></span>
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-1">
<label asp-for="Email" class="control-label"></label>
</div>
<div class="col-sm-11">
<input asp-for="Email" type="text" asp-format="{0:d}" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-1">
<label asp-for="Sex" class="control-label"></label>
</div>
<div class="col-sm-1">
<input asp-for="Sex" type="radio" value="M" class="form-check" />男
</div>
<div class="col-sm-1">
<input asp-for="Sex" type="radio" value="F" class="form-check" />女
</div>
<div class="col-sm-9">
<span asp-validation-for="Sex" class="text-danger"></span>
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-1">
<label asp-for="Experience" class="control-label"></label>
</div>
<div class="col-sm-11">
<select asp-for="Experience" class="form-control">
<option value="选择">选择</option>
<option value="0">新手</option>
<option value="1">0-1 年</option>
<option value="2">1-2 年</option>
<option value="3">2-3 年</option>
<option value="4">3-4 年</option>
<option value="5">4-5 年</option>
</select>
<span asp-validation-for="Experience" class="text-danger"></span>
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-1">
</div>
<div class="col-sm-1">
<input asp-for="TermsAccepted" class="form-label" />
</div>
<div class="col-sm-10">
<label asp-for="TermsAccepted" class="form-check-label">
我接受条款 & 条件
</label>
<span asp-validation-for="TermsAccepted" class="text-danger"></span>
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-11 offset-sm-1">
<button type="submit" class="btn btn-primary">提交</button>
</div>
</div>
</form>
当Email格式不正确时,将显示下面错误消息:

注意:我们也能使用[EmailAddress] 验证特性做email验证
[EmailAddress]
public string Email { get; set; }
3 自定义模型验证
如何在.NET Core中创建一个自定义的验证特性?客户自定义模型可以允许我们业务代码来处理验证我们需要继承这个类:1 Attribute 类
2 IModelValidator 接口这个Attribute类是自定义特性的基类,IModelValidator是一个接口定义了Validate() 方法,我们实现这个接口并在接口中写自定义验证代码
Validate() 方法的架子如下:
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context) { ...}
在参数中,我们能通过ModelValidationContext类实例接收到关于属性的信息,ModelValidationContext类属性如下:
姓名 | 描述 |
Model | 返回验证的属性的值 |
Container | 返回属性所拥有的对象 |
ActionContext | 提供上下文数据 |
在这个例子中,我们给DOB属性创建一个客户自定义属性,因此首先在根目录下创建一个Infrastructure 文件夹,并且在文件夹下创建一个CustomDate.cs类,下一步,添加下面代码:
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
namespace AspNetCore.ModelValidation.Infrastructure{ public class CustomDate : Attribute, IModelValidator { public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context) { if (Convert.ToDateTime(context.Model) > DateTime.Now) return new List<ModelValidationResult> { new ModelValidationResult("", "日期不能大于当前日期") }; else if (Convert.ToDateTime(context.Model) < new DateTime(1980, 1, 1)) return new List<ModelValidationResult> { new ModelValidationResult("", "日期必须在1980年以前") }; else return Enumerable.Empty<ModelValidationResult>(); } }}
在Validate()方法内针对DOB字段指定客户自定义验证,在这里满足两个条件:1 时间大于当前时间2 时间小于1980年如果这两个条件失败,返回ModelValidationResult的List对象,ModelValidationResult构造函数的第一个参数是和错误关联的属性名字,当验证单个属性时,它被指定为空,第二个参数是错误消息,如果这两个条件都通过,我们将返回一个空的enemurable 对象-return Enumerable.Empty()
现在在DOB字段上添加这个特性:
using AspNetCore.ModelValidation.Infrastructure;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace AspNetCore.ModelValidation.Models
{
public class JobApplication
{
[Required(ErrorMessage ="姓名不能为空")]
[DisplayName("姓名")]
public string Name { get; set; }
[DisplayName("出生日期")]
[Required(ErrorMessage = "请输入你的出生日期")]
[CustomDate]
public DateTime? DOB { get; set; }
[Required(ErrorMessage = "请选择性别")]
[DisplayName("性别")]
public string Sex { get; set; }
[Range(0, 5,ErrorMessage ="工作年限必须在0-5年")]
[DisplayName("工作经验")]
public string? Experience { get; set; }
[Range(typeof(bool), "true", "true", ErrorMessage = "你必须接受条款")]
[DisplayName("条款")]
public bool TermsAccepted { get; set; }
[RegularExpression("^[a-zA-Z0-9_\.-]+@([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,6}$", ErrorMessage = "电子邮件不正确")]
[DisplayName("电子邮件")]
public string Email { get; set; }
}
}
我们可以把action 方法里面验证DOB字段的代码移除掉:
[HttpPost]
public IActionResult Index(JobApplication jobApplication)
{
if (ModelState.IsValid)
return View("Accepted", jobApplication);
else
return View();
}
现在,我们测试一下,下面图片显示错误消息:

我们针对另外一个属性创建客户自定义验证,阻止一些用户使用特定名字,因此,在Infrastructure文件夹下创建一个新的类叫NameValidate.cs并且添加下面代码:
namespace AspNetCore.ModelValidation.Infrastructure{ public class NameValidate : Attribute, IModelValidator { public string[] NotAllowed { get; set; } public string ErrorMessage { get; set; } public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context) { if (NotAllowed.Contains(context.Model as string)) return new List<ModelValidationResult> { new ModelValidationResult("", ErrorMessage) }; else return Enumerable.Empty<ModelValidationResult>(); } }}
当在属性上面使用[NameValidate]特性时,我们需要提供一个值,这里:ErrorMessage – 错误信息
NotAllowed –字符串的数组,表示你不想让用户输入的名字现在只需要在name字段上添加特性:
[Required][Display(Name = "Job applicant name")][NameValidate(NotAllowed = new string[] { "Osama Bin Laden", "Saddam Hussain", "Mohammed Gaddafi" }, ErrorMessage = "You cannot apply for the Job")]public string Name { get; set; }
使用这个特性,像 “Osama Bin Laden”, “Saddam Hussain”, “Mohammed Gaddafi” 将不能申请工作当名字是Saddam Hussain,将给与下面错误信息:

4 客户端验证
直到现在我们使用的验证都是服务器端验证,这意味着表单需要提交到服务器端才能完成验证工作,然后服务器端发送错误消息到客户端,网络顺畅的情况下会很快,如果网络比较慢的情况下验证会执行很慢如果想在浏览器中立即获得验证而没有延迟,我们必须使用客户端验证,这些验证在浏览器端完成,而不是去服务器绕一圈再返回,我们可以通过在客户端调用JQuery库来完成这个工作为了指定客户端验证,我们需要在应用程序中添加3个文件:1 jQuery 2 jQuery Validation3 jQuery Validation Unobtrusive在JobController的Index视图中引用这3个文件:
@model JobApplication@{ Layout = "_Layout"; ViewData["Title"] = "Job Application";}@section scripts { <script src="~/lib/jquery/dist/jquery.min.js"></script> <script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script> <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>}<style> .input-validation-error { border-color: red; }</style><h2>Job Application</h2>
现在我们重新运行应用程序,客户端验证会立即触发,而没有去服务器验证
客户端验证不能工作针对Range特性处理bool值,因此你需要注释掉Range特性,因此需要在客户端单独处理代码如下:
//[Range(typeof(bool), "true", "true", ErrorMessage = "You must accept the Terms")]
public bool TermsAccepted { get; set; }
客户端验证也不支持像用户自定义模型证 [CustomDate]和[NameValidate]这对这些验证你需要在客户端创建验证
5 ASP.NET Core 远程验证
远程验证是通过异步方式验证,尽管看上去像客户端验证,但实际上通过Ajax在服务器端完成,远程验证在服务器端进行,当控件失去焦点时出发验证验证,而不是点击提交按钮
为了创建远程验证,我们需要添加一个action方法,返回JsonResult对象,有一个DOB的参数
我们把DOB属性上验证Attribute移除掉,添加一个新的action方法调用ValidateDate在JobController,代码如下:
public JsonResult ValidateDate(DateTime DOB){ if (DOB > DateTime.Now) return Json("日期必须大于当前时间"); else if (DOB < new DateTime(1980, 1, 1)) return Json("日期不能再1980年以前"); else return Json(true);}
如果时间大于当前时间或者小于1980年,在这种情况下会以JSON格式返回错误的字符串,如果时间格式是正确的,JSON返回true现在注释掉[CustomDate]特性在JobApplication.cs类,并添加[Remote]特性代码如下:
using AspNetCore.ModelValidation.Infrastructure;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace AspNetCore.ModelValidation.Models
{
public class JobApplication
{
[Required(ErrorMessage ="姓名不能为空")]
[DisplayName("姓名")]
[NameValidate(NotAllowed = new string[] { "Osama Bin Laden", "Saddam Hussain", "Mohammed Gaddafi" },
ErrorMessage = "你不能申请这份工作")]
public string Name { get; set; }
[DisplayName("出生日期")]
[Required(ErrorMessage = "请输入你的出生日期")]
[Remote("ValidateDate", "Job")]
public DateTime? DOB { get; set; }
[Required(ErrorMessage = "请选择性别")]
[DisplayName("性别")]
public string Sex { get; set; }
[Range(0, 5,ErrorMessage ="工作年限必须在0-5年")]
[DisplayName("工作经验")]
public string? Experience { get; set; }
[Range(typeof(bool), "true", "true", ErrorMessage = "你必须接受条款")]
[DisplayName("条款")]
public bool TermsAccepted { get; set; }
[RegularExpression("^[a-zA-Z0-9_\.-]+@([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,6}$", ErrorMessage = "电子邮件不正确")]
[DisplayName("电子邮件")]
[Required(ErrorMessage="电子邮件不能为空")]
public string Email { get; set; }
}
}
Remote特性有两个参数:1 方法名字 2 控制器名
现在我们输入DOB字段来检查验证,注意当鼠标移走时,远程验证将被调用

源代码地址
https://github.com/bingbing-gui/Asp.Net-Core-Skill/tree/master/Fundamentals/AspNetCore.ModelValidation/AspNetCore.ModelValidation
参考文献
声明:文中观点不代表本站立场。本文传送门:http://eyangzhen.com/341890.html