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 ...
随机推荐
- Linux 查找最大文件
1.使用命令df -h 查看硬盘空间 2.使用命令du -h –max-depth=1 /var/log/* 查看/var/log路径下文件的大小 du -sh /* 查看哪个目录最大,一步一步的查 ...
- 通过多线程处理提高Redis性能
Redis通常被称为单进程单线程模型. 这不是真的! Redis还运行多个后端线程来执行后端清理工作,例如清理脏数据和关闭文件描述符.在Redis中,主线程负责主要任务,包括但不限于:接收来自客户端的 ...
- 为你揭秘知乎是如何搞AI的——窥大厂 | 数智方法论第1期
文章发布于公号[数智物语] (ID:decision_engine),关注公号不错过每一篇干货. 数智物语(公众号ID:decision_engine)出品 策划.编写:卷毛雅各布 「我们相信,在垃圾 ...
- 12月16日广州.NET俱乐部下午4点爬白云山活动
正如我们在<广州.NET微软技术俱乐部与其他技术群的区别>和<广州.NET微软技术俱乐部每周三五晚周日下午爬白云山活动>里面提到的, 我们会在每周三五晚和周日下午爬白云山. ...
- 企业信息化-Excel快速生成系统
企业信息化,主要是指对企业生产运营过程所形成的信息数字化,最终形成了数字资产.大型企业为了节约成本,提高协同工作效率,都会定制ERP.办公OA.流程审批等系统做信息化支撑.但是中小企业精力投入到生成中 ...
- 瓦片切图工具gdal2tiles.py改写为纯c++版本(二)
python这么火,C++/C#的程序员都生存不下去了,为啥还要干把python转写成c++的这种反动的事? 项目需要呗... gdal2tiles.py文件中有两个类是计算全球墨卡托与WGS84两种 ...
- Redis进阶学习笔记
Redis是什么这里不用再说了吧?下面是官方的解释 Redis is an open source (BSD licensed), in-memory data structure store, us ...
- Nodejs 操作 Sql Server
Nodejs 操作 Sql Server Intro 最近项目需要爬取一些数据,数据有加密,前端的js又被混淆了,ajax请求被 hook 了,有些复杂,最后打算使用 puppeteer 来爬取数据. ...
- JIRA笔记(一):安装部署JIRA
(一) 说明 说明JIRA的安装及破解. 操作系统:WIN 10 数据库:Oracle 12C R2(这个版本的jira,atlassian建议的是 12C R1,不过R2也能用,其他版本不清 ...
- MAX
Max的用法1(Min类似) Matlab中max函数在矩阵中求函数大小的实例如下: C = max(A)返回一个数组各不同维中的最大元素.如果A是一个向量,max(A)返回A中的最大元素.如果A是一 ...