MvcFilter
在Volo.Abp.AspNetCore.Mvc 👉 Validation文件夹中
namespace Volo.Abp.AspNetCore.Mvc.Validation;public class AbpValidationActionFilter : IAsyncActionFilter, ITransientDependency{public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){// 是否是控制器行为// 是否是JsonResult、ObjectResult、NoContentResultd的一种// 不是则跳过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;}// https://docs.microsoft.com/zh-cn/dotnet/api/system.reflection.memberinfo.declaringtype// 该方法的声明是不是上下文中控制器的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;}}}// 调用IModelStateValidator模型状态验证器进行验证context.GetRequiredService<IModelStateValidator>().Validate(context.ModelState);await next();}}
IModelStateValidator的默认实现ModelStateValidator。实现很简单,就是判断是否通过验证,然后将未通过的验证提示信息,添加到验证结果中,然后用验证异常AbpValidationException抛出,交给异常处理器处理。
namespace Volo.Abp.AspNetCore.Mvc.Validation;public class ModelStateValidator : IModelStateValidator, ITransientDependency{public virtual void Validate(ModelStateDictionary modelState){var validationResult = new AbpValidationResult();AddErrors(validationResult, modelState);if (validationResult.Errors.Any()){throw new AbpValidationException("ModelState is not valid! See ValidationErrors for details.",validationResult.Errors);}}public virtual void AddErrors(IAbpValidationResult validationResult, ModelStateDictionary modelState){if (modelState.IsValid){return;}foreach (var state in modelState){foreach (var error in state.Value.Errors){validationResult.Errors.Add(new ValidationResult(error.ErrorMessage, new[] { state.Key }));}}}}
通过AbpMvcOptionsExtensions扩展方法统一将过滤器注入到Mvc过滤器集合中。
internal static class AbpMvcOptionsExtensions{public static void AddAbp(this MvcOptions options, IServiceCollection services){,..// 注入AddActionFilters(options);...}private static void AddActionFilters(MvcOptions options){...// 验证过滤器options.Filters.AddService(typeof(AbpValidationActionFilter));...}...}
拦截器
只要实现了IValidationEnabled接口就将其添加到拦截器中。
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);}}
流程 源码地址
- 拦截器组装
MethodInvocationValidationContext上下文,调用IMethodInvocationValidator.ValidateAsync进行验证。 - 默认实现
MethodInvocationValidator,先进行判断- 参数不能为空
- 修饰符必须是
Public - 未使用
DisableValidationAttribute特性 - 参数数量和参数值数量相等(一对一)
MethodInvocationValidator调用IObjectValidator.GetErrorsAsync()方法获取验证结果- 默认实现
ObjectValidator调用AbpValidationOptions选项中的ObjectValidationContributors属性,该属性提供了IObjectValidationContributor集合,IObjectValidationContributor是验证辅助类型。当前类库中提供了DataAnnotationObjectValidationContributor模型注解验证,就是上面MVC那一套。DataAnnotationObjectValidationContributor里面使用了递归验证,最大深度为8。
DataAnnotationObjectValidationContributor调用了DefaultAttributeValidationResultProvider获取验证结果,默认实现使用了ValidationAttribute.GetValidationResult()- 在MVC中重新用
AbpMvcAttributeValidationResultProvider继承并重写了DefaultAttributeValidationResultProvider的GetOrDefault()方法。用它实现了本地化。 ```csharp namespace Volo.Abp.AspNetCore.Mvc.Localization;
- 在MVC中重新用
[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);}
}
<a name="d0tco"></a>## FluentValidation引入了第三方类库`FluentValidation`,并实现了`IObjectValidationContributor`。```csharpnamespace Volo.Abp.FluentValidation;public class FluentObjectValidationContributor : IObjectValidationContributor, ITransientDependency{private readonly IServiceProvider _serviceProvider;public FluentObjectValidationContributor(IServiceProvider serviceProvider){_serviceProvider = serviceProvider;}// 通过Ioc获取验证器,然后将验证结果添加到上下文的Errors集合中。public virtual async Task AddErrorsAsync(ObjectValidationContext context){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 })));}}}
