ABP中的拦截器之ValidationInterceptor(下)
在上篇我分析了整个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(下)的更多相关文章
- ABP中的拦截器之ValidationInterceptor(上)
从今天这一节起就要深入到ABP中的每一个重要的知识点来一步步进行分析,在进行介绍ABP中的拦截器之前我们先要有个概念,到底什么是拦截器,在介绍这些之前,我们必须要了解AOP编程思想,这个一般翻译是面向 ...
- ABP中的拦截器之AuditingInterceptor
在上面两篇介绍了ABP中的ValidationInterceptor之后,我们今天来看看ABP中定义的另外一种Interceptor即为AuditingInterceptor,顾名思义就是一种审计相关 ...
- ABP中的拦截器之EntityHistoryInterceptor
今天我们接着之前的系列接着来写另外一种拦截器EntityHistoryInterceptor,这个拦截器到底是做什么的呢?这个从字面上理解是实体历史?这个到底是什么意思?带着这个问题我们来一步步去分析 ...
- ABP中的本地化处理(下)
在上篇文章中我们的重点是讲述怎样通过在Domain层通过PreInitialize()配置ILocalizationConfiguration中的Sources(IList<ILocalizat ...
- 6. abp中的拦截器
abp拦截器基本定义 拦截器接口定义: public interface IAbpInterceptor { void Intercept(IAbpMethodInvocation invocatio ...
- ABP拦截器之UnitOfWorkRegistrar(一)
ABP中UnitOfWorkRegistrar拦截器是整个ABP中非常关键的一个部分,这个部分在整个业务系统中也是用的最多的一个部分,这篇文章的主要思路并不是写如何使用ABP中的UnitOfWork, ...
- ABP中的Filter(下)
接着上面的一个部分来叙述,这一篇我们来重点看ABP中的AbpUowActionFilter.AbpExceptionFilter.AbpResultFilter这三个部分也是按照之前的思路来一个个介绍 ...
- ABP拦截器之AuthorizationInterceptor
在整体介绍这个部分之前,如果对ABP中的权限控制还没有一个很明确的认知,请先阅读这篇文章,然后在读下面的内容. AuthorizationInterceptor看这个名字我们就知道这个拦截器拦截用户一 ...
- ABP拦截器之UnitOfWorkRegistrar(二)
在上面一篇中我们主要是了解了在ABP系统中是如何使用UnitOfWork以及整个ABP系统中如何执行这些过程的,那么这一篇就让我们来看看UnitOfWorkManager中在执行Begin和Compl ...
随机推荐
- SpringBoot 整合 apollo
简介 Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性,适用于微服务配置管理场景 ...
- php设计模式--简单介绍
鉴于最近有几个小伙伴总问一些设计模式相关的东西,本人借鉴了一些东西,准备将常见的几种php的设计模式总结整理出来. 平时我们用到的设计模式很多,建议大家多多读一些php开源框架,当深入的阅读了一些ph ...
- C++系列总结——mutable关键字
介绍 mutable的中文意思是易变的,是C++的一个关键字.它的作用就是允许修改被const修饰的对象的成员变量. 常用场景 什么情况下我们会使用到mutable? 一般我们会用const修饰get ...
- CSS像素、物理像素、逻辑像素、设备像素比、PPI、Viewport
1.PX(CSS pixels) 1.1 定义 虚拟像素,可以理解为“直觉”像素,CSS和JS使用的抽象单位,浏览器内的一切长度都是以CSS像素为单位的,CSS像素的单位是px. 1.2 注意 在CS ...
- 谷歌浏览器扩展程序manifest.json参数详解
{ // Required "manifest_version": 2, // manifest编写规范版本,目前主流2 "name": "My Ex ...
- iOS----------取数据的两种取法
NSMutableArray * dataArray =[responseDictionary valueForKeyPath:@"data.list_dic.list"]; NS ...
- 设置抓包工具Fiddler的host
大家都知道在电脑本地上有一个Host文件. Hosts文件主要作用是定义IP地址和主机名的映射关系,是一个映射IP地址和主机名的规定.可以用文本文件打开!当用户在浏览器中输入一个需要登录的网址时,系统 ...
- Git在商业项目中的使用流程
一 引言 这一篇文章还是记录我在杭州工作的总结. 我刚来公司的时候,对Git的使用很头痛,因为在学校里面很少用这个东西,即使用,一般也只有一个分支,不会出现代码冲突和代码合并的情况.但是公司里面一个项 ...
- IE8环境下的上传图片预览
今天做一个需要在IE浏览器上使用的信息录入项目,遇到了图片上传预览的问题,找了一些资料,最后使用了IE自带的滤镜做到了 <!--HTML IE8不支持opacity,只能使用双层,一层背景半透明 ...
- PostgreSql 查询表结构和说明
select (select relname from pg_class where oid=a.attrelid) relname , () as comment from pg_class whe ...