使用 DataAnnotations(數據注解)實現通用模型數據校驗

dd09848975753cc1dfa9b68761d6585a.png

.net 跨平臺

參數校驗的意義

在實際項目開發中,無論任何方式、任何規模的開發模式,項目中都離不開對接入數據模型參數的合法性校驗,目前普片的開發模式基本是前后端分離,當用戶在前端頁面中輸入一些表單數據時,點擊提交按鈕,觸發請求目標服務器的一系列后續操作,在這中間的執行過程中(標準做法推薦)無論是前端代碼部分,還是服務端代碼部分都應該有針對用戶輸入數據的合法性校驗,典型做法如下:

  • 前端部分:當用戶在頁面輸入表單數據時,前端監聽頁面表單事件觸發相應的數據合法性校驗規則,當數據非法時,合理的提示用戶數據錯誤,只有當所有表單數據都校驗通過后,才繼續提交數據給目標后端對應的接口;

  • 后端部分:當前端數據合法校驗通過后,向目標服務器提交表單數據時,服務端接收到相應的提交數據,在入口源頭出就應該觸發相關的合法性校驗規則,當數據都校驗通過后,繼續執行后續的相關業務邏輯處理,反之則響應相關非法數據的提示信息;

特別說明:在實際的項目中,無論前端部分還是服務端部分,參數的校驗都是很有必要性的。無效的參數,可能會導致應用程序的異常和一些不可預知的錯誤行為。

常用的參數校驗項

這里例舉一些項目中比較常用的參數模型校驗項,如下所示:

  • Name:姓名校驗,比如需要是純漢字的姓名;

  • Password:密碼強度驗證,比如要求用戶輸入必須包含大小寫字母、數字和特殊符號的強密碼;

  • QQ:QQ 號碼驗證,是否是有效合法的 QQ 號碼;

  • China Postal Code:中國郵政編碼;

  • IP Address:IPV4 或者 IPV6 地址驗證;

  • Phone:手機號碼或者座機號碼合法性驗證;

  • ID Card:身份證號碼驗證,比如:15 位和 18 位數身份證號碼;

  • Email Address:郵箱地址的合法性校驗;

  • String:字符串驗證,比如字段是否不為 null、長度是否超限;

  • URL:驗證屬性是否具有 URL 格式;

  • Number:數值型參數校驗,數值范圍校驗,比如非負數,非負整數,正整數等;

  • File:文件路徑及擴展名校驗;

對于參數校驗,常見的方式有正則匹配校驗,通過對目標參數編寫合法的正則表達式,實現對參數合法性的校驗。

.NET 中內置 DataAnnotations 提供的特性校驗

上面我們介紹了一些常用的參數驗證項,接下來我們來了解下在 .NET 中內置提供的 DataAnnotations 數據注解,該類提供了一些常用的驗證參數特性。

官方解釋:

  • 提供用于為 ASP.NET MVCASP.NET 數據控件定義元數據的特性類。

  • 該類位于 System.ComponentModel.DataAnnotations 命名空間。

關于 DataAnnotations 中的特性介紹

讓我們可以通過這些特性對 API 請求中的參數進行驗證,常用的特性一般有:

  • **[ValidateNever]**:指示應從驗證中排除屬性或參數。

  • **[CreditCard]**:驗證屬性是否具有信用卡格式。

  • **[Compare]**:驗證模型中的兩個屬性是否匹配。

  • **[EmailAddress]**:驗證屬性是否具有電子郵件格式。

  • **[Phone]**:驗證屬性是否具有電話號碼格式。

  • **[Range]**:驗證屬性值是否位于指定范圍內。

  • **[RegularExpression]**:驗證屬性值是否與指定的正則表達式匹配。

  • **[Required]**:驗證字段是否不為 null。

  • **[StringLength]**:驗證字符串屬性值是否不超過指定的長度限制。

  • **[Url]**:驗證屬性是否具有 URL 格式。

其中 RegularExpression 特性,基于正則表達式可以擴展實現很多常用的驗證類型,下面的( 基于 DataAnnotations 的通用模型校驗封裝 )環節舉例說明。

關于該類更多詳細信息請查看,https://learn.microsoft.com/zh-cn/dotnet/api/system.componentmodel.dataannotations?view=net-7.0

基于 DataAnnotations 的通用模型校驗封裝

此處主要是使用了 Validator.TryValidateObject() 方法:

Validator.TryValidateObject(object?instance,?ValidationContext?validationContext,?ICollection<ValidationResult>??validationResults,?bool?validateAllProperties);

Validator 類提供如下校驗方法:

33f5227fcc18df1e13328cebe66144af.png
Validator

基于 DataAnnotations 的特性校驗助手實現步驟

  • 錯誤成員對象類 ErrorMember

namespace?Jeff.Common.Validatetion;///?<summary>
///?錯誤成員對象
///?</summary>
public?class?ErrorMember
{///?<summary>///?錯誤信息///?</summary>public?string??ErrorMessage?{?get;?set;?}///?<summary>///?錯誤成員名稱///?</summary>public?string??ErrorMemberName?{?get;?set;?}
}
  • 驗證結果類 ValidResult

namespace?Jeff.Common.Validatetion;///?<summary>
///?驗證結果類
///?</summary>
public?class?ValidResult
{public?ValidResult(){ErrorMembers?=?new?List<ErrorMember>();}///?<summary>///?錯誤成員列表///?</summary>public?List<ErrorMember>?ErrorMembers?{?get;?set;?}///?<summary>///?驗證結果///?</summary>public?bool?IsVaild?{?get;?set;?}
}
  • 定義操作正則表達式的公共類 RegexHelper(基于 RegularExpression 特性擴展)

