在上篇我分析了整个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. "PECS原则"几篇好文章

    <? extends T>和<? super T>Java 泛型中的PECS原则(copy源码样例) ​

  2. 轻松理解 Java HashMap 和 ConcurrentHashMap

    前言 Map 这样的 Key Value 在软件开发中是非常经典的结构,常用于在内存中存放数据. 本篇主要想讨论 ConcurrentHashMap 这样一个并发容器,在正式开始之前我觉得有必要谈谈 ...

  3. Cesium 之简介以及离线部署运行篇

    前言 cesium 官网的api文档介绍地址cesium官网api,里面详细的介绍 cesium 各个类的介绍,还有就是在线例子:cesium 官网在线例子,这个也是学习 cesium 的好素材. C ...

  4. Android WebView的HTML中的select标签不起作用

    Android WebView的HTML中的select标签不起作用 经过查询资料,了解到android对html里的select标签是弹出一个原生的选择器. 问题: Webview中的select没 ...

  5. 中文乱码之《字符编码:ASCII,Unicode 和 UTF-8》

    参考文献:字符编码笔记:ASCII,Unicode 和 UTF-8 一.ASCII 码 我们知道,计算机内部,所有信息最终都是一个二进制值.每一个二进制位(bit)有0和1两种状态,因此八个二进制位就 ...

  6. 电脑一键U盘启动快捷键

    下面是我特意列出的品牌电脑.笔记本电脑.组装电脑一键U盘启动快捷键对应列表,仅供大家查阅参考! [品牌-笔记本电脑] 笔记本品牌  启动按键 联想笔记本  F12 宏基笔记本  F12 华硕笔记本   ...

  7. [20190401]隐含参数_mutex_spin_count.txt

    [20190401]隐含参数_mutex_spin_count.txt --//上午做了一些测试关于semtimedop函数调用,发现自己上个星期在一些问题上理解错误.--//相关链接:--//htt ...

  8. python正则表达式模块re

    正则表达式的特殊元素 匹配符号 描述 '.'(点dot) 在默认模式下,它匹配除换行符之外的任何字符.如果指定了DOTALL标志,则匹配包括换行符在内的任何字符 '^'(Caret) 匹配以字符串开头 ...

  9. lcd参数解释及刷新率计算,LCD时序

    一.LCD显示图像的过程如下: 其中,VSYNC和HSYNC是有宽度的,加上后如下: 参数解释: HBP(Horizontal Back Porch)水平后沿:在每行或每列的象素数据开始输出时要插入的 ...

  10. Swift代理的使用

    Swift代理的使用 协议规定了用来实现某一特定功能所必需的方法和属性. 任意能够满足协议要求的类型被称为遵循(conform)这个协议. 类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议 ...