.Net Core 3.x Api开发笔记 — 输入参数模型验证(六)

  • A+
所属分类:.NET技术
摘要

参数模型验证  一般是对传入的参数按照制定规则校验,该章节主要演示在服务端对传入参数进行校验

参数模型验证  一般是对传入的参数按照制定规则校验,该章节主要演示在服务端对传入参数进行校验

校验主要包括3点:

1,定义验证规则

2,按照规则进行检查

3,错误报告

 

1,定义验证规则

这里介绍3中验证方式:

方式一:使用 Data Annotations程序集,通过属性注解方式,例如  [Required]、[MaxLength] 等

方式二:自定义属性 Attribute 验证

方式三:使用 FluentValidation 方式验证 (推荐)

 

方式一 和 方式二 都要引入下边的程序集

引入程序集:System.ComponentModel.Annotations  项目没有的需要安装一下该程序包

方式一:属性注解验证

优点:简单

缺点:只能作用在属性上、存在代码侵入、校验方式简单、验证只能在Controller的Action中使用,不支持非Controller中或者控制台程序的验证

 1 public class ProductsDto  2 {  3         [Display(Name = "商品编号")]  4         [Required(ErrorMessage = "{0}是必填项")]  5         [StringLength(maximumLength: 10, MinimumLength = 5, ErrorMessage = "{0}的长度范围是{2}到{1}")]  6         public string ProductCode { get; set; }  7   8         [Display(Name = "商品名称")]  9         [Required(ErrorMessage = "{0}是必填项")] 10         [MinLength(1, ErrorMessage = "{0}的最小长度是1")] 11         public string ProductName { get; set; } 12  13         [Display(Name = "商品价格")] 14         [Required(ErrorMessage = "{0}是必填项")] 15         [RegularExpression(@"^(?!.{12,}$)d{1,18}(.d{1,2})?$", ErrorMessage = "{0}格式不规范,{0}要保留小数点后1到2位")] 16         public decimal? Price { get; set; } 17  18         [Display(Name = "会员价")] 19         [Compare("Price", ErrorMessage = "{0}必须和{1}相同")] 20         public decimal? VipPrice { get; set; } 21  22         [Display(Name = "状态")] 23         [Range(0, 1, ErrorMessage = "{0}必须是{1}或{2}")] 24         public int? Status { get; set; } 25  26 }

简单解释: 

Display  定义别名

Required  必填项

StringLength   字符串长度验证   

   maximumLength: 10   最大长度

   MinimumLength = 5    最小长度

   ErrorMessage = "{0}的长度范围是{2}到{1}"      -- 错误提示内容 

   {0}、{1}、{2} 是占位符,{0}表示当前属性 ,{1}第一个参数 maximumLength , {2}是第二个参数MinimumLength  

RegularExpression   定义正则表达式

Compare  和其他属性进行比较

 

通过接口进行测试,返回错误报告如下:

AddProducts(ProductsDto products)

.Net Core 3.x  Api开发笔记 -- 输入参数模型验证(六)

方式二:自定义属性 Attribute 验证

优点:可以将验证声明到类级别上、在方式一的基础上进一步封装、可以进行复杂的规则校验

缺点:依然存在代码侵入(小到可以忽略不计),验证只能在Controller的Action中使用,不支持非Controller中或者控制台程序的验证

定义一个类:LoginFilterValidationAttribute,继承 ValidationAttribute 属性,重写IsValid()方法

public class LoginFilterValidationAttribute : ValidationAttribute     {         protected override ValidationResult IsValid(object value, ValidationContext validationContext)         {             var userDto = (UsersDto)validationContext.ObjectInstance;   //获取类的实例对象              //验证用户名不能为空             if (string.IsNullOrWhiteSpace(userDto.Username))             {                 return new ValidationResult("用户名不能为空", new[] { nameof(userDto.Username) });             }              //验证密码不能为空             if (string.IsNullOrWhiteSpace(userDto.Password))             {                 return new ValidationResult("密码不能为空", new[] { nameof(userDto.Password) });             }              //验证手机号不能为空             if (string.IsNullOrWhiteSpace(userDto.Mobile))             {                 return new ValidationResult("手机号不能为空", new[] { nameof(userDto.Mobile) });             }              //手机号输入规则验证             if (!string.IsNullOrWhiteSpace(userDto.Mobile))             {                 var regex = new Regex(@"^1[3456789]d{9}$");                 if (!regex.IsMatch(userDto.Mobile))                     return new ValidationResult("手机号不符合规则", new[] { nameof(userDto.Mobile) });             }              //验证密码强度             if (!string.IsNullOrWhiteSpace(userDto.Password))             {                 //正则                 var regex = new Regex(@"                                       (?=.*[0-9])                     #必须包含数字                                       (?=.*[a-zA-Z])                  #必须包含小写或大写字母                                       (?=([x21-x7e]+)[^a-zA-Z0-9])  #必须包含特殊符号                                       .{6,16}                         #至少6个字符,最多16个字符                                       ", RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace);                 //是否匹配,如果不匹配则返回                 if (!regex.IsMatch(userDto.Password))                 {                     return new ValidationResult("密码不符合规则,请重新输入", new[] { nameof(userDto.Password) });                 }             }              //验证两次输入密码             if (!string.IsNullOrWhiteSpace(userDto.ConfirmPassword))             {                 if (!userDto.Password.Equals(userDto.ConfirmPassword))                 {                     return new ValidationResult("两次输入密码不同,请重新输入", new[] {                             nameof(userDto.Password),                             nameof(userDto.ConfirmPassword)                         });                 }             }              return ValidationResult.Success;         }      }

在Model类中直接如下声明即可:

