.NETCore下的模型验证相信绝大部分的.NET开发者或多或少的都用过,微软官方提供的模型验证相关的类位于System.ComponentModel.DataAnnotations命令空间下,在使用的时候只需要给属性添加不同的特性即可实现对应的模型验证。如下所示:

public class Movie
{public int Id { get; set; }[Required][StringLength(100)]public string Title { get; set; }
}

在WebApi中,当请求接口时,程序会自动对模型进行验证,如无法验证通过,则会直接终止后续的逻辑执行,并响应400状态码,响应内容如下所示:

{
"type": "https://tools.ietf.org/html/rfc7231#p-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-4b16460fc83d7b4daa4f10d939016982-f823eebede419a4a-00",
"errors": {
"aa": [
"The aa field is required."
]
}
}

当然,你也可以自定义响应的内容,这不是本文的重点。本文的重点是,.NETCore系统默认的模型验证功能并不够强大,仅支持在Controller的Action中使用,不支持非Controller中或者控制台程序的验证,且代码侵入性较强。

而FluentValidation(https://fluentvalidation.net/ )则是功能更为强大的模型验证框架,支持任何场景下的模型验证,且不侵入代码。

下面就来和笔者一起了解下FluentValidation的用法。

接入

FluentValidation支持一下平台:

  • .NET 4.6.1+

  • .NET Core 2.0+

  • .NET Standard 2.0+

各个平台的集成方式大同小异,本文仅讲解.NETCore3.1的集成方式。

首先,使用NuGet安装FluentValidation.AspNetCore依赖。

添加需要验证的模型类,如Student类,代码如下:

public class Student
{public int Id { get; set; }public int Age { get; set; }public string Name { get; set; }
}

然后创建类StudentValidator,并集成类AbstractValidator<Student>,代码如下:

public class StudentValidator : AbstractValidator<Student>
{public StudentValidator(){RuleFor(x => x.Age).InclusiveBetween(10, 50);RuleFor(x => x.Name).NotEmpty().MaximumLength(5);}
}

上述的验证类中,要求Age大于10且小于50,Name不为空,且长度小于5。

最后,还需要将验证类注册到服务中。修改Startup的ConfigureServices,部分代码如下:

services.AddControllers().AddFluentValidation(conf =>{conf.RegisterValidatorsFromAssemblyContaining<StudentValidator>();conf.RunDefaultMvcValidationAfterFluentValidationExecutes = false;});

上述代码中,RegisterValidatorsFromAssemblyContaining方法的作用是扫描StudentValidator类所在的程序集中的所有验证类,并注册到服务中。

RunDefaultMvcValidationAfterFluentValidationExecutes为false时,会屏蔽掉系统默认的模型验证,如需兼容系统默认的模型验证,将RunDefaultMvcValidationAfterFluentValidationExecutes的值改为true即可。此参数默认为true。

下面在Controller中,添加一个Action,代码如下:

[HttpPost]
public IActionResult Add([FromBody] Student student)
{return Ok(student);
}

打开swagger,访问接口,响应如下所示:

{"type": "https://tools.ietf.org/html/rfc7231#p-6.5.1","title": "One or more validation errors occurred.","status": 400,"traceId": "00-6331a76578228b4cb9044aa40f514bc9-89fd8547c1921340-00","errors": {"Age": ["'Age' 必须在 10 (包含)和 25 (包含)之间, 您输入了 0。"],"Name": ["'Name' 必须小于或等于5个字符。您输入了6个字符。"]}
}

至此,在 ASP.NET Core中集成FluentValidation就完成了。但到现在为止,这和系统默认的模型验证并没有区别。在文章的开头笔者也提到过,FluentValidation不仅支持Controller中对模型进行验证,下面的代码就是非Controller场景下的验证。

public class DemoService
{private readonly IValidator<Student> _studentValidator;public DemoService(IValidator<Student> studentValidator){_studentValidator = studentValidator;}public bool Run(Student student){var valid = _studentValidator.Validate(student);return valid.IsValid;}
}

在上述代码中,通过构造函数注入的方式,获取到了IValidator<Student>实例,在Run方法中只需要调用Validate方法,参数是需要验证的对象,返回的对象就包含了验证的是否通过以及不通过时,具体的错误信息。

基础用法

内置规则

FluentValidation内置了多个常用的验证器,下面简单介绍几个特别常用或容易出错的验证器。

NotNull 和 NotEmpty

NotNull是确保指定的属性不为null,NotEmpty则表示确保指定的属性不为null、空字符串或空白(值类型的默认值,比如int类型的默认值为0),如果int类型属性设置NotEmpty验证器,则当值为0时,验证是无法通过的。

NotEqual 和 Equal

NotEqual 和 Equal分别是不相等和相等验证器,可与指定的值或者指定的属性进行比较。

MaximumLength、MinimumLength和Length

MaximumLength为最大长度验证器,MinimumLength为最小长度验证器,而Length则是二者的结合,需要注意的是,这三种验证器仅对字符串有效,且不会验证null,当值为null时,则不对长度进行验证,所以使用长度验证器时,建议结合NotNull一起使用。

LessThan、LessThanOrEqualTo、GreaterThan、GreaterThanOrEqualTo

上述的几个验证器为比较验证器,仅适用于继承IComparable接口的属性,分别表示的是:小于、小于或等于、大于、大于或等于。

Matches

正则表达式验证器,用于确保指定的属性与给定的正则表达式匹配。

ExclusiveBetween和InclusiveBetween

示例代码如下:

RuleFor(x => x.Id).ExclusiveBetween(1,10);
RuleFor(x => x.Id).InclusiveBetween(1,10);

以上代码均表示输入的Id的值需要在1,10之间,而两者的区别是,InclusiveBetween验证器是包含头和尾的,而ExclusiveBetween是不包含的,例如当Id值为1时,ExclusiveBetween验证失败,但InclusiveBetween则验证成功。

覆盖验证器默认的错误提示

在文章的开头提到了,当验证Student的Age属性不通过时,提示信息是:'Age' 必须在 10 (包含)和 25 (包含)之间, 您输入了 0。

这个提示信息对于开发者来讲,定位问题已经很清晰了,但如果要在WebApi中讲验证的错误信息返回给前端,那么这个提示就会被用户看到,则此错误信息就不太友好,FluentValidation提供了多种覆盖错误提示的方式,下面就来一起看下。

占位符

我们可以将验证Age的代码改为如下所示:

RuleFor(x => x.Age).InclusiveBetween(10, 25).WithMessage("年龄必须在{From}到{To}之间");

当验证不通过时,输出的错误信息则为:年龄必须在10到25之间。

程序自动将{From}和{To}进行了替换。每个验证器的占位符都不一样,有关占位符的完整列表,请查看官方文档 https://docs.fluentvalidation.net/en/latest/built-in-validators.html。

覆盖属性名称

此方法是将属性的名称使用指定的字符串替换,如下所示:

RuleFor(x => x.Age).InclusiveBetween(10, 25).WithName("年龄");

当发生错误时,会自动将系统默认的错误提示信息中的"Age"替换为"年龄"

默认情况下,When或者Otherwise将应用于链式调用的所有前置的验证器,如果只希望条件引用于前面的第一个验证器,则必须使用ApplyConditionTo.CurrentValidator显示指定

 RuleFor(x => x.Age).GreaterThan(10).LessThan(20).When(x => x.Sex == 2,ApplyConditionTo.CurrentValidator);

上述的代码,如果不加ApplyConditionTo.CurrentValidator,则当Sex等于2时,则要求Age大于10且小于20。而Sex不等于2时,则不作任何验证。如果加上ApplyConditionTo.CurrentValidator,则Age大于10的验证跟Sex的值没有任何关系了,程序会始终验证Age是否大于10

带条件的验证规则

使用When方法可控制规则执行的条件。例如,国家的法定结婚年龄为女性20岁,则验证年龄属性时,只有当性别为女时,才对年龄大于等于20进行校验。

RuleFor(x => x.Age).GreaterThan(20).When(x => x.Sex == 2);

相反的,Unless表示的是当指定条件不满足时,才执行校验。

RuleFor(x => x.Age).GreaterThan(20).Unless(x => x.Sex == 2);

上述代码表示当Sex值不为2时,校验Age是否大于等于20

如果需要为多个验证规则指定相同的条件,可以调用When的顶级方法,而不是在规则末尾调用When方法。

When(x => x.Sex == 2, () =>
{RuleFor(x => x.Name).Must(x => !x.EndsWith("国庆"));RuleFor(x => x.Age).LessThan(30);
});

上述代码表示是,当Sex等于2时,Age需要小于30,并且名字不能以"国庆"结尾。

将Otherwise方法链接到When调用,表示When条件不满足时,执行的验证规则。

When(x => x.Sex == 2, () =>
{RuleFor(x => x.Name).Must(x => x.EndsWith("国庆"));RuleFor(x => x.Age).LessThan(30);
}).Otherwise(() =>
{RuleFor(x => x.Age).LessThan(50);
});

上述代码中的Otherwise方法表示的是,当Sex不等于2时,则Age需要小于50

链式调用

当一个属性使用多个验证规则时,可将多个验证器链接在一起,比如,Student类的Name属性不能为空,并且,长度需要小于10,则对应的代码为:

public StudentValidator()
{RuleFor(x =>x.Name).NotEmpty().MaximumLength(10);
}

CascadeMode

CascadeMode是一个枚举类型的属性,有两个选项:Continue和Stop

如果设置为Stop,则检测到失败的验证,则立即终止,不会继续执行剩余属性的验证。默认值为Continue

CascadeMode = CascadeMode.Stop;
RuleFor(x => x.Name).NotEmpty().MaximumLength(10);
RuleFor(x => x.NickName).NotEmpty().MaximumLength(10);

如上述代码所示,当Name值不满足要求时,则会停止对NickName的校验

依赖规则

默认情况下,FluentValidation 中的所有规则都是独立的,不能彼此影响。这是异步验证工作所必需的,也是必要的。但是,在某些情况下,您可能希望确保某些规则仅在另一个规则完成之后执行。您可以使用DependentRules它来做到这一点。

比如,只有身高超过130的儿童,才需要验证是否购票,则可以通过如下的代码实现:

RuleFor(x => x.Height).GreaterThan(130).DependentRules(() =>
{RuleFor(x => x.HasTicket).NotEmpty();
});

高级用法

异步验证

在某些情况下,你可能希望定义异步规则,比如从数据库或者外部api判断。

public StudentValidator(IStudentService studentService)
{_studentService = studentService;RuleFor(x => x.Name).MustAsync(async (name, token) => await _studentService.CheckExist(name));
}

上述代码中,通过一个异步方法的返回值验证Name属性。
另外,如果在非Controller场景下使用,则必须调用ValidateAsync方法进行验证。

转换值

您可以在对属性值执行验证之前使用 Transform方法转换属性值。

RuleFor(x => x.Weight).Transform(x => int.TryParse(x, out int val)?(int?)val:null).GreaterThan(10);

上述代码先试图将string类型转换成int类型,如果转换成功则对转换后的值做大于验证。如果转换失败,则不做验证。

回调

如果验证失败,可以使用回调做一些操作。

RuleFor(x => x.Weight).NotEmpty().OnFailure(x =>{Console.WriteLine("验证失败");});

预验证

如果需要每次调用验证器前运行特定代码,可以通过重写PreValidate方法来做到这一点。

public class StudentValidator : AbstractValidator<Student>
{public StudentValidator(){RuleFor(x => x.Weight).NotEmpty();}protected override bool PreValidate(ValidationContext<Student> context,ValidationResult result){if (context.InstanceToValidate == null) return true;result.Errors.Add(new ValidationFailure("", "实体不能为null"));return false;}
}

福禄ICH.架构出品

作者:福尔斯

2021年3月

客官,.NETCore无代码侵入的模型验证了解下相关推荐

  1. AREX-携程无代码侵入的自动化回归测试平台

    背景 对于一个初上线的简单服务,只需通过常规的自动化测试加上人工即可解决,但我们线上核心的业务系统往往比较复杂,通常也会频繁的需求迭代,如何保证被修改后的系统原有业务的正确性就比较重要.常规的自动化测 ...

  2. confd_confd + Nacos | 无代码侵入的配置变更管理

    为什么要支持confd,老的应用配置管理模式是启动时读取配置文件,然后重新读取配置文件需要应用重启. 一般的配置管理系统都是代码侵入性的,应用接入配置管理系统都需要使用对应的SDK来查询和监听数据的变 ...

  3. 无代码开发究竟是不是伪需求?

    作者 | Alex Hudson 译者 | 风车云马,责编 | 唐小引 封图 | 付费自东方 IC 出品 | CSDN(ID:CSDNnews) 最近几年里越来越流行一句话,"2020 年将 ...

  4. 如何定义企业级无代码 | 数睿数据解读艾瑞咨询白皮书

    近日,数睿数据携手艾瑞咨询联合发布了<2021年中国企业级无代码开发白皮书>(以下简称<白皮书>),历时三个月,结合各类公开资料.专家访谈与案例分析,<白皮书> 从 ...

  5. 艾瑞咨询《2021年中国企业级无代码开发白皮书》

    无代码开发丨研究报告 核心摘要:近年来,企业信息化建设需求与底层技术发生翻天覆地的变化,传统软件开发模式已无法快速响应复杂多变的企业业务诉求,而IT人才贵.易流失,传统信息化建设低质低效,缺乏创新能力 ...

  6. ML之Validation:机器学习中模型验证方法的简介、代码实现、案例应用之详细攻略

    ML之Validation:机器学习中模型验证方法的简介.代码实现.案例应用之详细攻略 目录 模型验证方法的简介 1.Hold-out验证 2.K-折交叉验证 3.自助重采样 模型验证方法的代码实现 ...

  7. 同步机无传感滑膜观测器模型加代码 仿真模型+代码(基于28035

    同步机无传感滑膜观测器模型加代码 仿真模型+代码(基于28035),典型的smo+pll方案 代码为实际应用代码,非一般玩票代码可比(非ti例程): 少数文件中文注释 ID:6950062162100 ...

  8. 同步机无传感滑膜观测器模型加代码

    同步机无传感滑膜观测器模型加代码 仿真模型+代码(基于28035),典型的smo+pll方案 代码为实际应用代码,非一般玩票代码可比(非ti例程): 少数文件中文注释有乱码,请知悉- YID:6950 ...

  9. 同步机无传感滑膜观测器模型加代码 仿真模型+代码(基于28035),典型的smo+pll方案

    同步机无传感滑膜观测器模型加代码 仿真模型+代码(基于28035),典型的smo+pll方案: 代码为实际应用代码,非一般玩票代码可比(非ti例程): 少数文件中文注释有乱码 :77500621621 ...

最新文章

  1. 【Dual-Path-RNN-Pytorch源码分析】loss函数:SI-SNR
  2. 数据蒋堂 | 大数据集群该不该透明化?
  3. 计算机课程设计结业感言,课程设计感言
  4. 用BlockingQueue实现生产者与消费者问题
  5. VS中使用OpenCV构建应用程序
  6. 基于Walle的多渠道快速打包自动脚本
  7. SAP CRM调查问卷的评分和图表显示功能介绍 1
  8. 往AWS上传文件[超快]
  9. [论文阅读] End-to-End Incremental Learning
  10. OBJECT和EMBED标签(转载)
  11. SAP系统登录以及注册开发人员
  12. 接口(Api)版本号命名规则
  13. [Campus]我的大学
  14. java POI导出excel,导出的excel,打开文件提示文件格式或扩展名无效
  15. 找不到Vivado卸载程序的解决方案
  16. 大数据之路读书笔记-16数据应用
  17. SEAL开源库源码02
  18. 上海计算机学院网上报名,上海市学历积分报名网,成年人考试网上报名
  19. Involution论文解读:从卷积变换出的神经网络新算子
  20. Android 消息队列

热门文章

  1. 蒙特卡洛方法_基本理论-蒙特卡洛方法与定积分
  2. 用U盘或移动硬盘安装Windows7 (超简单制作Win7安装U盘方法)
  3. ReactNative 触摸事件处理
  4. static作用:静态变量的生存周期和作用域
  5. C++中的namespace ----转载
  6. floyd判环算法(龟兔赛跑算法)
  7. 1Android系统移植与驱动开发概述
  8. net MVC 重定向总结
  9. Hadoop 新增删除节点
  10. 总结 一下UML 类图的关系