创建Web.Api项目(四)DataAnnotations实现数据验证
来自:.net5 - 创建Web.Api项目(四)DataAnnotations实现数据验证 - gygtech - 博客园 (cnblogs.com)
命名空间
System.ComponentModel.DataAnnotations
全局模型验证,统一api响应
1、WebApi项目下新建文件夹【Custom】,新建文件夹【Filter】定义类FieldActionFilter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using System.Linq; namespace Test.WebApi.Custom.Filter { /// <summary> /// 验证参数 /// </summary> public class FieldActionFilter : IActionFilter { public void OnActionExecuted(ActionExecutedContext context) { //throw new NotImplementedException(); } public void OnActionExecuting(ActionExecutingContext context) { ResponseDto result = new (); if (!context.ModelState.IsValid) { var errorMessage = context.ModelState.Values.SelectMany(v => v.Errors).FirstOrDefault(); result.Success = false ; result.Message = errorMessage.ErrorMessage; context.Result = new JsonResult(result); } } } } |
2、StartUp注册服务
1
2
3
4
5
6
7
8
|
services.AddMvc(options => { options.Filters.Add<FieldActionFilter>(); //Dto参数验证 }); services.Configure<ApiBehaviorOptions>(options => { options.SuppressModelStateInvalidFilter = true ; // 使用自定义模型验证【Api接口需要添加才能生效】 }); |
基础属性
属性名称 | 描述 |
---|---|
Required | 标识该属性为必需参数,不能为空 |
StringLength | 标识该字符串有长度限制,可以限制最小或最大长度 |
MaxLength | 不为null时,不能超过最大字符串长度 |
MixLength | 不为null时,不能少于最小字符串长度 |
Range | 标识该属性值范围,通常被用在数值型和日期型 |
Compare | 比较 - 与制定的字段值进行比较 具体见代码 [Compare(“MyOtherProperty”)]两个属性必须相同值,比如我们要求用户重复输入两次密码时有用 |
RegularExpression | 标识该属性将根据提供的正则表达式进行对比验证 |
Remote | 服务端验证 |
CustomValidation | 标识该属性将按照用户提供的自定义验证方法,进行数值验证 |
以下使用于表单:没有测试过,如需使用请自己测试 | |
DisplayName | 显示名 – 定义表单字段的提示名称,一般用于在PropertyGrid或者DataGridView里显示这个对象时,列名会自动显示DisplayName定义的值,而不是字段名 |
Bind | 绑定 – 列出在将请求参数绑定到模型的时候,包含和不包含的字段 |
ScaffoldColumn | 支架列 - 在编辑表单的时候,需要隐藏起来的的字符 [ScaffoldColumn(true|false)] |
DataType | 在前端显示的文本框类型 |
Editable | [Editable(false)] //放在主键上显示不可修改 |
Validations---这个是所有验证属性的基类
基础属性【Required、Range、StringLength、Compare】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
using System; using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace Test.Application.Employee.Dto { public class AddEmployeeDto { /// <summary> /// 职员姓名不能为空 /// </summary> [Required(ErrorMessage = "职员姓名不能为空" )] public string EmpName { get ; set ; } /// <summary> /// 密码 /// </summary> [Required(ErrorMessage = "密码不能为空" )] [StringLength(12, MinimumLength = 6, ErrorMessage = "密码只能是6到12位" )] public string Pwd { get ; set ; } /// <summary> /// 确认密码 /// </summary> [Compare( "Pwd" , ErrorMessage = "两次密码输入的不一致" )] public string RPwd { get ; set ; } public string AAA { get ; set ; } /// <summary> /// 备注 /// </summary> [StringLength(200, ErrorMessage = "备注不能超过200字" )] public string Remark { get ; set ; } /// <summary> /// 职员编号 /// </summary> [Required] //Swagger 字段后有一个*表示必填,实际上在传参的时候不填会有默认值0,这个没有起到实际意义上的验证效果 [Range(1, int .MaxValue, ErrorMessage = "职员编号不能为空" )] //适用于自增长Id public int EmpId { get ; set ; } /// <summary> /// 备注2 /// </summary> [StringLength(200, MinimumLength = 1, ErrorMessage = "备注2只能是1至200字" )] //可以为null,不能为"" public string Remark2 { get ; set ; } /// <summary> /// 年龄 /// </summary> [Range(18, 60, ErrorMessage = "年龄只能是18到60岁" )] //可null类型,可以为null public int ? Age { get ; set ; } /// <summary> /// 价格 /// </summary> [Range(9.99, 99.99, ErrorMessage = "价格只能是9.99至99.99" )] public double Price { get ; set ; } /// <summary> /// 生日 /// </summary> [Range( typeof (DateTime), "2021-03-02" , "2021-04-02" , ErrorMessage = "日期只能是3月2号到4月2号" )] //2021-04-02 00:00:01 不符合 public DateTime Birthday { get ; set ; } } } |
基础属性【RegularExpression】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
using System.ComponentModel.DataAnnotations; namespace Test.Application.Employee.Dto { /// <summary> /// 常工规则 /// 1、手机号码 /// 通用校验规则:^1[3456789]\d{9}$ /// 详细手机号校验规则:^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}$ /// 验证手机号和固定电话:^((0\d{2,3}-\d{7,8})|(1[34578]\d{9}))$ /// 2、邮箱格式 /// [\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])? /// 3、用户名校验 /// 6-16位的包含大小写字母、数字、特殊符号- _ 的用户名:^[a-zA-Z0-9_-]{6,16}$ /// 6-20位字母和数字组合:^(?![0-9]*$)(?![a-zA-Z]*$)[a-zA-Z0-9]{6,20}$ /// 4、密码强度校验 /// 密码强度正则,最少6位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符:^.*(?=.{6,})(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$ /// 请输入6-20位英文字母、数字或者符号(除空格),且字母、数字和标点符号至少包含两种:^(?![\d]+$)(?![a-zA-Z]+$)(?![^\da-zA-Z]+$)([^\u4e00-\u9fa5\s]){6,20}$ /// 5、整数校验 /// 正整数正则:^\d+$ /// 负整数正则:^-\d+$ /// 整数正则:^-?\d+$ /// 6、身份证验证 /// (^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$) /// 7、合法url校验 /// ^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$ /// 8、验证输入内容是否包含英文数字及下划线 /// ^[_a-zA-Z0-9]+$ /// 9、验证是否两位小数 /// (^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$) /// 10、中文校验 /// ^[\u0391-\uFFE5A-Za-z]+$ /// 11、纯数字校验 /// ^\d+$|^\d+[.]?\d+$ /// 12、最多一位小数 /// ^[0-9]+([.]{1}[0-9]{1})?$ /// 13、ip地址校验 /// ^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ /// 14、包含中文的校验 /// [\u4e00-\u9fa5] /// 15、内容只能由英文、数字、下划线组成 /// ^\w+$ /// 16、内容只能包含英文字母和数字 /// ^[a-z0-9]+$ /// 17、固定电话 /// ^((0\d{2,5}-)|0\d2,50\d2,5)?\d{7,8}(-\d{3,4})?$ /// </summary> public class ModifyEmployeeDto { /// <summary> /// 邮箱 /// </summary> [RegularExpression( @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4})$" , ErrorMessage = "邮箱格式不正确" )] public string Email { get ; set ; } /// <summary> /// 身份证 /// </summary> [RegularExpression( @"^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$" , ErrorMessage = "错误" )] public string IDCard { get ; set ; } } } |
基础属性【Remote】【没有查到相关资料,有知道的小伙伴请在评论区留下代码】
自定义验证
在XXX.Service项目中新建文件夹【Base/Validation】新建类:EnumCheckAttribute
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
using System; using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Linq; namespace Test.Application.Base.Validation { /// <summary> /// 自定义验证 /// 验证是否是有效的枚举值 /// </summary> public class EnumCheckAttribute : ValidationAttribute { /// <summary> /// 验证的枚举类Type /// </summary> public Type TypeClass { get ; set ; } /// <summary> /// 枚举范围外的键,如有更好的方案请留言 /// </summary> public int OtherKey { get ; set ; } = int .MinValue; /// <summary> /// 枚举范围外的值 /// </summary> public string OtherValue { get ; set ; } public override bool IsValid( object value) { string enumValue = Enum.GetName(TypeClass, value); if (enumValue == null ) { if (OtherKey == Convert.ToInt32(value)) { return true ; } return false ; } return true ; } /// <summary> /// 提示中,添加枚举的键值对,如有更好的方案请留言 /// </summary> /// <param name="name"></param> /// <returns></returns> public override string FormatErrorMessage( string name) { return string .Format(CultureInfo.CurrentCulture, ErrorMessageString, new object [] { EnumToJson() }); } /// <summary> /// 如有更好的方案请留言 /// </summary> /// <returns></returns> private string EnumToJson() { if (!TypeClass.IsEnum) throw new InvalidOperationException( "enum expected" ); var results = Enum.GetValues(TypeClass).Cast< object >().ToDictionary(enumValue => ( int )enumValue, enumValue => enumValue.ToString()); //if (OtherKey != int.MinValue) //{ // results.Add(OtherKey, OtherValue); //} //return string.Format("{0}", JsonConvert.SerializeObject(results)).Replace("\"", ""); string result = "{" ; if (OtherKey != int .MinValue) { result += OtherValue + ":" + OtherKey + "," ; } foreach ( var key in results.Keys) { result += results[key] + ":" + key + "," ; } result = result.Remove(result.Length - 1); result += "}" ; return result; } } } |
使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
using Test.Application.Base.Validation; using Test.Model; namespace Test.Application.Employee.Dto { public class QueryEmployeeDto { /// <summary> /// 性别 /// </summary> [EnumCheck(TypeClass = typeof (Genders), OtherKey = -1, OtherValue = "全部" , ErrorMessage = "性别无效,范围值为:{0}" )] public int Gender { get ; set ; } } } |