Skip to content

Instantly share code, notes, and snippets.

@asbjornu
Last active January 26, 2017 12:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save asbjornu/d759c98408285dcab421e7494046615e to your computer and use it in GitHub Desktop.
Save asbjornu/d759c98408285dcab421e7494046615e to your computer and use it in GitHub Desktop.
public class RecursiveDataAnnotationsValidator : IModelValidator
{
private readonly IModelValidator validator;
public RecursiveDataAnnotationsValidator(IModelValidator innerValidator, Type type)
{
if (innerValidator == null)
throw new ArgumentNullException(nameof(innerValidator));
if (type == null)
throw new ArgumentNullException(nameof(type));
this.validator = innerValidator;
Description = CreateDescription(innerValidator, type);
}
public ModelValidationResult Validate(object instance, NancyContext context)
{
// Rewritten to fit into Nancy from: https://github.com/reustmd/DataAnnotationsValidatorRecursive/blob/master/DataAnnotationsValidator/DataAnnotationsValidator/DataAnnotationsValidator.cs
var result = this.validator.Validate(instance, context);
var properties = instance
.GetType()
.GetProperties()
.Where(prop => prop.CanRead && prop.GetIndexParameters().Length == 0)
.ToList();
var errors = result.Errors;
foreach (var property in properties)
{
if (property.PropertyType == typeof(string) || property.PropertyType.IsValueType)
continue;
var value = instance.GetPropertyValue(property.Name);
if (value == null)
continue;
var enumerable = value as IEnumerable;
if (enumerable != null)
{
foreach (var e in enumerable)
{
var nestedResult = this.validator.Validate(e, context);
Add(nestedResult, property, errors);
}
}
else
{
var nestedResult = this.validator.Validate(value, context);
Add(nestedResult, property, errors);
}
}
return new ModelValidationResult(errors);
}
public ModelValidationDescriptor Description { get; }
public Type ModelType => this.validator.ModelType;
private static void Add(ModelValidationResult nestedResult,
PropertyInfo property,
IDictionary<string, IList<ModelValidationError>> errors)
{
foreach (var kvp in nestedResult.Errors)
{
var key = string.Format("{0}.{1}", property.Name, kvp.Key);
var modelValidationErrors = kvp.Value.Select(error => Transform(error, property)).ToList();
errors.Add(key, modelValidationErrors);
}
}
private static ModelValidationDescriptor CreateDescription(IModelValidator validator, Type modelType)
{
var rules = validator.Description.Rules.ToDictionary(x => string.Format("{0}.{1}", validator.ModelType, x.Key),
x => x.Value);
return new ModelValidationDescriptor(rules, modelType);
}
private static ModelValidationError Transform(ModelValidationError error, PropertyInfo property)
{
var memberNames = error.MemberNames.Select(n => string.Format("{0}.{1}", property.Name, n));
return new ModelValidationError(memberNames, error.ErrorMessage);
}
}
public class RecursiveDataAnnotationsValidatorFactory : IModelValidatorFactory
{
private readonly IModelValidatorFactory modelValidatorFactory;
public RecursiveDataAnnotationsValidatorFactory(IPropertyValidatorFactory propertyValidatorFactory,
IValidatableObjectAdapter validatableObjectAdapter)
{
this.modelValidatorFactory = new DataAnnotationsValidatorFactory(propertyValidatorFactory, validatableObjectAdapter);
}
public IModelValidator Create(Type type)
{
var innerValidator = this.modelValidatorFactory.Create(type);
var validator = new RecursiveDataAnnotationsValidator(innerValidator, type);
return validator.Description.Rules.Any()
? validator
: null;
}
}
public class RecursiveDataAnnotationsValidatorLocator : IModelValidatorLocator
{
private readonly IModelValidatorFactory modelValidatorFactory;
public RecursiveDataAnnotationsValidatorLocator(IPropertyValidatorFactory propertyValidatorFactory,
IValidatableObjectAdapter validatableObjectAdapter)
{
if (propertyValidatorFactory == null)
throw new ArgumentNullException(nameof(propertyValidatorFactory));
if (validatableObjectAdapter == null)
throw new ArgumentNullException(nameof(validatableObjectAdapter));
this.modelValidatorFactory = new RecursiveDataAnnotationsValidatorFactory(propertyValidatorFactory,
validatableObjectAdapter);
}
public IModelValidator GetValidatorForType(Type type)
{
return this.modelValidatorFactory.Create(type);
}
}
public class RecursiveValidatorBootstrapper : DefaultNancyBootstrapper
{
protected override IEnumerable<Type> ModelValidatorFactories =>
new[] { typeof(RecursiveDataAnnotationsValidatorFactory) };
protected override NancyInternalConfiguration InternalConfiguration =>
NancyInternalConfiguration.WithOverrides(config => config.ModelValidatorLocator = typeof(RecursiveDataAnnotationsValidatorLocator));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment