阿里云wordpress建站教程,网站怎么建设原始站点,怎么做网站咨询,河南商都建设有限公司网站.net 跨平台参数校验的意义在实际项目开发中#xff0c;无论任何方式、任何规模的开发模式#xff0c;项目中都离不开对接入数据模型参数的合法性校验#xff0c;目前普片的开发模式基本是前后端分离#xff0c;当用户在前端页面中输入一些表单数据时#xff0c;点击提交按….net 跨平台参数校验的意义在实际项目开发中无论任何方式、任何规模的开发模式项目中都离不开对接入数据模型参数的合法性校验目前普片的开发模式基本是前后端分离当用户在前端页面中输入一些表单数据时点击提交按钮触发请求目标服务器的一系列后续操作在这中间的执行过程中标准做法推荐无论是前端代码部分还是服务端代码部分都应该有针对用户输入数据的合法性校验典型做法如下前端部分当用户在页面输入表单数据时前端监听页面表单事件触发相应的数据合法性校验规则当数据非法时合理的提示用户数据错误只有当所有表单数据都校验通过后才继续提交数据给目标后端对应的接口后端部分当前端数据合法校验通过后向目标服务器提交表单数据时服务端接收到相应的提交数据在入口源头出就应该触发相关的合法性校验规则当数据都校验通过后继续执行后续的相关业务逻辑处理反之则响应相关非法数据的提示信息特别说明在实际的项目中无论前端部分还是服务端部分参数的校验都是很有必要性的。无效的参数可能会导致应用程序的异常和一些不可预知的错误行为。常用的参数校验项这里例举一些项目中比较常用的参数模型校验项如下所示Name姓名校验比如需要是纯汉字的姓名Password密码强度验证比如要求用户输入必须包含大小写字母、数字和特殊符号的强密码QQQQ 号码验证是否是有效合法的 QQ 号码China Postal Code中国邮政编码IP AddressIPV4 或者 IPV6 地址验证Phone手机号码或者座机号码合法性验证ID Card身份证号码验证比如15 位和 18 位数身份证号码Email Address邮箱地址的合法性校验String字符串验证比如字段是否不为 null、长度是否超限URL验证属性是否具有 URL 格式Number数值型参数校验数值范围校验比如非负数非负整数正整数等File文件路径及扩展名校验对于参数校验常见的方式有正则匹配校验通过对目标参数编写合法的正则表达式实现对参数合法性的校验。.NET 中内置 DataAnnotations 提供的特性校验上面我们介绍了一些常用的参数验证项接下来我们来了解下在 .NET 中内置提供的 DataAnnotations 数据注解该类提供了一些常用的验证参数特性。官方解释提供用于为 ASP.NET MVC 和 ASP.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?viewnet-7.0基于 DataAnnotations 的通用模型校验封装此处主要是使用了 Validator.TryValidateObject() 方法Validator.TryValidateObject(object instance, ValidationContext validationContext, ICollectionValidationResult? validationResults, bool validateAllProperties);Validator 类提供如下校验方法Validator基于 DataAnnotations 的特性校验助手实现步骤错误成员对象类 ErrorMembernamespace Jeff.Common.Validatetion;/// summary
/// 错误成员对象
/// /summary
public class ErrorMember
{/// summary/// 错误信息/// /summarypublic string? ErrorMessage { get; set; }/// summary/// 错误成员名称/// /summarypublic string? ErrorMemberName { get; set; }
}验证结果类 ValidResultnamespace Jeff.Common.Validatetion;/// summary
/// 验证结果类
/// /summary
public class ValidResult
{public ValidResult(){ErrorMembers new ListErrorMember();}/// summary/// 错误成员列表/// /summarypublic ListErrorMember ErrorMembers { get; set; }/// summary/// 验证结果/// /summarypublic 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?redirectedfromMSDNviewnet-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 DictionaryValidateType, string keyValuePairs new DictionaryValidateType, 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 namevalidateType/param/// returns/returnspublic 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 nameinput输入的字符串/param/// param namevalidateType模式字符串类型/param/// param namematchTimeout超时间隔/param/// param nameoptions筛选条件/param/// returns/returnspublic 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 nameinput输入字符串/param/// param namepattern模式字符串/param /// returns/returnspublic static bool IsMatch(string input, string pattern){return IsMatch(input, pattern, TimeSpan.Zero, RegexOptions.IgnoreCase);}/// summary/// 验证输入字符串是否与模式字符串匹配匹配返回true/// /summary/// param nameinput输入的字符串/param/// param namepattern模式字符串/param/// param namematchTimeout超时间隔/param/// param nameoptions筛选条件/param/// returns/returnspublic 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 ResponseInfoT where T : class
{/*Microsoft.AspNetCore.Http.StatusCodesSystem.Net.HttpStatusCode*//// summary/// 响应代码自定义/// /summarypublic int Code { get; set; }/// summary/// 接口状态/// /summarypublic bool Success { get; set; }#region 此处可以考虑多语言国际化设计语言提示代号对照表/// summary/// 语言对照码参考https://blog.csdn.net/shenenhua/article/details/79150053/// /summarypublic string Lang { get; set; } zh-cn;/// summary/// 提示信息/// /summarypublic string Message { get; set; } string.Empty;#endregion/// summary/// 数据体/// /summarypublic T? Data { get; set; }
}实现验证助手类 ValidatetionHelper配合 System.ComponentModel.DataAnnotations 类使用// 数据注解https://learn.microsoft.com/zh-cn/dotnet/api/system.componentmodel.dataannotations?viewnet-7.0
using System.ComponentModel.DataAnnotations;
using Jeff.Common.Model;namespace Jeff.Common.Validatetion;/// summary
/// 验证助手类
/// /summary
public sealed class ValidatetionHelper
{/// summary/// DTO 模型校验/// /summary/// param namevalue/param/// returns/returnspublic static ValidResult IsValid(object value){var result new ValidResult();try{var validationContext new ValidationContext(value);var results new ListValidationResult();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 ListErrorMember{new ErrorMember(){ErrorMessage ex.Message,ErrorMemberName Internal error}};}return result;}/// summary/// DTO 模型校验统一响应信息/// /summary/// typeparam nameT/typeparam/// param namemodel/param/// returns/returnspublic static ResponseInfoValidResult GetValidInfoT(T model) where T : class{var result new ResponseInfoValidResult();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添加校验特性 ValidationAttributeusing 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();}启动控制台输出如下信息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继承自 ValidationAttributeusing System.ComponentModel.DataAnnotations;namespace Jeff.Common.Validatetion.CustomAttributes;/// summary
/// 是否是英文字母、数字和特殊字符的组合
/// /summary
public class EnglishNumberSymbolCombinationAttribute : ValidationAttribute
{/// summary/// 默认的错误提示信息/// /summaryprivate 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 ValueTaskActionResult Delete([EnglishNumberSymbolCombination]string userId, string storeName)该自定义验证特性还可以结合 DataAnnotations 内置的 [Compare] 特性可以实现账号注册的密码确认验证输入密码和确认密码是否一致性。关于更多自定义参数校验特性感兴趣的小伙伴可参照上面案例的实现思路自行扩展实现哟。总结对于模型参数的校验在实际项目系统中是非常有必要性的通常在数据源头提供验证利用 .NET 内置的 DataAnnotations数据注解提供的特性校验可以很方便的实现通用的模型校验助手关于其他特性的用法请自行参考微软官方文档这里注意下RegularExpressionAttribute指定 ASP.NET 动态数据中的数据字段值必须与指定的正则表达式匹配该特性可以方便的接入正则匹配验证当遇到复杂的参数校验时可以快速方便的扩展自定义校验特性从此告别传统编码中各种 if(xxx ! yyyy) 判断的验证让整体代码编写更佳简练干净。