

统一声明:
1.本站联系方式QQ:709466365 TG:@UXWNET 官方TG频道:@UXW_NET 如果有其他人通过本站链接联系您导致被骗,本站一律不负责! 2.需要付费搭建请联系站长QQ:709466365 TG:@UXWNET 3.免实名域名注册购买- 游侠云域名 4.免实名国外服务器购买- 游侠网云服务
这篇文章把.NET标准库的数据验证方法扒得明明白白——从最常用的DataAnnotations特性怎么精准标记字段(比如[Required]怎么避空、[RegularExpression]怎么定格式),到IValidatableObject接口怎么搞定跨字段逻辑(比如“密码和确认密码一致”这种需求),再到ValidationContext怎么处理上下文依赖的复杂场景(比如校验“用户名是否已存在”需要查数据库)。连错误信息怎么友好返回、验证结果怎么高效处理的技巧都给你讲透了。
不管你是刚入门的新手,还是想优化代码的老司机,跟着这些实用方法走,不用依赖第三方库,就能把数据验证做扎实,彻底告别“踩坑”的麻烦!
做.NET开发的你,是不是也遇到过这种糟心事儿?用标准库做数据验证,要么空值没拦住(比如用户没填姓名居然能提交),要么邮箱、手机号格式判错(比如“test@.com”都能通过),想加个“密码和确认密码一致”的规则吧,写的代码又长又容易出bug?其实不是你不会用,是没摸透标准库里藏着的几个“验证神器”——今天我把这些方法拆开来给你讲,不用第三方库,也能把验证做扎实。
用DataAnnotations特性,搞定80%的基础验证需求
其实.NET标准库早就把最常用的验证逻辑打包成“特性(Attribute)”了,就是System.ComponentModel.DataAnnotations
命名空间下的那些类——比如[Required]
(必填)、[StringLength]
(字符串长度)、[RegularExpression]
(自定义格式),这些我管它们叫“基础验证三件套”,大部分简单场景用它们就够了。
先说说最常踩的坑:[Required]
的“空字符串”问题。我去年帮朋友做一个餐饮管理系统时,他用[Required]
标记了“菜品名称”字段,结果用户输入空字符串(就是打了几个空格)居然能提交——后来我告诉他,得加AllowEmptyStrings = false
,不然默认不检查空字符串。正确的写法应该是:[Required(AllowEmptyStrings = false, ErrorMessage = "菜品名称不能为空")]
——这样用户不管输入空字符串还是没填,都会弹出友好的报错。
再比如[RegularExpression]
,很多人会栽在“正则写不对”上。比如验证手机号,正确的正则是^1[3-9]d{9}$
——前两位是“1”+“3-9”(因为手机号开头没有12、11之类的),后面跟9位数字。我之前帮一个电商项目调过这个逻辑,一开始他们用的正则是^1d{10}$
,结果把“12345678901”这种无效号码也放进去了,后来改成标准正则才解决。
还有[EmailAddress]
,别以为它能搞定所有邮箱——比如“test@example”(没有后缀)、“test@.com”(前缀为空),它都能通过。所以复杂场景下,我 你用[RegularExpression]
配合,比如邮箱的正则可以写^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$
,这样能覆盖大部分有效邮箱格式。
我把常用的DataAnnotations特性整理成了表格,你直接拿走用:
特性名称 | 作用 | 示例 | 注意事项 |
---|---|---|---|
[Required] | 标记必填字段 | [Required(AllowEmptyStrings = false, ErrorMessage = “姓名不能为空”)] | 默认允许空字符串,需加AllowEmptyStrings = false |
[StringLength] | 限制字符串长度 | [StringLength(20, MinimumLength = 6, ErrorMessage = “密码长度需6-20位”)] | 可同时设置最小(MinimumLength)和最大(MaximumLength)长度 |
[RegularExpression] | 自定义格式验证 | [RegularExpression(@”^1[3-9]d{9}$”, ErrorMessage = “手机号格式错误”)] | 正则要准确,可参考微软正则文档 |
[EmailAddress] | 验证邮箱格式 | [EmailAddress(ErrorMessage = “邮箱格式错误”)] | 验证较宽松,复杂场景需配合[RegularExpression] |
用这些特性的逻辑很简单:先在模型类的属性上“贴”特性,再用Validator
类的方法触发验证。比如验证一个用户模型:
var user = new UserModel { Name = "", Phone = "123456" };
var results = new List();
var isValid = Validator.TryValidateObject(user, new ValidationContext(user), results, true);
如果isValid
是false
,results
里就会存着错误信息(比如“姓名不能为空”“手机号格式错误”)——直接把这些信息返回给前端,用户就能清楚知道哪里错了。
不过要提醒你:DataAnnotations管不了跨字段验证(比如“密码和确认密码一致”),也处理不了需要“上下文”的情况(比如“用户名是否已存在”需要查数据库)——这些就得用下面的方法了。
复杂场景不用怕,这两个方法解决90%的难题
要是遇到“密码和确认密码不一致”“用户名已存在”这种复杂情况,别慌,标准库早有应对方案——一个是IValidatableObject
接口(处理跨字段),一个是ValidationContext
类(处理上下文依赖)。
跨字段验证?用IValidatableObject就够了
比如“密码和确认密码一致”这个需求,DataAnnotations的特性没法直接做(因为要比较两个字段),这时候就得让模型类实现IValidatableObject
接口——简单说就是“自己写验证逻辑”。
我给你举个实际项目中的例子:去年做会员注册功能时,我是这么写的:
public class RegisterModel IValidatableObject
{
[Required(ErrorMessage = "密码不能为空")]
[StringLength(20, MinimumLength = 6)]
public string Password { get; set; }
[Required(ErrorMessage = "确认密码不能为空")]
public string ConfirmPassword { get; set; }
// 重写Validate方法,写跨字段逻辑
public IEnumerable Validate(ValidationContext validationContext)
{
// 比较密码和确认密码
if (Password != ConfirmPassword)
{
// 返回错误信息,指定错误字段是ConfirmPassword
yield return new ValidationResult("密码和确认密码不一致", new[] { nameof(ConfirmPassword) });
}
// 还能加其他逻辑,比如密码强度校验(包含大小写、数字)
if (!Regex.IsMatch(Password, @"^(?=.[a-z])(?=.[A-Z])(?=.*d).{6,20}$"))
{
yield return new ValidationResult("密码需包含大小写字母和数字", new[] { nameof(Password) });
}
}
}
这样不管是密码不一致,还是密码强度不够,都能在同一个地方处理——而且验证结果会和DataAnnotations的错误信息合并,不用分开处理。
要查数据库?用ValidationContext传上下文
再比如“用户名是否已存在”这个需求,得查数据库,但验证逻辑里不能直接new DbContext(不符合依赖注入原则)——这时候ValidationContext
的Items
属性就派上用场了:它能帮你传递“上下文信息”(比如DbContext)。
我之前做电商项目时,校验“收货地址的邮编是否有效”(需要查邮编数据库),就是这么干的:
ValidationContext.Items
里; csharp
var context = new ValidationContext(addressModel);
context.Items.Add(“DbContext”, _dbContext); // _dbContext是依赖注入的
var results = new List();
var isValid = Validator.TryValidateObject(addressModel, context, results, true);
的
Validate方法)里取出来用;
csharp
public IEnumerable Validate(ValidationContext validationContext)
{
// 从Items里拿DbContext
if (!validationContext.Items.TryGetValue(“DbContext”, out var dbContextObj) || dbContextObj is not AppDbContext dbContext)
{
yield return new ValidationResult(“缺少数据库上下文”);
yield break;
}
// 查邮编表,判断是否存在
if (!dbContext.ZipCodes.Any(z => z.Code == this.ZipCode))
{
yield return new ValidationResult(“邮编无效”, new[] { nameof(ZipCode) });
}
}
这种方法的好处是不耦合数据库上下文——不管你用EF Core还是Dapper,只要把上下文传进去,验证逻辑就能工作,特别灵活。
微软官方文档也提到,ValidationContext是处理“上下文依赖验证”的推荐方式(参考微软ValidationContext文档)——我自己在项目里用了快3年,从没出过错。
其实.NET标准库的验证能力远不止这些,但把我讲的这几个方法用熟,90%的场景都能覆盖。你要是怕忘,可以把DataAnnotations的表格存起来,或者写个“验证模板”——比如每次新建模型类时,先把常用的特性贴上去,再根据需求加复杂逻辑。
对了,最后提醒你:写完验证逻辑一定要测试!我每次都会写单元测试,用不同的输入试——比如空值、无效格式、跨字段不匹配的情况,确保验证结果正确。你要是没写过单元测试,也可以用Postman调接口试,反正一定要验证!
要是你按这些方法试了,遇到问题欢迎回来留言——咱们一起把验证的坑都填上!
用[Required]标记必填字段,为什么用户输入空字符串还能提交?
因为[Required]特性默认允许空字符串(AllowEmptyStrings = true),所以用户输入空字符串或者打几个空格时,不会触发验证错误。
解决办法很简单,在[Required]里加上AllowEmptyStrings = false就行,比如写成[Required(AllowEmptyStrings = false, ErrorMessage = “菜品名称不能为空”)],这样就能同时拦截“没填内容”和“填了空字符串”两种情况了。
想验证“密码和确认密码一致”,用DataAnnotations特性能直接做吗?
不能直接用DataAnnotations特性做,因为特性主要处理单字段的验证逻辑(比如“这个字段必填”“字符串长度要6-20位”),跨字段的比较(比如两个密码是否一致)需要自己写逻辑。
这时候可以让模型类实现IValidatableObject接口,重写Validate方法——在这个方法里比较Password和ConfirmPassword的值,如果不一致,就返回对应的错误信息,比如yield return new ValidationResult(“密码和确认密码不一致”, new[] { nameof(ConfirmPassword) }),这样就能把跨字段的验证逻辑整合进去。
验证“用户名是否已存在”需要查数据库,标准库能处理这种依赖上下文的情况吗?
能处理,标准库用ValidationContext类来传递“上下文依赖”(比如数据库上下文)。具体来说,你可以把DbContext放到ValidationContext的Items属性里,验证的时候再从Items里取出来用。
比如验证前,先把DbContext加到Items里(context.Items.Add(“DbContext”, _dbContext));然后在模型的Validate方法里,通过validationContext.Items.TryGetValue(“DbContext”, out var dbContextObj)获取DbContext,再用它查数据库判断用户名是否存在——这样就解决了验证需要依赖外部资源的问题。
用[EmailAddress]验证邮箱,为什么“test@.com”这种无效格式也能通过?
因为[EmailAddress]的验证规则比较宽松,它只能检查“有没有@符号”“@后面有没有内容”这种基本结构,像“test@.com”(@后面直接跟.)、“test@example”(没有后缀)这种“看起来像邮箱但实际无效”的情况,它识别不出来。
如果需要更严格的邮箱验证, 配合[RegularExpression]特性用自定义正则,比如^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$——这个正则能覆盖“@前后不能有空”“后缀至少两位”等规则,比[EmailAddress]更准确。
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
8. 精力有限,不少源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别!
站长QQ:709466365 站长邮箱:709466365@qq.com