.Net Core 3.x Api開發筆記 -- 輸入參數模型驗證(六)

參數模型驗證  一般是對傳入的參數按照制定規則校驗,該章節主要演示在服務端對傳入參數進行校驗

校驗主要包括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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章