using?System;
using?System.Net;
using?System.Text.RegularExpressions;namespace?Jeff.Common.Validatetion;///?<summary>
///?操作正則表達式的公共類
///?Regex 用法參考:https://learn.microsoft.com/zh-cn/dotnet/api/system.text.regularexpressions.regex.-ctor?redirectedfrom=MSDN&view=net-7.0
///?</summary>???
public?class?RegexHelper
{#region?常用正則驗證模式字符串public?enum?ValidateType{Email,?????????????????//?郵箱TelePhoneNumber,???????//?固定電話(座機)MobilePhoneNumber,?????//?移動電話Age,???????????????????//?年齡(1-120?之間有效)Birthday,??????????????//?出生日期Timespan,??????????????//?時間戳IdentityCardNumber,????//?身份證IpV4,??????????????????//?IPv4?地址IpV6,??????????????????//?IPV6?地址Domain,????????????????//?域名English,???????????????//?英文字母Chinese,???????????????//?漢字MacAddress,????????????//?MAC?地址Url,???????????????????//?URL?}private?static?readonly?Dictionary<ValidateType,?string>?keyValuePairs?=?new?Dictionary<ValidateType,?string>{{?ValidateType.Email,?_Email?},{?ValidateType.TelePhoneNumber,_TelephoneNumber?},??{?ValidateType.MobilePhoneNumber,_MobilePhoneNumber?},?{?ValidateType.Age,_Age?},?{?ValidateType.Birthday,_Birthday?},?{?ValidateType.Timespan,_Timespan },?{?ValidateType.IdentityCardNumber,_IdentityCardNumber?},?{?ValidateType.IpV4,_IpV4?},?{?ValidateType.IpV6,_IpV6?},?{?ValidateType.Domain,_Domain?},?{?ValidateType.English,_English?},?{?ValidateType.Chinese,_Chinese?},?{?ValidateType.MacAddress,_MacAddress?},?{?ValidateType.Url,_Url?},?};public?const?string?_Email?=?@"^(\w)+(\.\w)*@(\w)+((\.\w+)+)$";?//?^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$?,?[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}public?const?string?_TelephoneNumber?=?@"(d+-)?(d{4}-?d{7}|d{3}-?d{8}|^d{7,8})(-d+)?";?//座機號碼(中國大陸)public?const?string?_MobilePhoneNumber?=?@"^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$";?//移動電話public?const?string?_Age?=?@"^(?:[1-9][0-9]?|1[01][0-9]|120)$";?//?年齡?1-120?之間有效public?const?string?_Birthday?=?@"^((?:19[2-9]\d{1})|(?:20(?:(?:0[0-9])|(?:1[0-8]))))((?:0?[1-9])|(?:1[0-2]))((?:0?[1-9])|(?:[1-2][0-9])|30|31)$";public?const?string?_Timespan =?@"^15|16|17\d{8,11}$";?//?目前時間戳是15開頭,以后16、17等開頭,長度 10?位是秒級時間戳的正則,13 位時間戳是到毫秒級的。public?const?string?_IdentityCardNumber?=?@"^[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)$";public?const?string?_IpV4?=?@"^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$";public?const?string?_IpV6?=?@"^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$";public?const?string?_Domain?=?@"^[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?$";public?const?string?_English?=?@"^[A-Za-z]+$";public?const?string?_Chinese?=?@"^[\u4e00-\u9fa5]{0,}$";public?const?string?_MacAddress?=?@"^([0-9A-F]{2})(-[0-9A-F]{2}){5}$";public?const?string?_Url?=?@"^[a-zA-z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$";#endregion///?<summary>///?獲取驗證模式字符串///?</summary>///?<param?name="validateType"></param>///?<returns></returns>public?static?(bool?hasPattern,?string?pattern)?GetValidatePattern(ValidateType?validateType)?{bool?hasPattern?=?keyValuePairs.TryGetValue(validateType,?out?string??pattern);return?(hasPattern,?pattern????string.Empty);}#region?驗證輸入字符串是否與模式字符串匹配///?<summary>///?驗證輸入字符串是否與模式字符串匹配///?</summary>///?<param?name="input">輸入的字符串</param>///?<param?name="validateType">模式字符串類型</param>///?<param?name="matchTimeout">超時間隔</param>///?<param?name="options">篩選條件</param>///?<returns></returns>public?static?(bool?isMatch,?string?info)?IsMatch(string?input,?ValidateType?validateType,?TimeSpan?matchTimeout,?RegexOptions?options?=?RegexOptions.None){var?(hasPattern,?pattern)?=?GetValidatePattern(validateType);if?(hasPattern?&&?!string.IsNullOrWhiteSpace(pattern)){bool?isMatch?=?IsMatch(input,?pattern,?matchTimeout,?options);if?(isMatch)?return?(true,?"Format?validation?passed.");?//?格式驗證通過。else?return?(false,?"Format?validation?failed.");?//?格式驗證未通過。}return?(false,?"Unknown?ValidatePattern.");?//?未知驗證模式}///?<summary>///?驗證輸入字符串是否與模式字符串匹配,匹配返回true///?</summary>///?<param?name="input">輸入字符串</param>///?<param?name="pattern">模式字符串</param>????///?<returns></returns>public?static?bool?IsMatch(string?input,?string?pattern){return?IsMatch(input,?pattern,?TimeSpan.Zero,?RegexOptions.IgnoreCase);}///?<summary>///?驗證輸入字符串是否與模式字符串匹配,匹配返回true///?</summary>///?<param?name="input">輸入的字符串</param>///?<param?name="pattern">模式字符串</param>///?<param?name="matchTimeout">超時間隔</param>///?<param?name="options">篩選條件</param>///?<returns></returns>public?static?bool?IsMatch(string?input,?string?pattern,?TimeSpan?matchTimeout,?RegexOptions?options?=?RegexOptions.None){return?Regex.IsMatch(input,?pattern,?options,?matchTimeout);}#endregion
}
  • 定義驗證結果統一模型格式類 ResponseInfo(此類通常也是通用的數據響應模型類)

namespace?Jeff.Common.Model;public?sealed?class?ResponseInfo<T>?where?T?:?class
{/*Microsoft.AspNetCore.Http.StatusCodesSystem.Net.HttpStatusCode*////?<summary>///?響應代碼(自定義)///?</summary>public?int?Code?{?get;?set;?}///?<summary>///?接口狀態///?</summary>public?bool?Success?{?get;?set;?}#region?此處可以考慮多語言國際化設計(語言提示代號對照表)///?<summary>///?語言對照碼,參考:https://blog.csdn.net/shenenhua/article/details/79150053///?</summary>public?string?Lang?{?get;?set;?}?=?"zh-cn";///?<summary>///?提示信息///?</summary>public?string?Message?{?get;?set;?}?=?string.Empty;#endregion///?<summary>///?數據體///?</summary>public?T??Data?{?get;?set;?}
}
  • 實現驗證助手類 ValidatetionHelper,配合 System.ComponentModel.DataAnnotations 類使用

//?數據注解,https://learn.microsoft.com/zh-cn/dotnet/api/system.componentmodel.dataannotations?view=net-7.0
using?System.ComponentModel.DataAnnotations;
using?Jeff.Common.Model;namespace?Jeff.Common.Validatetion;///?<summary>
///?驗證助手類
///?</summary>
public?sealed?class?ValidatetionHelper
{///?<summary>///?DTO?模型校驗///?</summary>///?<param?name="value"></param>///?<returns></returns>public?static?ValidResult?IsValid(object?value){var?result?=?new?ValidResult();try{var?validationContext?=?new?ValidationContext(value);var?results?=?new?List<ValidationResult>();bool?isValid?=?Validator.TryValidateObject(value,?validationContext,?results,?true);result.IsVaild?=?isValid;if?(!isValid){foreach?(ValidationResult??item?in?results){result.ErrorMembers.Add(new?ErrorMember(){ErrorMessage?=?item.ErrorMessage,ErrorMemberName?=?item.MemberNames.FirstOrDefault()});}}}catch?(ValidationException?ex){result.IsVaild?=?false;result.ErrorMembers?=?new?List<ErrorMember>{new?ErrorMember(){ErrorMessage?=?ex.Message,ErrorMemberName?=?"Internal?error"}};}return?result;}///?<summary>///?DTO?模型校驗統一響應信息///?</summary>///?<typeparam?name="T"></typeparam>///?<param?name="model"></param>///?<returns></returns>public?static?ResponseInfo<ValidResult>?GetValidInfo<T>(T?model)?where?T?:?class{var?result?=?new?ResponseInfo<ValidResult>();var?validResult?=?IsValid(model);if?(!validResult.IsVaild){result.Code?=?420;result.Message?=?"DTO?模型參數值異常";result.Success?=?false;result.Data?=?validResult;}else{result.Code?=?200;result.Success?=?true;result.Message?=?"DTO?模型參數值合法";}return?result;}
}

如何使用 DataAnnotations 封裝的特性校驗助手?

  • 首先定義一個數據模型類(DTO),添加校驗特性 ValidationAttribute

