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

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

 protected virtual void SetValidationErrors(object validatingObject)
{
foreach (var validatorType in _configuration.Validators)
{
if (ShouldValidateUsingValidator(validatingObject, validatorType))
{
using (var validator = _iocResolver.ResolveAsDisposable<IMethodParameterValidator>(validatorType))
{
var validationResults = validator.Object.Validate(validatingObject);
ValidationErrors.AddRange(validationResults);
}
}
}
}

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

 private void AddMethodParameterValidators()
{
Configuration.Validation.Validators.Add<DataAnnotationsValidator>();
Configuration.Validation.Validators.Add<ValidatableObjectValidator>();
Configuration.Validation.Validators.Add<CustomValidator>();
}

  一  DataAnnotationsValidator

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

 public interface IMethodParameterValidator : ITransientDependency
{
IReadOnlyList<ValidationResult> Validate(object validatingObject);
}

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

 public class DataAnnotationsValidator : IMethodParameterValidator
{
public virtual IReadOnlyList<ValidationResult> Validate(object validatingObject)
{
return GetDataAnnotationAttributeErrors(validatingObject);
} /// <summary>
/// Checks all properties for DataAnnotations attributes.
/// </summary>
protected virtual List<ValidationResult> GetDataAnnotationAttributeErrors(object validatingObject)
{
var validationErrors = new List<ValidationResult>(); var properties = TypeDescriptor.GetProperties(validatingObject).Cast<PropertyDescriptor>();
foreach (var property in properties)
{
var validationAttributes = property.Attributes.OfType<ValidationAttribute>().ToArray();
if (validationAttributes.IsNullOrEmpty())
{
continue;
} var validationContext = new ValidationContext(validatingObject)
{
DisplayName = property.DisplayName,
MemberName = property.Name
}; foreach (var attribute in validationAttributes)
{
var result = attribute.GetValidationResult(property.GetValue(validatingObject), validationContext);
if (result != null)
{
validationErrors.Add(result);
}
}
} return validationErrors;
}
}

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

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

  public class CreateRoleDto
{
[Required]
[StringLength(AbpRoleBase.MaxNameLength)]
public string Name { get; set; } [Required]
[StringLength(AbpRoleBase.MaxDisplayNameLength)]
public string DisplayName { get; set; } public string NormalizedName { get; set; } [StringLength(Role.MaxDescriptionLength)]
public string Description { get; set; } public bool IsStatic { get; set; } public List<string> Permissions { get; set; }
}  

  二  ValidatableObjectValidator

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

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

public class ValidatableObjectValidator : IMethodParameterValidator
{
public virtual IReadOnlyList<ValidationResult> Validate(object validatingObject)
{
var validationErrors = new List<ValidationResult>(); if (validatingObject is IValidatableObject o)
{
validationErrors.AddRange(o.Validate(new ValidationContext(o)));
} return validationErrors;
}
}

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

[AutoMap(typeof(SelfAddedModel))]
public class SelfAddDto:Entity<int>,IHasPerson,IValidatableObject
{
public long UserId { get; set; }
public string UserName { get; set; } public string Country { get; set; } public string Province { get; set; } public string City { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrWhiteSpace(UserName))
{
return new List<ValidationResult>()
{
new ValidationResult("当前输入的参数用户名不能为空")
};
}
return new List<ValidationResult>();
}
}

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

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

  三 CustomerValidator

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

public class CustomValidator : IMethodParameterValidator
{
private readonly IIocResolver _iocResolver; public CustomValidator(IIocResolver iocResolver)
{
_iocResolver = iocResolver;
} public IReadOnlyList<ValidationResult> Validate(object validatingObject)
{
var validationErrors = new List<ValidationResult>(); if (validatingObject is ICustomValidate customValidateObject)
{
var context = new CustomValidationContext(validationErrors, _iocResolver);
customValidateObject.AddValidationErrors(context);
} return validationErrors;
}
}

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

public class CustomValidator : IMethodParameterValidator
{
private readonly IIocResolver _iocResolver; public CustomValidator(IIocResolver iocResolver)
{
_iocResolver = iocResolver;
} public IReadOnlyList<ValidationResult> Validate(object validatingObject)
{
var validationErrors = new List<ValidationResult>(); if (validatingObject is ICustomValidate customValidateObject)
{
var context = new CustomValidationContext(validationErrors, _iocResolver);
customValidateObject.AddValidationErrors(context);
} return validationErrors;
}
}

  ICustomValidate接口定义。

public interface ICustomValidate
{
/// <summary>
/// This method is used to validate the object.
/// </summary>
/// <param name="context">Validation context.</param>
void AddValidationErrors(CustomValidationContext context);
}

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

public class SelfAddDto:Entity<int>,IHasPerson,ICustomValidate
{
public long UserId { get; set; }
public string UserName { get; set; } public string Country { get; set; } public string Province { get; set; } public string City { get; set; } public void AddValidationErrors(CustomValidationContext context)
{
if (string.IsNullOrWhiteSpace(UserName))
{
context.Results.Add(new ValidationResult("当前输入的参数用户名不能为空"));
}
}
}

  通过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. 1.docker常用命令

    1.启动交互式容器 $ docker run -i -t IMAGE /bin/bash -i --interactive=true|false 默认是false -t --tty=true|fals ...

  2. Hibernate框架笔记03表操作多对多配置

    目录 1. 数据库表与表之间的关系 1.1 一对多关系 1.2 多对多关系 1.3 一对一关系[了解] 2. Hibernate的一对多关联映射 2.1 创建一个项目,引入相关jar包 2.2. 创建 ...

  3. 多线程(5)async&await

    .net 4.0的Task已经让我们可以非常简单地使用多线程,并且可以有返回值,也可以支持线程的取消等操作,可谓已经很强大了.但.net 4.5为我们带来了async&await,使得实现多线 ...

  4. 忘记时间戳的存在——Yii2超实用的自动更新时间戳的Behavior(改进版)

    本文改进了Yii2中内置行为类TimestampBehavior,使得时间戳字段(如created_at,updated_at) 完全自己更新,方便得让你忘记它们的存在. Yii2的内置行为类Time ...

  5. ios手机录屏软件哪个好

    苹果手机中的airplay镜像,是苹果手机系统的一大特色,可以轻松把手机屏幕投射电脑,这个功能使苹果手机相较安卓手机投屏会更加轻松,那么如何实现苹果手机投射电脑屏幕?下面小编便来分享ios手机录屏软件 ...

  6. 41.Odoo产品分析 (四) – 工具板块(10) – 问卷(1)

    查看Odoo产品分析系列--目录 在该模块下,可以创建问卷,收集答案,打印统计.  安装"问卷"模块,首页显示当前各个阶段中的问卷:  打开"开发者模式",能对 ...

  7. iOS----------取数据的两种取法

    NSMutableArray * dataArray =[responseDictionary valueForKeyPath:@"data.list_dic.list"]; NS ...

  8. 支持scrollTo的RecycleView

    RecycleView内部没有帮我们实现ScrollTo的方法,不过帮我们实现了ScrollBy,我们可以通过ScrollBy自定义一个支持scrollTo的RecycleView. public c ...

  9. 四、View的工作原理

    1.ViewRoot和DecorView ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完 ...

  10. 【学习笔记】【Javaweb】二、Session对象过期时间三种设置方法、Session失效监听器

    一.前言 本文:https://www.cnblogs.com/Twobox/p/10361712.html 参考:https://www.cnblogs.com/diewufeixian/p/422 ...