在上篇我分析了整个ABP中ValitationInterceptor的整个过程,就其中涉及到的Validator过程没有详细的论述,这篇文章就这个过程进行详细的论述,另外任何一个重要的特性如何应用是最关键的部分,这篇文章就通过介绍具体的应用来说用到底在实际的项目中如何使用这些特性。

  在上篇中我们知道MethodInvocationValidator中有一个重要的函数就是SetValidationErrors(validatingObject),这个过程就是查找ABP中的所有Validator类型,然后将当前待验证的validatingObject传入到Validator中进行相关的验证,具体的验证过程我们会通过后面的实例来进行论述,我们先来看看之前的SetValidationErrors函数。

  1. protected virtual void SetValidationErrors(object validatingObject)
  2. {
  3. foreach (var validatorType in _configuration.Validators)
  4. {
  5. if (ShouldValidateUsingValidator(validatingObject, validatorType))
  6. {
  7. using (var validator = _iocResolver.ResolveAsDisposable<IMethodParameterValidator>(validatorType))
  8. {
  9. var validationResults = validator.Object.Validate(validatingObject);
  10. ValidationErrors.AddRange(validationResults);
  11. }
  12. }
  13. }
  14. }

  我们先来从ABP中如何添加Validator说起,然后来分别介绍每一种Validator的实现和作用,我们知道AbpKernelModule是整个ABP系统中最先进行加载的模块,我们来看看这个模块中是如何初始化Validator的,在AbpKernelModule中会按照PreInitialize()、Initialize()、PostInitialize()方法依次进行执行,在PreInitialize()方法中会增加AddMethodParameterValidators()这个方法,在这个方法中会默认添加三种类型的Validator,即DataAnnotationsValidator、ValidatableObjectValidator、CustomValidator这几个类型是ABP系统中默认的Validator类型。

  1. private void AddMethodParameterValidators()
  2. {
  3. Configuration.Validation.Validators.Add<DataAnnotationsValidator>();
  4. Configuration.Validation.Validators.Add<ValidatableObjectValidator>();
  5. Configuration.Validation.Validators.Add<CustomValidator>();
  6. }

  一  DataAnnotationsValidator

  这个Validator主要是对一个对象中的参数进行验证的,它主要是继承自IMethodParameterValidator,我们先来看看这个接口是怎么定义的。

  1. public interface IMethodParameterValidator : ITransientDependency
  2. {
  3. IReadOnlyList<ValidationResult> Validate(object validatingObject);
  4. }

  在这个接口中定义了一个Validate方法用来验证当前的validatingObject,验证之后会返回一个IReadOnlyList<ValidationResult>的验证结果。

  1. public class DataAnnotationsValidator : IMethodParameterValidator
  2. {
  3. public virtual IReadOnlyList<ValidationResult> Validate(object validatingObject)
  4. {
  5. return GetDataAnnotationAttributeErrors(validatingObject);
  6. }
  7.  
  8. /// <summary>
  9. /// Checks all properties for DataAnnotations attributes.
  10. /// </summary>
  11. protected virtual List<ValidationResult> GetDataAnnotationAttributeErrors(object validatingObject)
  12. {
  13. var validationErrors = new List<ValidationResult>();
  14.  
  15. var properties = TypeDescriptor.GetProperties(validatingObject).Cast<PropertyDescriptor>();
  16. foreach (var property in properties)
  17. {
  18. var validationAttributes = property.Attributes.OfType<ValidationAttribute>().ToArray();
  19. if (validationAttributes.IsNullOrEmpty())
  20. {
  21. continue;
  22. }
  23.  
  24. var validationContext = new ValidationContext(validatingObject)
  25. {
  26. DisplayName = property.DisplayName,
  27. MemberName = property.Name
  28. };
  29.  
  30. foreach (var attribute in validationAttributes)
  31. {
  32. var result = attribute.GetValidationResult(property.GetValue(validatingObject), validationContext);
  33. if (result != null)
  34. {
  35. validationErrors.Add(result);
  36. }
  37. }
  38. }
  39.  
  40. return validationErrors;
  41. }
  42. }

  在这个类中我们重点来看看它定义的子方法GetDataAnnotationAttributeErrors,在这个方法中首先获取当前待验证的对象validationObject中所有的属性,然后再看每一个属性是否定义了ValidationAttribute,如果没有定义这个属性那么循环接着继续,然后再定义一个ValidationContext的验证上下文,这个是定义在一个系统级别的程序集中,其默认的命名空间为System.ComponentModel.DataAnnotations,后面通过循环获取定义了ValidationAttribute验证属性的验证结果,并将最后验证的结果返回到之前在接口中定义的IReadOnlyList<ValidationResult>集合中,从而完成最终的验证结果,其实这个是最好理解的,这个在验证一些串属性的长度等方面是非常有用的,特别是使用EntityFrameworkCore框架时,当我们定义领域层Model时,这个对象的属性经常要和数据库中的字段一一对应,如果数据库中的字段定义了长度,那么我们也需要对数据的长度进行验证,这个是非常重要的一个参数验证方式。

  在我们的系统中首先需要验证的就是定义的各种DTO,比如常用的有StringLengthAttribute,这个是继承自ValidationAttribute的一个自定义属性,通常用来验证字符串属性的长度,另外RequiredAttribute也是常见的自定义属性,定义了RequiredAttribute属性,那么当前Dto中这个属性就要求必须赋值,如果值为null,那么就会通过上面定义的DataAnnotationsValidator来进行相关的验证,并将最终的验证结果放到 List<ValidationResult>集合中,然后最终由ABP抛出这些异常信息。这里我们来举出常见的属性的应用。

  1. public class CreateRoleDto
  2. {
  3. [Required]
  4. [StringLength(AbpRoleBase.MaxNameLength)]
  5. public string Name { get; set; }
  6.  
  7. [Required]
  8. [StringLength(AbpRoleBase.MaxDisplayNameLength)]
  9. public string DisplayName { get; set; }
  10.  
  11. public string NormalizedName { get; set; }
  12.  
  13. [StringLength(Role.MaxDescriptionLength)]
  14. public string Description { get; set; }
  15.  
  16. public bool IsStatic { get; set; }
  17.  
  18. public List<string> Permissions { get; set; }
  19. }  

  二  ValidatableObjectValidator

  这个Validator也是用来验证validationObject的,这个对象也是继承自IMethodParameterValidator的,所以这个Validator也实现了里面定义的Validate方法用来验证当前待验证的对象,但是这里还有一个重要的限制就是待验证的这个对象必须继承自IValidatableObject接口,否则是不能进行相关验证的,我们来看看这个接口。

  这个接口也是系统级别的程序集中定义的接口,默认命名空间为System.ComponentModel.DataAnnotations,其内部也只定义了一个Validate方法,用来对当前的参数进行验证并返回最终的验证结果。

  1. public class ValidatableObjectValidator : IMethodParameterValidator
  2. {
  3. public virtual IReadOnlyList<ValidationResult> Validate(object validatingObject)
  4. {
  5. var validationErrors = new List<ValidationResult>();
  6.  
  7. if (validatingObject is IValidatableObject o)
  8. {
  9. validationErrors.AddRange(o.Validate(new ValidationContext(o)));
  10. }
  11.  
  12. return validationErrors;
  13. }
  14. }

  这个Abp中定义的验证器需要我们定义的Dto继承自一个IValidatableObject,这个接口中也定义了一个Validate(ValidationContext validationContext)方法,在这个方法中是一个ValidationContext的上下文对象,在这个上下文中有一个重要的对象就是ObjectInstance,这个对象表示当前验证的DTO对象SelfAddDto,这里面有这个对象所有的属性,在验证的时候我们只需要将最终验证的结果返回就能够进行返回,这里我们来举一个具体的实例来说明。

  1. [AutoMap(typeof(SelfAddedModel))]
  2. public class SelfAddDto:Entity<int>,IHasPerson,IValidatableObject
  3. {
  4. public long UserId { get; set; }
  5. public string UserName { get; set; }
  6.  
  7. public string Country { get; set; }
  8.  
  9. public string Province { get; set; }
  10.  
  11. public string City { get; set; }
  12.  
  13. public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
  14. {
  15. if (string.IsNullOrWhiteSpace(UserName))
  16. {
  17. return new List<ValidationResult>()
  18. {
  19. new ValidationResult("当前输入的参数用户名不能为空")
  20. };
  21. }
  22. return new List<ValidationResult>();
  23. }
  24. }

  在运行当前项目中,如果当前DTO中UserName输入为空时,就会由ABP中抛出该错误,我们来看看最后的结果。

  这个是在Swagger中定义的WebAPI,我们来看最终的结果。

  三 CustomerValidator

  这个是ABP系统中另外定义的一个Validator,顾名思义是一个自定义的Validator,我们首先来看看这个Validator的实现,然后再来一步步去分析。

  1. public class CustomValidator : IMethodParameterValidator
  2. {
  3. private readonly IIocResolver _iocResolver;
  4.  
  5. public CustomValidator(IIocResolver iocResolver)
  6. {
  7. _iocResolver = iocResolver;
  8. }
  9.  
  10. public IReadOnlyList<ValidationResult> Validate(object validatingObject)
  11. {
  12. var validationErrors = new List<ValidationResult>();
  13.  
  14. if (validatingObject is ICustomValidate customValidateObject)
  15. {
  16. var context = new CustomValidationContext(validationErrors, _iocResolver);
  17. customValidateObject.AddValidationErrors(context);
  18. }
  19.  
  20. return validationErrors;
  21. }
  22. }

  这个Validator需要你待验证的Dto需要继承自ICustomValidate这个接口,这个接口中也定义了一个AddValidationErrors(CustomValidationContext context)方法,我们来看看这个接口中的定义。

  1. public class CustomValidator : IMethodParameterValidator
  2. {
  3. private readonly IIocResolver _iocResolver;
  4.  
  5. public CustomValidator(IIocResolver iocResolver)
  6. {
  7. _iocResolver = iocResolver;
  8. }
  9.  
  10. public IReadOnlyList<ValidationResult> Validate(object validatingObject)
  11. {
  12. var validationErrors = new List<ValidationResult>();
  13.  
  14. if (validatingObject is ICustomValidate customValidateObject)
  15. {
  16. var context = new CustomValidationContext(validationErrors, _iocResolver);
  17. customValidateObject.AddValidationErrors(context);
  18. }
  19.  
  20. return validationErrors;
  21. }
  22. }

  ICustomValidate接口定义。

  1. public interface ICustomValidate
  2. {
  3. /// <summary>
  4. /// This method is used to validate the object.
  5. /// </summary>
  6. /// <param name="context">Validation context.</param>
  7. void AddValidationErrors(CustomValidationContext context);
  8. }

  那么我们会发现这个接口和上面IValidatableObject这个接口有明显的不同,那么具体体现在用法上有什么不同呢?这个也要我们用上面同样的例子来进行说明。

  1. public class SelfAddDto:Entity<int>,IHasPerson,ICustomValidate
  2. {
  3. public long UserId { get; set; }
  4. public string UserName { get; set; }
  5.  
  6. public string Country { get; set; }
  7.  
  8. public string Province { get; set; }
  9.  
  10. public string City { get; set; }
  11.  
  12. public void AddValidationErrors(CustomValidationContext context)
  13. {
  14. if (string.IsNullOrWhiteSpace(UserName))
  15. {
  16. context.Results.Add(new ValidationResult("当前输入的参数用户名不能为空"));
  17. }
  18. }
  19. }

  通过ValidatableObjectValidator中相同的例子来进行验证的话会得到相同的效果,这里的CustomerValidator完全是ABP中自定义的一个验证器,同时也是对ValidatableObjectValidator的一个有效的补充,通过这几个例子应该是能够加强你对整个ABP系统中的验证的机制有一个更加清楚的认识,如果对于本篇还有些不太理解的最好先读上篇从而对整个ABP中的Validation过程有一个基础的理解,本篇就到这里了。

  最后,点击这里返回整个ABP系列的主目录。

ABP中的拦截器之ValidationInterceptor(下)的更多相关文章

  1. ABP中的拦截器之ValidationInterceptor(上)

    从今天这一节起就要深入到ABP中的每一个重要的知识点来一步步进行分析,在进行介绍ABP中的拦截器之前我们先要有个概念,到底什么是拦截器,在介绍这些之前,我们必须要了解AOP编程思想,这个一般翻译是面向 ...

  2. ABP中的拦截器之AuditingInterceptor

    在上面两篇介绍了ABP中的ValidationInterceptor之后,我们今天来看看ABP中定义的另外一种Interceptor即为AuditingInterceptor,顾名思义就是一种审计相关 ...

  3. ABP中的拦截器之EntityHistoryInterceptor

    今天我们接着之前的系列接着来写另外一种拦截器EntityHistoryInterceptor,这个拦截器到底是做什么的呢?这个从字面上理解是实体历史?这个到底是什么意思?带着这个问题我们来一步步去分析 ...

  4. ABP中的本地化处理(下)

    在上篇文章中我们的重点是讲述怎样通过在Domain层通过PreInitialize()配置ILocalizationConfiguration中的Sources(IList<ILocalizat ...

  5. 6. abp中的拦截器

    abp拦截器基本定义 拦截器接口定义: public interface IAbpInterceptor { void Intercept(IAbpMethodInvocation invocatio ...

  6. ABP拦截器之UnitOfWorkRegistrar(一)

    ABP中UnitOfWorkRegistrar拦截器是整个ABP中非常关键的一个部分,这个部分在整个业务系统中也是用的最多的一个部分,这篇文章的主要思路并不是写如何使用ABP中的UnitOfWork, ...

  7. ABP中的Filter(下)

    接着上面的一个部分来叙述,这一篇我们来重点看ABP中的AbpUowActionFilter.AbpExceptionFilter.AbpResultFilter这三个部分也是按照之前的思路来一个个介绍 ...

  8. ABP拦截器之AuthorizationInterceptor

    在整体介绍这个部分之前,如果对ABP中的权限控制还没有一个很明确的认知,请先阅读这篇文章,然后在读下面的内容. AuthorizationInterceptor看这个名字我们就知道这个拦截器拦截用户一 ...

  9. ABP拦截器之UnitOfWorkRegistrar(二)

    在上面一篇中我们主要是了解了在ABP系统中是如何使用UnitOfWork以及整个ABP系统中如何执行这些过程的,那么这一篇就让我们来看看UnitOfWorkManager中在执行Begin和Compl ...

随机推荐

  1. Xhprof分析php性能

    https://windows.php.net/downloads/pecl/releases/xhprof/0.10.6/ 下载Xhprof版本 配置一个本地访问url,指向index.php,能访 ...

  2. 对HTML5标签的认识(二)

    ---恢复内容开始--- 这次随笔主要讲一下列表标签.链接标签.和表格标签.图像标签.音频标签.及视频标签的运用及作用. 一.<ul>和<ol> 首先先了解一下<ul&g ...

  3. IIS中虚拟目录不继承主站点web.config设置的办法(转载)

    ASP.NET提供了强大的Web.config来配置网站,一般来说一个网站只有一个根目录下的Web.config文件,有时候我们希望子目录有着不同的权限或者参数设置,则可以在相应子目录增加一个Web. ...

  4. 委托(3).net 2.0中的委托

    由于.net 2.0引入了匿名方法,所以创建委托的方式可以更简化了. .net 2.0中的委托 有了匿名方法,上一篇的例子可以简化为: namespace DelegateDemo { //声明委托 ...

  5. java开发环境配置——IntelliJ IDEA

    关于开发工具,之前是用eclipse,后来用了一段时间idea后,发现idea比eclipse好用太多了,所以推荐大家用idea 官网下载地址:https://www.jetbrains.com/id ...

  6. 上传漏洞总结-upload-labs

    介绍: Upload-labs是一个所有类型的上传漏洞的靶场 项目地址:https://github.com/c0ny1/upload-labs 思维导图: 小试牛刀: Pass-01 客户端js检查 ...

  7. Java新知识系列 八

    什么是死锁,死锁的原因和必要条件:       []什么是死锁,死锁的原因和必要条件: 死锁:死锁的原因在于进程在等待其它进程占有的某些资源,而自身的资源又被其它进程等待着,造成了死循环. 出现死锁的 ...

  8. asp.net core 2.1 部署 centos7

    asp.net core 2.1 部署 centos7 Kestrel 非常适合从 ASP.NET Core 提供动态内容. 但是,Web 服务功能不像服务器(如 IIS.Apache 或 Nginx ...

  9. office2019下载以及激活密钥(亲测可用)

    office2019激活密钥 W8W6K-3N7KK-PXB9H-8TD8W-BWTH9  或者: 链接:https://pan.baidu.com/s/1Ch0rc2ZN9I_lwmbjGESTuw ...

  10. 【转载】FPGA算法设计随笔

    FPGA设计算法依次需要完成MATLAB浮点仿真 MATLAB定点仿真 verilogHDL定点运算以及数据对比的流程.其中浮点到定点的转换尤为重要,需要在数据表示范围和精度之间做出权衡.另外掌握定点 ...