using?System.ComponentModel.DataAnnotations;
using?Jeff.Common.Validatetion;namespace?Jeff.Comm.Test;public?class?Person
{[Display(Name?=?"姓名"),?Required(ErrorMessage?=?"{0}必須填寫")]public?string?Name?{?get;?set;?}[Display(Name?=?"郵箱")][Required(ErrorMessage?=?"{0}必須填寫")][RegularExpression(RegexHelper._Email,?ErrorMessage?=?"RegularExpression:?{0}格式非法")][EmailAddress(ErrorMessage?=?"EmailAddress:?{0}格式非法")]public?string?Email?{?get;?set;?}[Display(Name?=?"Age年齡")][Required(ErrorMessage?=?"{0}必須填寫")][Range(1,?120,?ErrorMessage?=?"超出范圍")][RegularExpression(RegexHelper._Age,?ErrorMessage?=?"{0}超出合理范圍")]public?int?Age?{?get;?set;?}[Display(Name?=?"Birthday出生日期")][Required(ErrorMessage?=?"{0}必須填寫")][RegularExpression(RegexHelper._Timespan,?ErrorMessage?=?"{0}超出合理范圍")]public?TimeSpan?Birthday?{?get;?set;?}[Display(Name?=?"Address住址")][Required(ErrorMessage?=?"{0}必須填寫")][StringLength(200,?MinimumLength?=?10,?ErrorMessage?=?"{0}輸入長度不正確")]public?string?Address?{?get;?set;?}[Display(Name?=?"Mobile手機號碼")][Required(ErrorMessage?=?"{0}必須填寫")][RegularExpression(RegexHelper._MobilePhoneNumber,?ErrorMessage?=?"{0}格式非法")]public?string?Mobile?{?get;?set;?}[Display(Name?=?"Salary薪水")][Required(ErrorMessage?=?"{0}必須填寫")][Range(typeof(decimal),?"1000.00",?"3000.99")]public?decimal?Salary?{?get;?set;?}[Display(Name?=?"MyUrl連接")][Required(ErrorMessage?=?"{0}必須填寫")][Url(ErrorMessage?=?"Url:{0}格式非法")][RegularExpression(RegexHelper._Url,?ErrorMessage?=?"RegularExpression:{0}格式非法")]public?string?MyUrl?{?get;?set;?}
}
  • 控制臺調用通用校驗助手驗證方法 ValidatetionHelper.IsValid()ValidatetionHelper.GetValidInfo()

//?通用模型數據驗證測試
static?void?ValidatetionTest()?
{var?p?=?new?Person{Name?=?"",Age?=?-10,Email?=?"www.baidu.com",MobilePhoneNumber?=?"12345",Salary?=?4000,MyUrl?=?"aaa"};//?調用通用模型校驗var?result?=?ValidatetionHelper.IsValid(p);if?(!result.IsVaild){foreach?(ErrorMember?errorMember?in?result.ErrorMembers){//?控制臺打印字段驗證信息Console.WriteLine($"{errorMember.ErrorMemberName}:{errorMember.ErrorMessage}");}}Console.WriteLine();//?調用通用模型校驗,返回統一數據格式var?validInfo?=?ValidatetionHelper.GetValidInfo(p);var?options?=?new?JsonSerializerOptions{Encoder?=?JavaScriptEncoder.UnsafeRelaxedJsonEscaping,?//?設置中文編碼亂碼WriteIndented?=?false};string?jsonStr?=?JsonSerializer.Serialize(validInfo,?options);Console.WriteLine($"校驗結果返回統一數據格式:{jsonStr}");
}

在控制臺Program.Main 方法中調用 ValidatetionTest() 方法:

