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.IsOrEmpty(jobApplication.Name))
ModelState.AddModelError(nameof(jobApplication.Name), "请输入用户名");
else if (jobApplication.Name == "Osama Bin Laden")
ModelState.AddModelError("", "你不能申请工作");
// removed for clarity
}
@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>
2 模型验证中使用Data Annotations
ASP.NET Core 内置了大量的验证特性,我们可以在实体类上应用这些规则来指定验证规则,这些特性位于该命名空间(System.ComponentModel.DataAnnotations),验证处理过程是即简单又快,也可以少些一些验证的代码
微软在该命名空间下提供了大量的验证特性,我们也可以在应用程序中使用他们来做验证,可以在模型类的属性指定特性来验证属性,特性中定义了一系列的验证规则
下面列出了常用的特性:
名称 | 描述 |
[Required(ErrorMessage = “Some Message”)] | 确保值不为空,默认值为的类型指定一个值 例如: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; }
[Required(ErrorMessage = "你输入你的出生日期")]
public DateTime? DOB { get; set; }
在上面的代码中,我们应用了[Required(ErrorMessage = "")] 特性,确保DOB字段的值不能为空,并且有正确的时间格式,以相同的方式我们把[Required]特性应用到Sex字段
[Range(0, 5)]
public int Experience { get; set; }
[Range(typeof(bool), "true", "true", ErrorMessage = "你必须接受条款")]
[DisplayName("条款")]
public bool TermsAccepted { get; set; }
我们不能在用户选择条款上应用[Required]特性,这是因为一个bool类型的字段,如果不选择的话,默认值是false,因此Required 对该属性不起任何作用
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地址
^[a-zA-Z0-9_\\.-]+@([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$
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 自定义模型验证
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
{
...
}
姓名 | 描述 |
Model | 返回验证的属性的值 |
Container | 返回属性所拥有的对象 |
ActionContext | 提供上下文数据 |
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>();
}
}
}
现在在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; }
}
}
[HttpPost]
public IActionResult Index(JobApplication jobApplication)
{
if (ModelState.IsValid)
return View("Accepted", jobApplication);
else
return View();
}
现在,我们测试一下,下面图片显示错误消息:
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>();
}
}
}
[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; }
4 客户端验证
@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);
}
using AspNetCore.ModelValidation.Infrastructure;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace AspNetCore.ModelValidation.Models
{
public class JobApplication
{
[ ]
[ ]
["Osama Bin Laden", "Saddam Hussain", "Mohammed Gaddafi" }, ] {
ErrorMessage = "你不能申请这份工作")]
public string Name { get; set; }
[ ]
[ ]
[ ]
public DateTime? DOB { get; set; }
[ ]
[ ]
public string Sex { get; set; }
[ ]
[ ]
public string? Experience { get; set; }
[ ]
[ ]
public bool TermsAccepted { get; set; }
[ ]
[ ]
[ ]
public string Email { get; set; }
}
}
现在我们输入DOB字段来检查验证,注意当鼠标移走时,远程验证将被调用
源代码地址
参考文献
https://www.yogihosting.com/aspnet-core-model-validation/#custom-model-validation