mirror of https://github.com/abpframework/abp.git
15 changed files with 435 additions and 309 deletions
@ -0,0 +1,9 @@ |
|||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Validation |
||||
|
{ |
||||
|
public interface IMvcModelObjectValidator : IObjectValidator |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Microsoft.AspNetCore.Mvc.Abstractions; |
||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Validation |
||||
|
{ |
||||
|
public class MvcActionInvocationValidationContext : MethodInvocationValidationContext |
||||
|
{ |
||||
|
public ActionExecutingContext ActionContext { get; } |
||||
|
|
||||
|
public MvcActionInvocationValidationContext(ActionExecutingContext actionContext) |
||||
|
: base(actionContext.ActionDescriptor.GetMethodInfo(), GetParameterValues(actionContext)) |
||||
|
{ |
||||
|
ActionContext = actionContext; |
||||
|
} |
||||
|
|
||||
|
private static object[] GetParameterValues(ActionExecutingContext actionContext) |
||||
|
{ |
||||
|
var methodInfo = actionContext.ActionDescriptor.GetMethodInfo(); |
||||
|
|
||||
|
var parameters = methodInfo.GetParameters(); |
||||
|
var parameterValues = new object[parameters.Length]; |
||||
|
|
||||
|
for (var i = 0; i < parameters.Length; i++) |
||||
|
{ |
||||
|
parameterValues[i] = actionContext.ActionArguments.GetOrDefault(parameters[i].Name); |
||||
|
} |
||||
|
|
||||
|
return parameterValues; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,72 +1,37 @@ |
|||||
using System.Collections.Generic; |
using System.ComponentModel.DataAnnotations; |
||||
using System.ComponentModel.DataAnnotations; |
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions; |
|
||||
using Microsoft.AspNetCore.Mvc.Filters; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using Volo.Abp.Validation; |
using Volo.Abp.Validation; |
||||
|
|
||||
namespace Volo.Abp.AspNetCore.Mvc.Validation |
namespace Volo.Abp.AspNetCore.Mvc.Validation |
||||
{ |
{ |
||||
public class MvcActionInvocationValidator : MethodInvocationValidator |
public class MvcActionInvocationValidator : MethodInvocationValidatorBase |
||||
{ |
{ |
||||
protected ActionExecutingContext ActionContext { get; private set; } |
// ReSharper disable once SuggestBaseTypeForParameter
|
||||
|
public MvcActionInvocationValidator(IMvcModelObjectValidator objectValidator) |
||||
private bool _isValidatedBefore; |
: base(objectValidator) |
||||
|
|
||||
public MvcActionInvocationValidator(IOptions<AbpValidationOptions> options) |
|
||||
: base(options) |
|
||||
{ |
{ |
||||
|
|
||||
} |
} |
||||
|
|
||||
public void Initialize(ActionExecutingContext actionContext) |
public virtual void Validate(MvcActionInvocationValidationContext context) |
||||
{ |
|
||||
ActionContext = actionContext; |
|
||||
|
|
||||
SetDataAnnotationAttributeErrors(); |
|
||||
|
|
||||
base.Initialize( |
|
||||
actionContext.ActionDescriptor.GetMethodInfo(), |
|
||||
GetParameterValues(actionContext) |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
protected override void SetDataAnnotationAttributeErrors(object validatingObject) |
|
||||
{ |
{ |
||||
SetDataAnnotationAttributeErrors(); |
AddModelStateErrors(context); |
||||
|
ValidateInternal(context); |
||||
} |
} |
||||
|
|
||||
protected virtual void SetDataAnnotationAttributeErrors() |
public virtual void AddModelStateErrors(MvcActionInvocationValidationContext context) |
||||
{ |
{ |
||||
if (_isValidatedBefore || ActionContext.ModelState.IsValid) |
if (context.ActionContext.ModelState.IsValid) |
||||
{ |
{ |
||||
return; |
return; |
||||
} |
} |
||||
|
|
||||
foreach (var state in ActionContext.ModelState) |
foreach (var state in context.ActionContext.ModelState) |
||||
{ |
{ |
||||
foreach (var error in state.Value.Errors) |
foreach (var error in state.Value.Errors) |
||||
{ |
{ |
||||
ValidationErrors.Add(new ValidationResult(error.ErrorMessage, new[] { state.Key })); |
context.Errors.Add(new ValidationResult(error.ErrorMessage, new[] { state.Key })); |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
_isValidatedBefore = true; |
|
||||
} |
|
||||
|
|
||||
protected virtual object[] GetParameterValues(ActionExecutingContext actionContext) |
|
||||
{ |
|
||||
var methodInfo = actionContext.ActionDescriptor.GetMethodInfo(); |
|
||||
|
|
||||
var parameters = methodInfo.GetParameters(); |
|
||||
var parameterValues = new object[parameters.Length]; |
|
||||
|
|
||||
for (var i = 0; i < parameters.Length; i++) |
|
||||
{ |
|
||||
parameterValues[i] = actionContext.ActionArguments.GetOrDefault(parameters[i].Name); |
|
||||
} |
|
||||
|
|
||||
return parameterValues; |
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Validation |
||||
|
{ |
||||
|
[ExposeServices(typeof(IMvcModelObjectValidator))] |
||||
|
public class MvcModelObjectValidator : ObjectValidator, IMvcModelObjectValidator |
||||
|
{ |
||||
|
public MvcModelObjectValidator(IOptions<AbpValidationOptions> options) |
||||
|
: base(options, NullDataAnnotationValidator.Instance) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Validation |
||||
|
{ |
||||
|
public sealed class NullDataAnnotationValidator : IDataAnnotationValidator |
||||
|
{ |
||||
|
public static IDataAnnotationValidator Instance { get; } = new DataAnnotationValidator(); |
||||
|
|
||||
|
private NullDataAnnotationValidator() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public void AddDataAnnotationAttributeErrors(IAbpValidationResult validationResult, object validatingObject) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Linq; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
public class DataAnnotationValidator : IDataAnnotationValidator, ITransientDependency |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Checks all properties for DataAnnotations attributes.
|
||||
|
/// </summary>
|
||||
|
public virtual void AddDataAnnotationAttributeErrors(IAbpValidationResult validationResult, object validatingObject) |
||||
|
{ |
||||
|
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) |
||||
|
{ |
||||
|
validationResult.Errors.Add(result); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (validatingObject is IValidatableObject) |
||||
|
{ |
||||
|
var results = (validatingObject as IValidatableObject).Validate(new ValidationContext(validatingObject)); |
||||
|
validationResult.Errors.AddRange(results); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
|
||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
public interface IAbpValidationResult |
||||
|
{ |
||||
|
List<ValidationResult> Errors { get; } |
||||
|
|
||||
|
List<IShouldNormalize> ObjectsToBeNormalized { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
public interface IDataAnnotationValidator |
||||
|
{ |
||||
|
void AddDataAnnotationAttributeErrors(IAbpValidationResult validationResult, object validatingObject); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
public interface IObjectValidator |
||||
|
{ |
||||
|
void Validate(IAbpValidationResult validationResult, object validatingObject); |
||||
|
|
||||
|
void AddValidatationErrors(IAbpValidationResult validationResult, object validatingObject); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Reflection; |
||||
|
|
||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
public class MethodInvocationValidationContext : IAbpValidationResult |
||||
|
{ |
||||
|
public MethodInfo Method { get; } |
||||
|
|
||||
|
public object[] ParameterValues { get; } |
||||
|
|
||||
|
public ParameterInfo[] Parameters { get; } |
||||
|
|
||||
|
public List<ValidationResult> Errors { get; } |
||||
|
|
||||
|
public List<IShouldNormalize> ObjectsToBeNormalized { get; } |
||||
|
|
||||
|
public MethodInvocationValidationContext(MethodInfo method, object[] parameterValues) |
||||
|
{ |
||||
|
Method = method; |
||||
|
ParameterValues = parameterValues; |
||||
|
Parameters = method.GetParameters(); |
||||
|
|
||||
|
Errors = new List<ValidationResult>(); |
||||
|
ObjectsToBeNormalized = new List<IShouldNormalize>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,250 +1,19 @@ |
|||||
using System; |
|
||||
using System.Collections; |
|
||||
using System.Collections.Generic; |
|
||||
using System.ComponentModel; |
|
||||
using System.ComponentModel.DataAnnotations; |
|
||||
using System.Linq; |
|
||||
using System.Reflection; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
using Volo.Abp.Reflection; |
|
||||
|
|
||||
namespace Volo.Abp.Validation |
namespace Volo.Abp.Validation |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// This class is used to validate a method call (invocation) for method arguments.
|
/// This class is used to validate a method call (invocation) for method arguments.
|
||||
/// </summary>
|
/// </summary>
|
||||
public class MethodInvocationValidator : ITransientDependency |
public class MethodInvocationValidator : MethodInvocationValidatorBase |
||||
{ |
{ |
||||
private const int MaxRecursiveParameterValidationDepth = 8; |
public MethodInvocationValidator(IObjectValidator objectValidator) |
||||
|
: base(objectValidator) |
||||
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 AbpValidationOptions _options; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a new <see cref="MethodInvocationValidator"/> instance.
|
|
||||
/// </summary>
|
|
||||
public MethodInvocationValidator(IOptions<AbpValidationOptions> options) |
|
||||
{ |
|
||||
_options = options.Value; |
|
||||
|
|
||||
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>
|
public virtual void Validate(MethodInvocationValidationContext context) |
||||
/// Validates the method invocation.
|
|
||||
/// </summary>
|
|
||||
public void Validate() |
|
||||
{ |
{ |
||||
CheckInitialized(); |
ValidateInternal(context); |
||||
|
|
||||
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!"); |
|
||||
} |
|
||||
|
|
||||
if (ValidationErrors.Any() && HasSingleNullArgument()) |
|
||||
{ |
|
||||
ThrowValidationError(); |
|
||||
} |
|
||||
|
|
||||
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 bool HasSingleNullArgument() |
|
||||
{ |
|
||||
return Parameters.Length == 1 && ParameterValues[0] == 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; |
|
||||
} |
|
||||
|
|
||||
SetDataAnnotationAttributeErrors(validatingObject); |
|
||||
|
|
||||
//Validate items of enumerable
|
|
||||
if (validatingObject is IEnumerable && !(validatingObject is IQueryable)) |
|
||||
{ |
|
||||
foreach (var item in (validatingObject as IEnumerable)) |
|
||||
{ |
|
||||
ValidateObjectRecursively(item, currentDepth + 1); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
//Add list to be normalized later
|
|
||||
if (validatingObject is IShouldNormalize) |
|
||||
{ |
|
||||
ObjectsToBeNormalized.Add(validatingObject as IShouldNormalize); |
|
||||
} |
|
||||
|
|
||||
//Do not recursively validate for enumerable objects
|
|
||||
if (validatingObject is IEnumerable) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
var validatingObjectType = validatingObject.GetType(); |
|
||||
|
|
||||
//Do not recursively validate for primitive objects
|
|
||||
if (TypeHelper.IsPrimitiveExtendedIncludingNullable(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(property.GetValue(validatingObject), currentDepth + 1); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Checks all properties for DataAnnotations attributes.
|
|
||||
/// </summary>
|
|
||||
protected virtual void SetDataAnnotationAttributeErrors(object validatingObject) |
|
||||
{ |
|
||||
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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (validatingObject is IValidatableObject) |
|
||||
{ |
|
||||
var results = (validatingObject as IValidatableObject).Validate(new ValidationContext(validatingObject)); |
|
||||
ValidationErrors.AddRange(results); |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,113 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Reflection; |
||||
|
|
||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
public abstract class MethodInvocationValidatorBase : ITransientDependency |
||||
|
{ |
||||
|
private readonly IObjectValidator _objectValidator; |
||||
|
|
||||
|
protected MethodInvocationValidatorBase(IObjectValidator objectValidator) |
||||
|
{ |
||||
|
_objectValidator = objectValidator; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Validates the method invocation.
|
||||
|
/// </summary>
|
||||
|
protected virtual void ValidateInternal(MethodInvocationValidationContext context) |
||||
|
{ |
||||
|
Check.NotNull(context, nameof(context)); |
||||
|
|
||||
|
if (context.Parameters.IsNullOrEmpty()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
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!"); |
||||
|
} |
||||
|
|
||||
|
if (context.Errors.Any() && HasSingleNullArgument(context)) |
||||
|
{ |
||||
|
ThrowValidationError(context); |
||||
|
} |
||||
|
|
||||
|
AddMethodParameterValidationErrors(context); |
||||
|
|
||||
|
if (context.Errors.Any()) |
||||
|
{ |
||||
|
ThrowValidationError(context); |
||||
|
} |
||||
|
|
||||
|
foreach (var objectToBeNormalized in context.ObjectsToBeNormalized) |
||||
|
{ |
||||
|
objectToBeNormalized.Normalize(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual bool IsValidationDisabled(MethodInvocationValidationContext context) |
||||
|
{ |
||||
|
if (context.Method.IsDefined(typeof(EnableValidationAttribute), true)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<DisableValidationAttribute>(context.Method) != null; |
||||
|
} |
||||
|
|
||||
|
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 void AddMethodParameterValidationErrors(MethodInvocationValidationContext context) |
||||
|
{ |
||||
|
for (var i = 0; i < context.Parameters.Length; i++) |
||||
|
{ |
||||
|
AddMethodParameterValidationErrors(context, context.Parameters[i], context.ParameterValues[i]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual void AddMethodParameterValidationErrors(IAbpValidationResult context, ParameterInfo parameterInfo, object parameterValue) |
||||
|
{ |
||||
|
if (parameterValue == null) |
||||
|
{ |
||||
|
if (!parameterInfo.IsOptional && |
||||
|
!parameterInfo.IsOut && |
||||
|
!TypeHelper.IsPrimitiveExtendedIncludingNullable(parameterInfo.ParameterType, includeEnums: true)) |
||||
|
{ |
||||
|
context.Errors.Add(new ValidationResult(parameterInfo.Name + " is null!", new[] { parameterInfo.Name })); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
_objectValidator.AddValidatationErrors(context, parameterValue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,101 @@ |
|||||
|
using System.Collections; |
||||
|
using System.ComponentModel; |
||||
|
using System.Linq; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Reflection; |
||||
|
|
||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
public class ObjectValidator : ITransientDependency, IObjectValidator |
||||
|
{ |
||||
|
private const int MaxRecursiveParameterValidationDepth = 8; |
||||
|
|
||||
|
private readonly AbpValidationOptions _options; |
||||
|
private readonly IDataAnnotationValidator _dataAnnotationValidator; |
||||
|
|
||||
|
public ObjectValidator(IOptions<AbpValidationOptions> options, IDataAnnotationValidator dataAnnotationValidator) |
||||
|
{ |
||||
|
_dataAnnotationValidator = dataAnnotationValidator; |
||||
|
_options = options.Value; |
||||
|
} |
||||
|
|
||||
|
public virtual void Validate(IAbpValidationResult validationResult, object validatingObject) |
||||
|
{ |
||||
|
AddValidatationErrors(validationResult, validatingObject); |
||||
|
|
||||
|
if (validationResult.Errors.Any()) |
||||
|
{ |
||||
|
throw new AbpValidationException( |
||||
|
"Method arguments are not valid! See ValidationErrors for details.", |
||||
|
validationResult.Errors |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public virtual void AddValidatationErrors(IAbpValidationResult validationResult, object validatingObject) |
||||
|
{ |
||||
|
ValidateObjectRecursively(validationResult, validatingObject, 1); |
||||
|
} |
||||
|
|
||||
|
protected virtual void ValidateObjectRecursively(IAbpValidationResult context, object validatingObject, int currentDepth) |
||||
|
{ |
||||
|
if (currentDepth > MaxRecursiveParameterValidationDepth) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (validatingObject == null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
_dataAnnotationValidator.AddDataAnnotationAttributeErrors(context, validatingObject); |
||||
|
|
||||
|
//Validate items of enumerable
|
||||
|
if (validatingObject is IEnumerable && !(validatingObject is IQueryable)) |
||||
|
{ |
||||
|
foreach (var item in (validatingObject as IEnumerable)) |
||||
|
{ |
||||
|
ValidateObjectRecursively(context, item, currentDepth + 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//Add list to be normalized later
|
||||
|
if (validatingObject is IShouldNormalize) |
||||
|
{ |
||||
|
context.ObjectsToBeNormalized.Add(validatingObject as IShouldNormalize); |
||||
|
} |
||||
|
|
||||
|
//Do not recursively validate for enumerable objects
|
||||
|
if (validatingObject is IEnumerable) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var validatingObjectType = validatingObject.GetType(); |
||||
|
|
||||
|
//Do not recursively validate for primitive objects
|
||||
|
if (TypeHelper.IsPrimitiveExtendedIncludingNullable(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(context, property.GetValue(validatingObject), currentDepth + 1); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue