数据校验

关于数据校验

数据校验字面上的意思就是对使用者提交过来的数据进行合法性验证。在一套完善的应用系统中,数据有效性校验是必不可少的业务处理第一道关卡。

数据校验的好处

  • 过滤不安全数据,提高系统的安全性
  • 减少不必要的业务异常处理,提高系统的响应速度
  • 大大提高系统稳定性
  • 大数据并发时起着一定的缓冲作用

数据校验方式

  • 传统方式,在业务代码之前手动验证
  • Mvc 特性方式,Mvc 内置的 DataAnnotations 方式
  • 推荐方式,Furion 框架内置的 DataValidation 验证
  • 其他方式,使用第三方验证库,如 FluentValidation

传统方式

  1. public bool Insert(Person person)
  2. {
  3. // 验证参数
  4. if(string.IsNullOrEmty(person.Name))
  5. {
  6. throw new System.Exception("名字不能为空");
  7. }
  8. if(person.Age < 18)
  9. {
  10. throw new System.Exception("年龄不能小于 18 岁");
  11. }
  12. if(!person.Password.Equals(person.ConfirmPassword)
  13. {
  14. throw new System.Exception("两次密码不一致");
  15. }
  16. // 业务代码
  17. _repository.Insert(person.Adapt<PersonEntity>());
  18. // ...
  19. }
  • 从上面的代码看起来,似乎没有什么不妥,但是从一个程序可维护性来说,这是一个糟糕的代码,因为该业务代码中包含了太多与业务无关的数据验证。
  • 试想一下,如果这个 Person 有 几十个参数都需要验证呢?可想而知,这是一个庞大的业务代码。
  • 再者,如果其他地方也需要用到这个 Person 类验证呢?那代码好比老鼠啃过的面包屑一样,到处都是。
  • 如此得知,这样的方式是极其不推荐的,不但污染了业务代码,也破坏了业务职责单一性原理,也让验证逻辑无法实现通用,后续维护难度大大升级。

Mvc 特性方式

在 ASP.NET Core 中,微软为我们提供了全新的 特性 验证方式,可通过对对象贴特性实现数据验证。这种方式有效的将数据校验和业务代码剥离开来,而且容易使用和拓展

在模型中验证

  1. using System.ComponentModel.DataAnnotations;
  2. namespace Hoa.Application.Authorization.Dtos
  3. {
  4. public class SignInInput
  5. {
  6. [Required] // 必填验证
  7. [MinLength(4)] // 最小长度验证
  8. public string Account { get; set; }
  9. [Required] // 必填验证
  10. [MaxLength(32)] // 最大长度验证
  11. public string Password { get; set; }
  12. }
  13. }

在参数中验证

  1. public void CheckMethodParameterValid(
  2. [Required] // 必填验证
  3. [MinLength(4)] // 最小长度验证
  4. string name,
  5. int age,
  6. [Required] // 必填验证
  7. [RegularExpression("[a-zA-Z0-9_]{8,30}") // 正则表达式验证
  8. string password,
  9. [Required] // 必填验证
  10. [RegularExpression("[a-zA-Z0-9_]{8,30}") // 正则表达式验证
  11. string confirmPassword
  12. )
  13. {
  14. // TODO
  15. }

自定义特性验证

  1. public class ClassicMovieAttribute : ValidationAttribute
  2. {
  3. public ClassicMovieAttribute(int year)
  4. {
  5. Year = year;
  6. }
  7. public int Year { get; }
  8. public string GetErrorMessage() =>
  9. $"Classic movies must have a release year no later than {Year}.";
  10. protected override ValidationResult IsValid(object value, ValidationContext validationContext)
  11. {
  12. var movie = (Movie)validationContext.ObjectInstance;
  13. var releaseYear = ((DateTime)value).Year;
  14. if (movie.Genre == Genre.Classic && releaseYear > Year)
  15. {
  16. return new ValidationResult(GetErrorMessage());
  17. }
  18. return ValidationResult.Success;
  19. }
  20. }

IValidatableObject 复杂验证

  1. using System.Collections.Generic;
  2. public class DtoModel : IValidatableObject
  3. {
  4. [Required]
  5. [StringLength(100)]
  6. public string Title { get; set; }
  7. // 你的验证逻辑
  8. public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
  9. {
  10. // 还可以解析服务
  11. var service = validationContext.GetService(typeof(类型));
  12. if (你的逻辑代码)
  13. {
  14. yield return new ValidationResult(
  15. "错误消息"
  16. ,new[] { nameof(Title) } // 验证失败的属性
  17. );
  18. }
  19. }
  20. }
  • Mvc 特性方式极大的将业务逻辑和验证进行了剥离和解耦,而且还能实现自定义复杂验证。
  • 但是 Mvc 特性验证方式有几个明显的缺点:

    • 只能在 控制器 中的 Action(动作方法)中使用
    • 无法在任意类、任意方法中使用
    • 内置的验证类型非常有限,且不易拓展
    • 不支持验证消息后期配置
  • 所以,Furion 提供了新的验证引擎 DataValidation,在完全兼容 Mvc 内置验证的同时提供了大量常见验证、复杂验证、自定义验证等能力。