    [LoginFilterValidation]      //将参数验证声明到类上     public class UsersDto     {         public int Userid { get; set; }         public string Username { get; set; }         。。。     }

通过接口测试,返回错误内容如下:

AddUsers(UsersDto users)

.Net Core 3.x  Api开发笔记 -- 输入参数模型验证(六)

方式三:使用 FluentValidation 方式验证 (推荐) 

优点:支持任何场景下的模型验证(Controller层和Service层都能用),且不侵入代码,支持复制规则验证,规则定义类似方式二

缺点:适合大型项目(个人感觉),小项目用上边两种方式够用了

使用该方式需要引入下边程序包:FluentValidation.AspNetCore

.Net Core 3.x  Api开发笔记 -- 输入参数模型验证(六)

创建自定义类:RegisterValidationAttribute 、继承 AbstractValidator<UsersDto>

 1 public class RegisterValidationAttribute : AbstractValidator<UsersDto>, IModelValidator  2 {  3     public RegisterValidationAttribute()  4     {  5         //如果设置为Stop,则检测到失败的验证,则立即终止,不会继续执行剩余属性的验证。  6         //默认值为 Continue  7         CascadeMode = CascadeMode.Stop;  8   9         RuleFor(x => x.Username).NotEmpty().WithMessage("用户名不能为空") 10                              .Length(2, 12).WithMessage("用户名至少2个字符,最多12个字符"); 11  12         RuleFor(x => x.Password).NotEmpty().WithMessage("密码不能为空") 13                              .Length(6, 16).WithMessage("密码长度至少6个字符,最多16个字符") 14                   .Must(EncryptionPassword).WithMessage("密码不符合规则,必须包含数字、小写或大写字母、特殊符号"); 15  16         RuleFor(x => x.ConfirmPassword).NotEmpty().WithMessage("确认密码不能为空") 17                             .Must(ComparePassword).WithMessage("确认密码必须跟密码一样"); 18  19         RuleFor(x => x.Mobile).NotEmpty().WithMessage("手机号不能为空") 20                           .Must(IsMobile).WithMessage("手机号格式不正确"); 21     } 22  23     /// <summary> 24     /// 密码强度验证 25     /// </summary> 26     /// <returns></returns> 27     private bool EncryptionPassword(string password) 28     { 29         //正则 30         var regex = new Regex(@" 31                               (?=.*[0-9])                     #必须包含数字 32                               (?=.*[a-zA-Z])                  #必须包含小写或大写字母 33                               (?=([x21-x7e]+)[^a-zA-Z0-9])  #必须包含特殊符号 34                               .{6,16}                         #至少6个字符,最多16个字符 35                               ", RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace); 36         return regex.IsMatch(password); 37     } 38  39     /// <summary> 40     /// 比较两次密码是否一样 41     /// </summary> 42     /// <param name="confirmpwd">这里传的是:ConfirmPassword</param> 43     /// <returns></returns> 44     private bool ComparePassword(UsersDto model, string confirmpwd) 45     { 46         return string.Equals(model.Password, confirmpwd, StringComparison.OrdinalIgnoreCase);  //比较字符串并忽略大小写 47     } 48  49     //验证手机号 50     private bool IsMobile(string arg) 51     { 52         return Regex.IsMatch(arg, @"^[1][3-8]d{9}$"); 53     } 55 }

使用方式很简单,如下:

 1 [HttpPost]  2 public async Task<ActionResult> RegisterUsers(UsersDto usersDto)  3 {  4     var result = new CommonResult();  5       6     //使用如下两行代码即可  7     RegisterValidationAttribute validationRules = new RegisterValidationAttribute();  8     ValidationResult validaResult = validationRules.Validate(usersDto);  9  10     if (validaResult.IsValid)   //校验通过 11     { 12         //执行正常的业务逻辑 13         result.ResultCode = 1; 14         result.ResultMsg = "成功"; 15     } 16     else   //验证没通过,返回错误信息 17     { 18         result.ResultCode = 0; 19         result.ResultMsg = validaResult.ToString("||"); 20     } 21     return Ok(result); 22 }

测试结果,分别返回如下错误提示:

.Net Core 3.x  Api开发笔记 -- 输入参数模型验证(六)   .Net Core 3.x  Api开发笔记 -- 输入参数模型验证(六)

.Net Core 3.x  Api开发笔记 -- 输入参数模型验证(六)

.Net Core 3.x  Api开发笔记 -- 输入参数模型验证(六)   .Net Core 3.x  Api开发笔记 -- 输入参数模型验证(六)

如果将 Stop  替换成 Continue 会发生什么?

1 CascadeMode = CascadeMode.Stop; 2 替换成: 3 CascadeMode = CascadeMode.Continue;

测试结果如下:错误信息会全部返回  

1 { 2   "resultCode": 0, 3   "resultMsg": "'用户名' 不能为空。||用户名至少2个字符,最多12个字符||'密码' 不能为空。||密码长度至少6个字符,最多16个字符||密码不符合规则,必须包含数字、小写或大写字母、特殊符号||'验证码' 不能为空。||请输入4位验证码" 4 }

如果你嫌每次都要实例化一次对象进行注册,你也可以使用全局注册,直接在 Staup 中注册即可

 1 services.AddControllers()  2 //记得引入 using FluentValidation.AspNetCore  3 .AddFluentValidation(option =>  4  {  5      //所有验证类继承该接口,使用接口标识 IModelValidator 批量注册  6      //option.RegisterValidatorsFromAssemblyContaining<IModelValidator>();  7   8      //单个类注册  9      option.RegisterValidatorsFromAssemblyContaining<LoginValidationAttribute>(); 10  });

参考文档:

https://blog.csdn.net/fuluadmin/article/details/114619301

https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/validation?view=aspnetcore-5.0