.net5 - 创建Web.Api项目(四)DataAnnotations实现数据验证

命名空间

 System.ComponentModel.DataAnnotations

全局模型验证,统一api响应

1、WebApi项目下新建文件夹【Custom】,新建文件夹【Filter】定义类FieldActionFilter

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注册服务

            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】
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】
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

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;
        }
    }
}

 使用

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; }
    }
}

  

posted @ 2021-03-02 14:15  gygtech  Views(1324)  Comments(0Edit  收藏  举报