參數模型驗證 一般是對傳入的參數按照制定規則校驗,該章節主要演示在服務端對傳入參數進行校驗
校驗主要包括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)
方式二:自定義屬性 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)
方式三:使用 FluentValidation 方式驗證 (推薦)
優點:支持任何場景下的模型驗證(Controller層和Service層都能用),且不侵入代碼,支持復制規則驗證,規則定義類似方式二
缺點:適合大型項目(個人感覺),小項目用上邊兩種方式夠用了
使用該方式需要引入下邊程序包:FluentValidation.AspNetCore
創建自定義類: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 }
測試結果,分別返回如下錯誤提示:
如果將 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