internal?class?Program
{static?void?Main(string[]?args){Console.WriteLine("Hello,?DataAnnotations!");{#region?數據注解(DataAnnotations)模型驗證ValidatetionTest();?#endregion}Console.ReadKey();}

啟動控制臺,輸出如下信息:

b49133362f905871efe19926b24c67c1.png

ValidatetionHelper.IsValid

如何實現自定義的驗證特性?

當我們碰到這些參數需要驗證的時候,而上面內置類提供的特性又不能滿足需求時,此時我們可以實現自定義的驗證特性來滿足校驗需求,按照微軟給出的編碼規則,我們只需繼承 ValidationAttribute 類,并重寫 IsValid() 方法即可。

自定義校驗特性案例

比如實現一個密碼強度的驗證,實現步驟如下:

  • 定義密碼強度規則,只包含英文字母、數字和特殊字符的組合,并且組合長度至少 8 位數

///?<summary>
///?只包含英文字母、數字和特殊字符的組合
///?</summary>
///?<returns></returns>
public?static?bool?IsCombinationOfEnglishNumberSymbol(string?input,?int??minLength?=?null,?int??maxLength?=?null)
{var?pattern?=?@"(?=.*\d)(?=.*[a-zA-Z])(?=.*[^a-zA-Z\d]).";if?(minLength?is?null?&&?maxLength?is?null)pattern?=?$@"^{pattern}+$";else?if?(minLength?is?not?null?&&?maxLength?is?null)pattern?=?$@"^{pattern}{{{minLength},}}$";else?if?(minLength?is?null?&&?maxLength?is?not?null)pattern?=?$@"^{pattern}{{1,{maxLength}}}$";elsepattern?=?$@"^{pattern}{{{minLength},{maxLength}}}$";return?Regex.IsMatch(input,?pattern);
}
  • 實現自定義特性 EnglishNumberSymbolCombinationAttribute,繼承自 ValidationAttribute

using?System.ComponentModel.DataAnnotations;namespace?Jeff.Common.Validatetion.CustomAttributes;///?<summary>
///?是否是英文字母、數字和特殊字符的組合
///?</summary>
public?class?EnglishNumberSymbolCombinationAttribute?:?ValidationAttribute
{///?<summary>///?默認的錯誤提示信息///?</summary>private?const?string?error?=?"無效的英文字母、數字和特殊字符的組合";protected?override?ValidationResult?IsValid(object?value,?ValidationContext?validationContext){if?(value?is?null)?return?new?ValidationResult("參數值為?null");//if?(value?is?null)//{//????throw?new?ArgumentNullException(nameof(attribute));//}//?驗證參數邏輯?value?是需要驗證的值,而?validationContext?中包含了驗證相關的上下文信息,這里可自己封裝一個驗證格式的?FormatValidation?類if?(FormatValidation.IsCombinationOfEnglishNumberSymbol(value?as?string,?8))//驗證成功返回?successreturn?ValidationResult.Success;//不成功?提示驗證錯誤的信息else?return?new?ValidationResult(ErrorMessage????error);}
}

以上就實現了一個自定義規則的 自定義驗證特性,使用方式很簡單,可以把它附屬在我們 請求的參數 上或者 DTO 里的屬性,也可以是 Action 上的形參,如下所示:

public?class?CreateDTO
{[Required]public?string?StoreName?{?get;?init;?}[Required]//?附屬在?DTO?里的屬性[EnglishNumberSymbolCombination(ErrorMessage?=?"UserId?必須是英文字母、數字和特殊符號的組合")]public?string?UserId?{?get;?init;?}
}
...
//?附屬在?Action?上的形參
[HttpGet]
public?async?ValueTask<ActionResult>?Delete([EnglishNumberSymbolCombination]string?userId,?string?storeName)

該自定義驗證特性還可以結合 DataAnnotations 內置的 [Compare] 特性,可以實現賬號注冊的密碼確認驗證(輸入密碼和確認密碼是否一致性)。關于更多自定義參數校驗特性,感興趣的小伙伴可參照上面案例的實現思路,自行擴展實現喲。

總結

對于模型參數的校驗,在實際項目系統中是非常有必要性的(通常在數據源頭提供驗證),利用 .NET 內置的 DataAnnotations(數據注解)提供的特性校驗,可以很方便的實現通用的模型校驗助手,關于其他特性的用法,請自行參考微軟官方文檔,這里注意下RegularExpressionAttribute(指定 ASP.NET 動態數據中的數據字段值必須與指定的正則表達式匹配),該特性可以方便的接入正則匹配驗證,當遇到復雜的參數校驗時,可以快速方便的擴展自定義校驗特性,從此告別傳統編碼中各種 if(xxx != yyyy) 判斷的驗證,讓整體代碼編寫更佳簡練干凈。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/279858.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/279858.shtml
英文地址,請注明出處:http://en.pswp.cn/news/279858.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

網線的做法 及 POE的介紹

網線的做法 以太網線采用差分方式傳輸。所謂差分方式傳輸&#xff0c;就是發送端在兩條信號線上傳輸幅值相等相位相反的電信號&#xff0c;接收端對接受的兩條線信號作減法運算&#xff0c;這樣獲得幅值翻倍的信號。其抗干擾的原理是&#xff1a;假如兩條信號線都受到了同樣&am…

unity 使用tile_如何使用Tile從網上查找電話

unity 使用tileTile is a fantastic little gadget that can help you find your lost keys or wallet. However, it can also locate and ring your phone, even if you never buy a single physical Tile. Here’s how to find your lost phone using the Tile app on the we…

你與一份好簡歷之間的距離

閱讀本文大概需要 2.7 分鐘。每年年初都是企業的招聘旺季&#xff0c;對應的三四月份絕對跳槽、找工作的好時機&#xff0c;業內經常稱呼這兩個月為金三銀四。實力雄厚的人&#xff0c;那個月找工作問題都不大&#xff0c;但是也會盡量挑選個好時機&#xff0c;能有更多的選擇。…

Python 循環刪除指定文件夾下所有的.longtian類型文件

# -*- coding: utf-8 -*-import os#遍歷文件夾刪除文件 def traversing_dir(rootDir):#遍歷根目錄for root,dirs,files in os.walk(rootDir):for file in files:#文件后綴名extFileos.path.splitext(file)[1]if extFile".longtian":os.remove(os.path.join(root,file…

《ASP.NET Core 6框架揭秘實例》演示[35]:利用Session保留語境

客戶端和服務器基于HTTP的消息交換就好比兩個完全沒有記憶能力的人在交流&#xff0c;每次單一的HTTP事務體現為一次“一問一答”的對話。單一的對話毫無意義&#xff0c;在在同一語境下針對某個主題進行的多次對話才會有結果。會話的目的就是在同一個客戶端和服務器之間建立兩…

Vincross孫天齊:人機界面的突破將引發科技革命

8月23—27日&#xff0c;世界機器人大會在北京舉辦&#xff0c;全球各國機器人領域的優秀企業悉數亮相&#xff0c;五花八門的機器人及產業鏈上下游最新技術均能在這次盛會上找到蹤跡&#xff0c;整個會場充滿了未來感與時代發展的氣息。 大會中智慧城市服務機器人技術與應用專…

如何在Windows上使用64位Web瀏覽器

Google and Mozilla now offer 64-bit versions of Chrome and Firefox for Windows. Here’s how to find out what version you’re running and how to upgrade. Google和Mozilla現在提供適用于Windows的64位版本的Chrome和Firefox。 這是找出正在運行的版本以及如何升級的方…

立下“去O”Flag的AWS,悄悄修煉了哪些內功?

AWS re:Invent 2018大會上&#xff0c;AWS首席執行執行官Andy Jassy 表示到 2019 年底&#xff0c;亞馬遜將全面放棄使用 Oracle 數據庫&#xff0c;97&#xff05;的“關鍵任務數據庫”將運行在亞馬遜自己的數據庫服務上。 如今&#xff0c;2019年已經過去了四分之一&#xff…

static作用:靜態變量的生存周期和作用域

首先要理解生存周期與作用域的區別&#xff1a; 生存周期: 變量從定義到銷毀的時間范圍。存放在全局數據區的變量的生存周期存在于整個程序運行期間&#xff0c;而存放在棧中的數據則隨著函數等的作用域結束導致出棧而銷毀&#xff0c;除了靜態變量之外的局部變量都存放于棧中…

劉強東痛批京東高管,拿PPT騙他!網友怒了:愛用 PPT 忽悠的人,他們都遭人痛恨...

這是頭哥侃碼的第272篇原創因為被新冠感染&#xff0c;所以最近兩周都在休養。前幾天&#xff0c;我無意中看到一則有關劉強東的新聞&#xff0c;大致是他在京東內部管理培訓會上痛批部分高管&#xff0c;稱 “拿PPT和假大空詞匯忽悠自己的人就是騙子”&#xff0c;表示部分高管…

關于file的部分簡單命令

1.關于file的簡單命令 2.創建/刪除 文件/目錄 ## -f和-r可以連用&#xff0c;表示強制刪除 3.文件/目錄的復制 ##復制是一個新建的過程&#xff0c;在保持原有不變的基礎上重新再建立一個 4.文件/目錄的移動 ##移動是一個重命名的過程&#xff0c;但不改變其中的內容 本文轉自…

字節與浮點型轉換軟件_如何與另一個防病毒軟件一起運行惡意軟件字節

字節與浮點型轉換軟件Malwarebytes Anti-Malware is a great security tool that’s particularly effective against “potentially unwanted programs (PUPs)” and other nasty software traditional antivirus programs don’t deal with. But it’s intended to be used a…

vsftpd服務的搭建

1.vsftpd介紹vsftpd&#xff1a;是非常安全的ftp守護進程(Very secure ftp Daemon)。進程&#xff1a;正在進行&#xff08;運行running&#xff09;的程序。守護進程Daemon&#xff1a;網絡服務類的程序都會有守護進程。守護進程是指實時監測服務訪問狀態的程序。通常都是在系…

火狐瀏覽器書簽(收藏夾)全部消失,歷史記錄也消失,如何恢復

今天關閉再打開火狐瀏覽器瞬間懵逼&#xff0c;瀏覽器所有的記錄都沒了&#xff0c;映入眼簾的的火狐新手指導頁&#xff0c;而且主頁導航變成了hao123&#xff0c;我估計是外部程序篡改了瀏覽器配置&#xff0c;或者其他異常導致瀏覽器重置。書簽、歷史記錄對開發人員的重要性…

apple tv 開發_如何防止Apple TV進入睡眠狀態

apple tv 開發Your Apple TV, by default, goes to sleep fairly quickly when not in use. That’s great for power saving but not so great if you like to keep it on. Let’s take a look at how to extend how long it stays awake or disable sleep mode altogether. 默…

MASA MAUI Plugin (七)應用通知角標(小紅點)Android+iOS

背景MAUI的出現&#xff0c;賦予了廣大Net開發者開發多平臺應用的能力&#xff0c;MAUI 是Xamarin.Forms演變而來&#xff0c;但是相比Xamarin性能更好&#xff0c;可擴展性更強&#xff0c;結構更簡單。但是MAUI對于平臺相關的實現并不完整。所以MASA團隊開展了一個實驗性項目…

SAP如何查看會計憑證

比如SAP中已經存在著很多會計憑證&#xff0c;你想要進入SAP隨便看看會計憑證的列表&#xff0c;怎么操作呢&#xff1f;事務碼 IDCNDOC運行結果看到了憑證們&#xff0c;和每個憑證的行項目們上圖看到的結果比較凌亂實際上我們重新進入IDCNDOC可以通過輸入的勾選&#xff0c;選…

Spring Data Redis與Jedis的選擇(轉)

說明&#xff1a;內容可能有點舊&#xff0c;需要在業務上做權衡。 Redis的客戶端有兩種實現方式&#xff0c;一是可以直接調用Jedis來實現&#xff0c;二是可以使用Spring Data Redis&#xff0c;通過Spring的封裝來調用。應該使用哪一個呢&#xff1f;基于當前版本Spring Dat…

C# 溫故而知新:Stream篇(五)

MemoryStream 目錄&#xff1a; 1 簡單介紹一下MemoryStream 2 MemoryStream和FileStream的區別 3 通過部分源碼深入了解下MemoryStream 4 分析MemorySteam最常見的OutOfMemory異常 5 MemoryStream 的構造 6 MemoryStream 的屬性 7 MemoryStream 的方法 8 MemoryStream 簡單示例…

dosbox 自動運行_如何使用DOSBox運行DOS游戲和舊應用

dosbox 自動運行New versions of Windows don’t fully support classic DOS games and other old applications — this is where DOSBox comes in. It provides a full DOS environment that runs ancient DOS apps on modern operating systems. Windows的新版本不完全支持經…