MvcFilter

Volo.Abp.AspNetCore.Mvc 👉 Validation文件夹中

  1. namespace Volo.Abp.AspNetCore.Mvc.Validation;
  2. public class AbpValidationActionFilter : IAsyncActionFilter, ITransientDependency
  3. {
  4. public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
  5. {
  6. // 是否是控制器行为
  7. // 是否是JsonResult、ObjectResult、NoContentResultd的一种
  8. // 不是则跳过
  9. if (!context.ActionDescriptor.IsControllerAction() ||
  10. !context.ActionDescriptor.HasObjectResult())
  11. {
  12. await next();
  13. return;
  14. }
  15. // 是否开启验证
  16. if (!context.GetRequiredService<IOptions<AbpAspNetCoreMvcOptions>>().Value.AutoModelValidation)
  17. {
  18. await next();
  19. return;
  20. }
  21. // 该方法是否禁用验证
  22. if (ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableValidationAttribute>(context.ActionDescriptor.GetMethodInfo()) != null)
  23. {
  24. await next();
  25. return;
  26. }
  27. // 该控制器是否禁用验证
  28. if (ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableValidationAttribute>(context.Controller.GetType()) != null)
  29. {
  30. await next();
  31. return;
  32. }
  33. // https://docs.microsoft.com/zh-cn/dotnet/api/system.reflection.memberinfo.declaringtype
  34. // 该方法的声明是不是上下文中控制器的
  35. if (context.ActionDescriptor.GetMethodInfo().DeclaringType != context.Controller.GetType())
  36. {
  37. var baseMethod = context.ActionDescriptor.GetMethodInfo();
  38. // 获取重写前的方法
  39. var overrideMethod = context.Controller.GetType().GetMethods().FirstOrDefault(x =>
  40. x.DeclaringType == context.Controller.GetType() &&
  41. x.Name == baseMethod.Name &&
  42. x.ReturnType == baseMethod.ReturnType &&
  43. x.GetParameters().Select(p => p.ToString()).SequenceEqual(baseMethod.GetParameters().Select(p => p.ToString())));
  44. if (overrideMethod != null)
  45. {
  46. // 该方法是否禁用验证
  47. if (ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableValidationAttribute>(overrideMethod) != null)
  48. {
  49. await next();
  50. return;
  51. }
  52. }
  53. }
  54. // 调用IModelStateValidator模型状态验证器进行验证
  55. context.GetRequiredService<IModelStateValidator>().Validate(context.ModelState);
  56. await next();
  57. }
  58. }

IModelStateValidator的默认实现ModelStateValidator。实现很简单,就是判断是否通过验证,然后将未通过的验证提示信息,添加到验证结果中,然后用验证异常AbpValidationException抛出,交给异常处理器处理。

  1. namespace Volo.Abp.AspNetCore.Mvc.Validation;
  2. public class ModelStateValidator : IModelStateValidator, ITransientDependency
  3. {
  4. public virtual void Validate(ModelStateDictionary modelState)
  5. {
  6. var validationResult = new AbpValidationResult();
  7. AddErrors(validationResult, modelState);
  8. if (validationResult.Errors.Any())
  9. {
  10. throw new AbpValidationException(
  11. "ModelState is not valid! See ValidationErrors for details.",
  12. validationResult.Errors
  13. );
  14. }
  15. }
  16. public virtual void AddErrors(IAbpValidationResult validationResult, ModelStateDictionary modelState)
  17. {
  18. if (modelState.IsValid)
  19. {
  20. return;
  21. }
  22. foreach (var state in modelState)
  23. {
  24. foreach (var error in state.Value.Errors)
  25. {
  26. validationResult.Errors.Add(new ValidationResult(error.ErrorMessage, new[] { state.Key }));
  27. }
  28. }
  29. }
  30. }

通过AbpMvcOptionsExtensions扩展方法统一将过滤器注入到Mvc过滤器集合中。

  1. internal static class AbpMvcOptionsExtensions
  2. {
  3. public static void AddAbp(this MvcOptions options, IServiceCollection services)
  4. {
  5. ,..
  6. // 注入
  7. AddActionFilters(options);
  8. ...
  9. }
  10. private static void AddActionFilters(MvcOptions options)
  11. {
  12. ...
  13. // 验证过滤器
  14. options.Filters.AddService(typeof(AbpValidationActionFilter));
  15. ...
  16. }
  17. ...
  18. }

拦截器

只要实现了IValidationEnabled接口就将其添加到拦截器中。

  1. public static class ValidationInterceptorRegistrar
  2. {
  3. public static void RegisterIfNeeded(IOnServiceRegistredContext context)
  4. {
  5. if (ShouldIntercept(context.ImplementationType))
  6. {
  7. context.Interceptors.TryAdd<ValidationInterceptor>();
  8. }
  9. }
  10. private static bool ShouldIntercept(Type type)
  11. {
  12. return !DynamicProxyIgnoreTypes.Contains(type) && typeof(IValidationEnabled).IsAssignableFrom(type);
  13. }
  14. }

