Validate
拦截器验证
ObjectValidationContext 验证上下文,持有验证的结果
public class ObjectValidationContext
{
[NotNull]
public object ValidatingObject { get; }
public List<ValidationResult> Errors { get; }
public ObjectValidationContext([NotNull] object validatingObject)
{
ValidatingObject = Check.NotNull(validatingObject, nameof(validatingObject));
Errors = new List<ValidationResult>();
}
}
AbpValidationOptions 验证配置项
public class AbpValidationOptions
{
public List<Type> IgnoredTypes { get; }
public ITypeList<IObjectValidationContributor>
ObjectValidationContributors { get; set; }
public AbpValidationOptions()
{
IgnoredTypes = new List<Type>();
ObjectValidationContributors =
new TypeList<IObjectValidationContributor>();
}
}
IObjectValidator 验证类
namespace Volo.Abp.Validation;
public interface IObjectValidator
{
Task ValidateAsync(
object validatingObject,
string? name = null,
bool allowNull = false
);
Task<List<ValidationResult>> GetErrorsAsync(
object validatingObject,
string? name = null,
bool allowNull = false
);
}
获取所有的 IObjectValidationContributor,用它们去验证
public class ObjectValidator : IObjectValidator, ITransientDependency
{
protected IServiceScopeFactory ServiceScopeFactory { get; }
protected AbpValidationOptions Options { get; }
public ObjectValidator(IOptions<AbpValidationOptions> options,
IServiceScopeFactory serviceScopeFactory)
{
ServiceScopeFactory = serviceScopeFactory;
Options = options.Value;
}
public virtual async Task ValidateAsync(object validatingObject,
string? name = null, bool allowNull = false)
{
var errors = await GetErrorsAsync(validatingObject, name, allowNull);
if (errors.Any())
{
throw new AbpValidationException(
"Object state is not valid! See ValidationErrors for details.",
errors
);
}
}
public virtual async Task<List<ValidationResult>> GetErrorsAsync(
object validatingObject, string? name = null, bool allowNull = false)
{
if (validatingObject == null)
{
if (allowNull)
{
return new List<ValidationResult>();
//TODO: Returning an array would be more performent
}
else
{
return new List<ValidationResult>
{
name == null
? new ValidationResult("Given object is null!")
: new ValidationResult(name + " is null!", new[] {name})
};
}
}
var context = new ObjectValidationContext(validatingObject);
//获取所有的 IObjectValidationContributor,用它们去验证
using (var scope = ServiceScopeFactory.CreateScope())
{
foreach (var contributorType in Options.ObjectValidationContributors)
{
var contributor = (IObjectValidationContributor)
scope.ServiceProvider.GetRequiredService(contributorType);
await contributor.AddErrorsAsync(context);
}
}
return context.Errors;
}
}
IObjectValidationContributor
DataAnnotationObjectValidationContributor 通过数据注解验证
public class DataAnnotationObjectValidationContributor :
IObjectValidationContributor, ITransientDependency
{
public const int MaxRecursiveParameterValidationDepth = 8;
protected IServiceProvider ServiceProvider { get; }
protected AbpValidationOptions Options { get; }
public DataAnnotationObjectValidationContributor(
IOptions<AbpValidationOptions> options,
IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
Options = options.Value;
}
//递归验证模型的属性
public Task AddErrorsAsync(ObjectValidationContext context)
{
ValidateObjectRecursively(context.Errors,
context.ValidatingObject, currentDepth: 1);
return Task.CompletedTask;
}
//因为模型可能套模型,所有需要递归处理,处理好嵌套的深度就可以
protected virtual void ValidateObjectRecursively(
List<ValidationResult> errors, object? validatingObject, int currentDepth)
{
//退出递归
if (currentDepth > MaxRecursiveParameterValidationDepth)
{
return;
}
//退出递归
if (validatingObject == null)
{
return;
}
//验证模型属性,验证的错误加入 errors
AddErrors(errors, validatingObject);
//Validate items of enumerable
if (validatingObject is IEnumerable enumerable)
{
if (!(enumerable is IQueryable))
{
foreach (var item in enumerable)
{
// Do not recursively validate for primitive objects
// 简单类型退出递归
if (item == null || TypeHelper.IsPrimitiveExtended(item.GetType()))
{
break;
}
//继续递归
ValidateObjectRecursively(errors, item, currentDepth + 1);
}
}
return;
}
var validatingObjectType = validatingObject.GetType();
// Do not recursively validate for primitive objects
//简单类型退出递归
if (TypeHelper.IsPrimitiveExtended(validatingObjectType))
{
return;
}
// 不需要验证的也退出递归
if (Options.IgnoredTypes.Any(t => t.IsInstanceOfType(validatingObject)))
{
return;
}
//验证属性
var properties = TypeDescriptor.GetProperties(validatingObject).
Cast<PropertyDescriptor>();
foreach (var property in properties)
{
if (property.Attributes.OfType<DisableValidationAttribute>().Any())
{
continue;
}
//递归
ValidateObjectRecursively(errors, property.GetValue(
validatingObject), currentDepth + 1);
}
}
//验证 模型的所有属性及 IValidatableObject 接口
public void AddErrors(List<ValidationResult> errors, object validatingObject)
{
var properties = TypeDescriptor.GetProperties(
validatingObject).Cast<PropertyDescriptor>();
//对验证对象的属性
foreach (var property in properties)
{
AddPropertyErrors(validatingObject, property, errors);
}
// IValidatableObject 验证模型实现的 IValidatableObject 验证
if (validatingObject is IValidatableObject validatableObject)
{
errors.AddRange(
validatableObject.Validate(
new ValidationContext(validatableObject, ServiceProvider, null))
);
}
}
//属性验证
protected virtual void AddPropertyErrors(
object validatingObject, PropertyDescriptor property, List<ValidationResult> errors)
{
var validationAttributes = property.Attributes.OfType<ValidationAttribute>().ToArray();
if (validationAttributes.IsNullOrEmpty())
{
return;
}
//生成验证上下文,传递给 IAttributeValidationResultProvider 用于验证
var validationContext = new ValidationContext(validatingObject, ServiceProvider, null)
{
DisplayName = property.DisplayName,
MemberName = property.Name
};
var attributeValidationResultProvider = ServiceProvider.
GetRequiredService<IAttributeValidationResultProvider>();
foreach (var attribute in validationAttributes)
{
//通过 IAttributeValidationResultProvider 进行验证
var result = attributeValidationResultProvider.
GetOrDefault(attribute, property.GetValue(validatingObject), validationContext);
if (result != null)
{
errors.Add(result);
}
}
}
}
AbpMvcAttributeValidationResultProvider 数据注解验证的提供者
AbpMvcAttributeValidationResultProvider 实现了注解验证的多语言
namespace Volo.Abp.Validation;
public class DefaultAttributeValidationResultProvider :
IAttributeValidationResultProvider, ITransientDependency
{
public virtual ValidationResult? GetOrDefault(
ValidationAttribute validationAttribute, object?
validatingObject, ValidationContext validationContext)
{
return validationAttribute.GetValidationResult(validatingObject, validationContext);
}
}
// Volo.Abp.AspNetCore.Mvc 模块里定义
// 注入的类型进行替换为 AbpMvcAttributeValidationResultProvider
namespace Volo.Abp.AspNetCore.Mvc.Localization;
[Dependency(ReplaceServices = true)]
public class AbpMvcAttributeValidationResultProvider :
DefaultAttributeValidationResultProvider
{
private readonly AbpMvcDataAnnotationsLocalizationOptions
_abpMvcDataAnnotationsLocalizationOptions;
private readonly IStringLocalizerFactory
_stringLocalizerFactory;
public AbpMvcAttributeValidationResultProvider(
IOptions<AbpMvcDataAnnotationsLocalizationOptions>
abpMvcDataAnnotationsLocalizationOptions,
IStringLocalizerFactory stringLocalizerFactory)
{
_abpMvcDataAnnotationsLocalizationOptions =
abpMvcDataAnnotationsLocalizationOptions.Value;
_stringLocalizerFactory = stringLocalizerFactory;
}
public override ValidationResult? GetOrDefault(ValidationAttribute validationAttribute,
object? validatingObject, ValidationContext validationContext)
{
var resourceSource = _abpMvcDataAnnotationsLocalizationOptions.AssemblyResources.
GetOrDefault(validationContext.ObjectType.Assembly);
if (resourceSource == null)
{
return base.GetOrDefault(validationAttribute, validatingObject, validationContext);
}
if (validationAttribute.ErrorMessage == null)
{
ValidationAttributeHelper.SetDefaultErrorMessage(validationAttribute);
}
if (validationAttribute.ErrorMessage != null)
{
//本地化错误消息
validationAttribute.ErrorMessage = _stringLocalizerFactory.Create(
resourceSource)[validationAttribute.ErrorMessage];
}
return base.GetOrDefault(validationAttribute, validatingObject, validationContext);
}
}
AbpMvcDataAnnotationsLocalizationOptions
namespace Volo.Abp.AspNetCore.Mvc.Localization;
public class AbpMvcDataAnnotationsLocalizationOptions
{
public IDictionary<Assembly, Type> AssemblyResources { get; }
public AbpMvcDataAnnotationsLocalizationOptions()
{
AssemblyResources = new Dictionary<Assembly, Type>();
}
//多个程序集共享资源
public void AddAssemblyResource(
[NotNull] Type resourceType,
params Assembly[] assemblies)
{
if (assemblies.IsNullOrEmpty())
{
assemblies = new[] { resourceType.Assembly };
}
foreach (var assembly in assemblies)
{
AssemblyResources[assembly] = resourceType;
}
}
}
FluentObjectValidationContributor 通过 fluent 验证
namespace Volo.Abp.FluentValidation;
public class FluentObjectValidationContributor : IObjectValidationContributor,
ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public FluentObjectValidationContributor(
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public virtual async Task AddErrorsAsync(ObjectValidationContext context)
{
//首先确保要注入 fluent IValidator 的实现者
var serviceType = typeof(IValidator<>).MakeGenericType(
context.ValidatingObject.GetType());
var validator = _serviceProvider.GetService(serviceType) as IValidator;
if (validator == null)
{
return;
}
//进行验证
var result = await validator.ValidateAsync((IValidationContext)Activator.
CreateInstance(
typeof(ValidationContext<>).
MakeGenericType(context.ValidatingObject.GetType()),
context.ValidatingObject)!);
if (!result.IsValid)
{
context.Errors.AddRange(
result.Errors.Select(
error =>
new ValidationResult(error.ErrorMessage, new[] { error.PropertyName })
)
);
}
}
}
Volo.Abp.FluentValidation 需要实现自动注入 IValidator<> 实现
namespace Volo.Abp.FluentValidation;
public class AbpFluentValidationConventionalRegistrar : DefaultConventionalRegistrar
{
//获取能被注入的 IValidator<> 实现
protected override bool IsConventionalRegistrationDisabled(Type type)
{
return !type.GetInterfaces().Any(x => x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(IValidator<>)) ||
base.IsConventionalRegistrationDisabled(type);
}
//指定生命周期
protected override ServiceLifetime? GetDefaultLifeTimeOrNull(Type type)
{
return ServiceLifetime.Transient;
}
//导出服务类型
protected override List<Type> GetExposedServiceTypes(Type type)
{
return new List<Type>()
{
type,
typeof(IValidator<>).MakeGenericType(GetFirstGenericArgumentOrNull(type, 1)!)
};
}
//获取 GenericArgument 参数
private static Type? GetFirstGenericArgumentOrNull(Type type, int depth)
{
const int maxFindDepth = 8;
if (depth >= maxFindDepth)
{
return null;
}
if (type.IsGenericType && type.GetGenericArguments().Length >= 1)
{
return type.GetGenericArguments()[0];
}
//因为是单继承 AbstractValidator ,所以这里可以获取到,但是感觉这种写法不完善
return GetFirstGenericArgumentOrNull(type.BaseType!, depth + 1);
}
}
namespace Volo.Abp.FluentValidation;
[DependsOn(
typeof(AbpValidationModule)
)]
public class AbpFluentValidationModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
//加入约定注入
context.Services.AddConventionalRegistrar(
new AbpFluentValidationConventionalRegistrar());
}
}
ValidationInterceptor 拦截器里使用 IObjectValidator 进行自动验证
public class ValidationInterceptor : AbpInterceptor, ITransientDependency
{
private readonly IMethodInvocationValidator _methodInvocationValidator;
public ValidationInterceptor(IMethodInvocationValidator methodInvocationValidator)
{
_methodInvocationValidator = methodInvocationValidator;
}
public override async Task InterceptAsync(IAbpMethodInvocation invocation)
{
//模型验证
await ValidateAsync(invocation);
await invocation.ProceedAsync();
}
protected virtual async Task ValidateAsync(IAbpMethodInvocation invocation)
{
await _methodInvocationValidator.ValidateAsync(
//验证验证的对象,对象方法及参数
new MethodInvocationValidationContext(
invocation.TargetObject,
//methodInfo
invocation.Method,
// object[] 方法所有的参数
invocation.Arguments
)
);
}
}
public interface IMethodInvocationValidator
{
Task ValidateAsync(MethodInvocationValidationContext context);
}
// 核心验证还是通过 IObjectValidator 验证
public class MethodInvocationValidator : IMethodInvocationValidator, ITransientDependency
{
private readonly IObjectValidator _objectValidator;
//注入验证的核心服务 IObjectValidator
public MethodInvocationValidator(IObjectValidator objectValidator)
{
_objectValidator = objectValidator;
}
public virtual async Task ValidateAsync(MethodInvocationValidationContext context)
{
Check.NotNull(context, nameof(context));
//没有参数
if (context.Parameters.IsNullOrEmpty())
{
return;
}
//不能不是 public
if (!context.Method.IsPublic)
{
return;
}
//禁止验证
if (IsValidationDisabled(context))
{
return;
}
//参数概述不匹配
if (context.Parameters.Length != context.ParameterValues.Length)
{
throw new Exception(
"Method parameter count does not match with argument count!");
}
//todo: consider to remove this condition
if (context.Errors.Any() && HasSingleNullArgument(context))
{
ThrowValidationError(context);
}
await AddMethodParameterValidationErrorsAsync(context);
if (context.Errors.Any())
{
ThrowValidationError(context);
}
}
//虽然方法被拦截器拦截了但是,可以选择通过验证
// EnableValidationAttribute DisableValidationAttribute 通过这俩个特性进行判断
protected virtual bool IsValidationDisabled(MethodInvocationValidationContext context)
{
if (context.Method.IsDefined(typeof(EnableValidationAttribute), true))
{
return false;
}
if (ReflectionHelper.
GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableValidationAttribute>(
context.Method) != null)
{
return true;
}
return false;
}
protected virtual bool HasSingleNullArgument(MethodInvocationValidationContext context)
{
return context.Parameters.Length == 1 && context.ParameterValues[0] == null;
}
protected virtual void ThrowValidationError(MethodInvocationValidationContext context)
{
throw new AbpValidationException(
"Method arguments are not valid! See ValidationErrors for details.",
context.Errors
);
}
//每个参数进行验证
protected virtual async Task AddMethodParameterValidationErrorsAsync(
MethodInvocationValidationContext context)
{
for (var i = 0; i < context.Parameters.Length; i++)
{
await AddMethodParameterValidationErrorsAsync(
context, context.Parameters[i], context.ParameterValues[i]);
}
}
//对参数值进行验证
protected virtual async Task AddMethodParameterValidationErrorsAsync(
IAbpValidationResult context, ParameterInfo parameterInfo, object parameterValue)
{
//判断值是否为null
var allowNulls = parameterInfo.IsOptional ||
parameterInfo.IsOut ||
TypeHelper.IsPrimitiveExtended(
parameterInfo.ParameterType, includeEnums: true);
//进行验证
context.Errors.AddRange(
await _objectValidator.GetErrorsAsync(
parameterValue,
parameterInfo.Name,
allowNulls
)
);
}
}
IValidationEnabled 需要使用 abp 拦截器进行方法参数模型进行验证的类需要继承的接口
public interface IValidationEnabled
{
}
ValidationInterceptorRegistrar 拦截器使用的范围
namespace Volo.Abp.Validation;
public static class ValidationInterceptorRegistrar
{
public static void RegisterIfNeeded(IOnServiceRegistredContext context)
{
if (ShouldIntercept(context.ImplementationType))
{
context.Interceptors.TryAdd<ValidationInterceptor>();
}
}
private static bool ShouldIntercept(Type type)
{
return !DynamicProxyIgnoreTypes.Contains(type) &&
typeof(IValidationEnabled).IsAssignableFrom(type);
}
}
AbpValidationModule
namespace Volo.Abp.Validation;
[DependsOn(
typeof(AbpValidationAbstractionsModule),
typeof(AbpLocalizationModule)
)]
public class AbpValidationModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
//加入拦截器
context.Services.OnRegistered(ValidationInterceptorRegistrar.RegisterIfNeeded);
//加入验证的 Contributors,AbpValidationOptions
AutoAddObjectValidationContributors(context.Services);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
// abp 默认提供的这对资源感觉用不上,名字难以记忆
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpValidationResource>();
});
//验证的资源
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<AbpValidationResource>("en")
.AddVirtualJson("/Volo/Abp/Validation/Localization");
});
}
private static void AutoAddObjectValidationContributors(IServiceCollection services)
{
var contributorTypes = new List<Type>();
services.OnRegistered(context =>
{
if (typeof(IObjectValidationContributor).
IsAssignableFrom(context.ImplementationType))
{
contributorTypes.Add(context.ImplementationType);
}
});
services.Configure<AbpValidationOptions>(options =>
{
options.ObjectValidationContributors.AddIfNotContains(contributorTypes);
});
}
}
过滤器验证 基于数据注解
实现微软的过滤器 IAsyncActionFilter
namespace Volo.Abp.AspNetCore.Mvc.Validation;
public class AbpValidationActionFilter : IAsyncActionFilter, ITransientDependency
{
public async Task OnActionExecutionAsync(
ActionExecutingContext context, ActionExecutionDelegate next)
{
if (!context.ActionDescriptor.IsControllerAction() ||
!context.ActionDescriptor.HasObjectResult())
{
await next();
return;
}
if (!context.GetRequiredService<IOptions<AbpAspNetCoreMvcOptions>>().
Value.AutoModelValidation)
{
await next();
return;
}
if (ReflectionHelper.
GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableValidationAttribute>
(context.ActionDescriptor.GetMethodInfo()) != null)
{
await next();
return;
}
if (ReflectionHelper.
GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableValidationAttribute>(
context.Controller.GetType()) != null)
{
await next();
return;
}
if (context.ActionDescriptor.GetMethodInfo().DeclaringType !=
context.Controller.GetType())
{
var baseMethod = context.ActionDescriptor.GetMethodInfo();
var overrideMethod = context.Controller.GetType().GetMethods().
FirstOrDefault(x =>
x.DeclaringType == context.Controller.GetType() &&
x.Name == baseMethod.Name &&
x.ReturnType == baseMethod.ReturnType &&
x.GetParameters().Select(p => p.ToString()).SequenceEqual(
baseMethod.GetParameters().Select(p => p.ToString())));
if (overrideMethod != null)
{
if (ReflectionHelper.
GetSingleAttributeOfMemberOrDeclaringTypeOrDefault
<DisableValidationAttribute>(overrideMethod) != null)
{
await next();
return;
}
}
}
context.GetRequiredService<IModelStateValidator>().Validate(context.ModelState);
await next();
}
}
IAbpValidationResult 验证结果处理
验证结果
public interface IAbpValidationResult
{
List<ValidationResult> Errors { get; }
}
验证异常
public class AbpValidationException : AbpException,
IHasLogLevel,
IHasValidationErrors,
IExceptionWithSelfLogging
{
public IList<ValidationResult> ValidationErrors { get; }
public LogLevel LogLevel { get; set; }
public AbpValidationException()
{
ValidationErrors = new List<ValidationResult>();
LogLevel = LogLevel.Warning;
}
//...
}
👍🎉🎊