第十节:数据批注(DataAnnotationModel)和自定义验证(包括Model级别的验证)
一. 简介
写完上一个章节MVC中的常用特性,迫不及待将该系列补全,该章节主要介绍数据批注(也叫:注解)。
一听【数据批注】,好高大上的名字,但仔细一看,它们其实是【System.ComponentModel.DataAnnotations】程序集下的一些特性类,O(∩_∩)O哈哈~,既然是特性,就符合特性的所有特征,只不过这些特性是作用于“属性”上的。
再一看【System.ComponentModel.DataAnnotations】这个命名空间,有点眼熟,与之前EF中的一篇文章【EF的CodeFirst模式通过DataAnnotations修改默认协定】中的一类操作来源于同一个命名空间下。
所以综上所述:该命名空间下的特性,在EF中可以用来映射生成数据库中的表字段,在日常开发中也可以用于做类中属性的限制和验证。
原理:均继承了ValidationAttribute特性,通过覆写IsValide方法进行校验。
适用场景:很多项目需要客户端和服务器端进行双重格式验证,使之更加安全,这时服务器端就可以使用数据批注了来进行校验了。
以Required特性为例,查看一下源码:
二. 常用的数据批注
这里总结一下【System.ComponentModel.DataAnnotations】命名空间下常用的数据批注,即特性。
① Key :声明主键
② Required:非空声明
③ MinLength和MaxLength:设置string类型的最大长度和最小长度,数据库的对应nvarchar
④ StringLength:设置string类型的长度,数据库对应nvarchar
⑤ Compare:新老密码对比
⑥ RegularExpression:正则的匹配
⑦ Phone:验证手机号码
⑧ Range:验证范围
⑨ Timestamp:将byte[]类型设置为timestamp类型
⑩ ConcurrencyCheck:并发检查,执行update操作时,会检查并发性(乐观锁) (在后面并发章节着重介绍Timestamp和ConcurrencyCheck)
另外还有一些不是很常用的,如:
① DisplayName:声明属性的名称
② Remote:远程验证,需要JQuery插件的支持 (这里不做测试等待补充 参考: https://www.cnblogs.com/JustRun1983/p/3505151.html)
下面补充一下该命名空间反射源码,可以自行查找需要的批注:
代码测试:
(1). 实体类,在其属性上添加数据标注
1 /// <summary> 2 /// 用户信息类 ,用于测试框架本身提供的数据批注 3 /// </summary> 4 public class UserInfor 5 { 6 [Required] 7 public string id { get; set; } 8 9 [StringLength(4)] 10 public string userName { get; set; } 11 12 [MaxLength(8)] 13 public string userMsg { get; set; } 14 15 [Range(2, 10)] 16 public int userAge { get; set; } 17 18 [RegularExpression("[a-d]")] //a-d中的一个 19 public string userMsg3 { get; set; } 20 21 [Phone] 22 public string userPhone { get; set; } 23 24 public string userOldPwd { get; set; } 25 26 [Compare("userOldPwd")] //比较和userOldPwd的值是否相等 27 public string userNewPwd { get; set; } 28 29 }
(2). 前端代码
1 //1. 测试数据批注 2 $("#btn1").click(function () { 3 $.ajax({ 4 type: "Post", 5 url: "TestDataAnnotationModel", 6 data: { 7 "id":"123", 8 "userName": "mr12", 9 "userMsg": "ypf1234", 10 "userAge": 6, 11 "userMsg3": "a", 12 "userPhone": "15764222366", 13 "userOldPwd": "123456", 14 "userNewPwd":"123456" 15 16 }, 17 success: function (data) { 18 if (data == "ok") { 19 alert("测试通过"); 20 } 21 if (data == "error") { 22 alert("测试未通过"); 23 } 24 } 25 }); 26 });
(3). 服务器端代码
1 public ActionResult TestDataAnnotationModel(UserInfor user) 2 { 3 //通过该方法进行验证 4 var isValidate = ModelState.IsValid; 5 6 if (isValidate) 7 { 8 return Content("ok"); 9 } 10 return Content("error"); 11 }
三. 自定义数据批注
思路:通过上面的批注源码可知,均为自定义类继承:ValidationAttribute,覆写IsValid方法
需求:这里我们自定义一个批注,要求不为空,且长度区间为6-12位
使用方法同样为:action中通过实体接收,通过ModelState.IsValid的值为true或false来判断验证是否通过
代码测试:
(1). 实体类和自定义批注
1 /// <summary> 2 /// 角色类,用于测试自定义业务的数据批注 3 /// </summary> 4 public class RoleInfor 5 { 6 7 public string id { get; set; } 8 9 [myOwnCheck] 10 public string roleName { get; set; } 11 } 12 13 /// <summary> 14 /// 自定义数据批注,要求非空且长度为6-12位 15 /// </summary> 16 public class myOwnCheckAttribute : ValidationAttribute 17 { 18 public override bool IsValid(object value) 19 { 20 if (value != null && value.ToString().Length > 6 && value.ToString().Length < 12) 21 { 22 return true; 23 } 24 return false; 25 } 26 }
(2). 前端代码
1 //2. 测试自定义业务逻辑的验证 2 $("#btn2").click(function () { 3 $.ajax({ 4 type: "Post", 5 url: "TestMyOwnCheck", 6 data: { 7 "id": "123", 8 "roleName": "mr12345" 9 }, 10 success: function (data) { 11 if (data == "ok") { 12 alert("测试通过"); 13 } 14 if (data == "error") { 15 alert("测试未通过"); 16 } 17 } 18 }); 19 });
(3). 服务器端代码
1 public ActionResult TestDataAnnotationModel(UserInfor user) 2 { 3 //通过该方法进行验证 4 var isValidate = ModelState.IsValid; 5 6 if (isValidate) 7 { 8 return Content("ok"); 9 } 10 return Content("error"); 11 }
四. Model级别的验证扩展
实现IValidaableObjec接口,实现Validate方法。(了解即可)
1 public class CarInfor: IValidatableObject 2 { 3 public string id { get; set; } 4 5 public string carName { get; set; } 6 7 public int carAge { get; set; } 8 9 IEnumerable<ValidationResult> IValidatableObject.Validate(ValidationContext validationContext) 10 { 11 if (carAge % 2 == 0) 12 { 13 var result = new ValidationResult("车龄验证不通过", new string[] { "carAge" }); 14 15 yield return result; 16 } 17 } 18 }