| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.ComponentModel.DataAnnotations;
- using System.Linq;
- using System.Reflection;
- using Abp.Collections.Extensions;
- using Abp.Configuration.Startup;
- using Abp.Dependency;
- using Abp.Reflection;
- namespace Abp.Runtime.Validation.Interception
- {
- /// <summary>
- /// This class is used to validate a method call (invocation) for method arguments.
- /// </summary>
- public class MethodInvocationValidator : ITransientDependency
- {
- private const int MaxRecursiveParameterValidationDepth = 8;
- protected MethodInfo Method { get; private set; }
- protected object[] ParameterValues { get; private set; }
- protected ParameterInfo[] Parameters { get; private set; }
- protected List<ValidationResult> ValidationErrors { get; }
- protected List<IShouldNormalize> ObjectsToBeNormalized { get; }
- private readonly IValidationConfiguration _configuration;
- private readonly IIocResolver _iocResolver;
- /// <summary>
- /// Creates a new <see cref="MethodInvocationValidator"/> instance.
- /// </summary>
- public MethodInvocationValidator(IValidationConfiguration configuration, IIocResolver iocResolver)
- {
- _configuration = configuration;
- _iocResolver = iocResolver;
- ValidationErrors = new List<ValidationResult>();
- ObjectsToBeNormalized = new List<IShouldNormalize>();
- }
- /// <param name="method">Method to be validated</param>
- /// <param name="parameterValues">List of arguments those are used to call the <paramref name="method"/>.</param>
- public virtual void Initialize(MethodInfo method, object[] parameterValues)
- {
- Check.NotNull(method, nameof(method));
- Check.NotNull(parameterValues, nameof(parameterValues));
- Method = method;
- ParameterValues = parameterValues;
- Parameters = method.GetParameters();
- }
- /// <summary>
- /// Validates the method invocation.
- /// </summary>
- public void Validate()
- {
- CheckInitialized();
- if (Parameters.IsNullOrEmpty())
- {
- return;
- }
- if (!Method.IsPublic)
- {
- return;
- }
- if (IsValidationDisabled())
- {
- return;
- }
- if (Parameters.Length != ParameterValues.Length)
- {
- throw new Exception("Method parameter count does not match with argument count!");
- }
- for (var i = 0; i < Parameters.Length; i++)
- {
- ValidateMethodParameter(Parameters[i], ParameterValues[i]);
- }
- if (ValidationErrors.Any())
- {
- ThrowValidationError();
- }
- foreach (var objectToBeNormalized in ObjectsToBeNormalized)
- {
- objectToBeNormalized.Normalize();
- }
- }
- protected virtual void CheckInitialized()
- {
- if (Method == null)
- {
- throw new AbpException("This object has not been initialized. Call Initialize method first.");
- }
- }
- protected virtual bool IsValidationDisabled()
- {
- if (Method.IsDefined(typeof(EnableValidationAttribute), true))
- {
- return false;
- }
- return ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableValidationAttribute>(Method) != null;
- }
- protected virtual void ThrowValidationError()
- {
- throw new AbpValidationException(
- "Method arguments are not valid! See ValidationErrors for details.",
- ValidationErrors
- );
- }
- /// <summary>
- /// Validates given parameter for given value.
- /// </summary>
- /// <param name="parameterInfo">Parameter of the method to validate</param>
- /// <param name="parameterValue">Value to validate</param>
- protected virtual void ValidateMethodParameter(ParameterInfo parameterInfo, object parameterValue)
- {
- if (parameterValue == null)
- {
- if (!parameterInfo.IsOptional &&
- !parameterInfo.IsOut &&
- !TypeHelper.IsPrimitiveExtendedIncludingNullable(parameterInfo.ParameterType, includeEnums: true))
- {
- ValidationErrors.Add(new ValidationResult(parameterInfo.Name + " is null!", new[] { parameterInfo.Name }));
- }
- return;
- }
- ValidateObjectRecursively(parameterValue, 1);
- }
- protected virtual void ValidateObjectRecursively(object validatingObject, int currentDepth)
- {
- if (currentDepth > MaxRecursiveParameterValidationDepth)
- {
- return;
- }
- if (validatingObject == null)
- {
- return;
- }
- if (_configuration.IgnoredTypes.Any(t => t.IsInstanceOfType(validatingObject)))
- {
- return;
- }
- if (TypeHelper.IsPrimitiveExtendedIncludingNullable(validatingObject.GetType()))
- {
- return;
- }
- SetValidationErrors(validatingObject);
- // Validate items of enumerable
- if (IsEnumerable(validatingObject))
- {
- foreach (var item in (IEnumerable) validatingObject)
- {
- ValidateObjectRecursively(item, currentDepth + 1);
- }
- }
- // Add list to be normalized later
- if (validatingObject is IShouldNormalize)
- {
- ObjectsToBeNormalized.Add(validatingObject as IShouldNormalize);
- }
- if (ShouldMakeDeepValidation(validatingObject))
- {
- var properties = TypeDescriptor.GetProperties(validatingObject).Cast<PropertyDescriptor>();
- foreach (var property in properties)
- {
- if (property.Attributes.OfType<DisableValidationAttribute>().Any())
- {
- continue;
- }
- ValidateObjectRecursively(property.GetValue(validatingObject), currentDepth + 1);
- }
- }
- }
- 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);
- }
- }
- }
- }
- protected virtual bool ShouldValidateUsingValidator(object validatingObject, Type validatorType)
- {
- return true;
- }
- protected virtual bool ShouldMakeDeepValidation(object validatingObject)
- {
- // Do not recursively validate for enumerable objects
- if (validatingObject is IEnumerable)
- {
- return false;
- }
- var validatingObjectType = validatingObject.GetType();
- // Do not recursively validate for primitive objects
- if (TypeHelper.IsPrimitiveExtendedIncludingNullable(validatingObjectType))
- {
- return false;
- }
- return true;
- }
- private bool IsEnumerable(object validatingObject)
- {
- return
- validatingObject is IEnumerable &&
- !(validatingObject is IQueryable) &&
- !TypeHelper.IsPrimitiveExtendedIncludingNullable(validatingObject.GetType());
- }
- }
- }
|