流程 源码地址

  1. 拦截器组装MethodInvocationValidationContext上下文,调用IMethodInvocationValidator.ValidateAsync进行验证。
  2. 默认实现MethodInvocationValidator,先进行判断
    1. 参数不能为空
    2. 修饰符必须是Public
    3. 未使用DisableValidationAttribute特性
    4. 参数数量和参数值数量相等(一对一)
  3. MethodInvocationValidator调用IObjectValidator.GetErrorsAsync()方法获取验证结果
  4. 默认实现ObjectValidator调用AbpValidationOptions选项中的ObjectValidationContributors属性,该属性提供了IObjectValidationContributor集合,IObjectValidationContributor是验证辅助类型。当前类库中提供了DataAnnotationObjectValidationContributor模型注解验证,就是上面MVC那一套。
    1. DataAnnotationObjectValidationContributor里面使用了递归验证,最大深度为8。
  5. DataAnnotationObjectValidationContributor调用了DefaultAttributeValidationResultProvider获取验证结果,默认实现使用了ValidationAttribute.GetValidationResult()
    1. 在MVC中重新用AbpMvcAttributeValidationResultProvider继承并重写了DefaultAttributeValidationResultProviderGetOrDefault()方法。用它实现了本地化。 ```csharp namespace Volo.Abp.AspNetCore.Mvc.Localization;

[Dependency(ReplaceServices = true)] public class AbpMvcAttributeValidationResultProvider : DefaultAttributeValidationResultProvider { private readonly AbpMvcDataAnnotationsLocalizationOptions _abpMvcDataAnnotationsLocalizationOptions; private readonly IStringLocalizerFactory _stringLocalizerFactory;

  1. public AbpMvcAttributeValidationResultProvider(
  2. IOptions<AbpMvcDataAnnotationsLocalizationOptions> abpMvcDataAnnotationsLocalizationOptions,
  3. IStringLocalizerFactory stringLocalizerFactory)
  4. {
  5. _abpMvcDataAnnotationsLocalizationOptions = abpMvcDataAnnotationsLocalizationOptions.Value;
  6. _stringLocalizerFactory = stringLocalizerFactory;
  7. }
  8. public override ValidationResult GetOrDefault(ValidationAttribute validationAttribute, object validatingObject, ValidationContext validationContext)
  9. {
  10. var resourceSource = _abpMvcDataAnnotationsLocalizationOptions.AssemblyResources.GetOrDefault(validationContext.ObjectType.Assembly);
  11. if (resourceSource == null)
  12. {
  13. return base.GetOrDefault(validationAttribute, validatingObject, validationContext);
  14. }
  15. if (validationAttribute.ErrorMessage == null)
  16. {
  17. ValidationAttributeHelper.SetDefaultErrorMessage(validationAttribute);
  18. }
  19. if (validationAttribute.ErrorMessage != null)
  20. {
  21. validationAttribute.ErrorMessage = _stringLocalizerFactory.Create(resourceSource)[validationAttribute.ErrorMessage];
  22. }
  23. return base.GetOrDefault(validationAttribute, validatingObject, validationContext);
  24. }

}

  1. <a name="d0tco"></a>
  2. ## FluentValidation
  3. 引入了第三方类库`FluentValidation`,并实现了`IObjectValidationContributor`。
  4. ```csharp
  5. namespace Volo.Abp.FluentValidation;
  6. public class FluentObjectValidationContributor : IObjectValidationContributor, ITransientDependency
  7. {
  8. private readonly IServiceProvider _serviceProvider;
  9. public FluentObjectValidationContributor(
  10. IServiceProvider serviceProvider)
  11. {
  12. _serviceProvider = serviceProvider;
  13. }
  14. // 通过Ioc获取验证器,然后将验证结果添加到上下文的Errors集合中。
  15. public virtual async Task AddErrorsAsync(ObjectValidationContext context)
  16. {
  17. var serviceType = typeof(IValidator<>).MakeGenericType(context.ValidatingObject.GetType());
  18. var validator = _serviceProvider.GetService(serviceType) as IValidator;
  19. if (validator == null)
  20. {
  21. return;
  22. }
  23. // 获取验证结果
  24. var result = await validator.ValidateAsync((IValidationContext)Activator.CreateInstance(
  25. typeof(ValidationContext<>).MakeGenericType(context.ValidatingObject.GetType()),
  26. context.ValidatingObject));
  27. if (!result.IsValid)
  28. {
  29. // 返回验证失败信息
  30. context.Errors.AddRange(
  31. result.Errors.Select(
  32. error =>
  33. new ValidationResult(error.ErrorMessage, new[] { error.PropertyName })
  34. )
  35. );
  36. }
  37. }
  38. }