private void ValidateProperties(ModelValidationContext validationContext) { var modelState = validationContext.ModelState; var model = ModelMetadata.Model; var updatedMetadata = validationContext.MetadataProvider.GetMetadataForType(() => model, ModelMetadata.ModelType); foreach (var propertyMetadata in updatedMetadata.Properties) { // Only want to add errors to ModelState if something doesn't already exist for the property node, // else we could end up with duplicate or irrelevant error messages. var propertyKeyRoot = ModelBindingHelper.CreatePropertyModelName(ModelStateKey, propertyMetadata.PropertyName); if (modelState.GetFieldValidationState(propertyKeyRoot) == ModelValidationState.Unvalidated) { var propertyValidators = GetValidators(validationContext, propertyMetadata); var propertyValidationContext = new ModelValidationContext(validationContext, propertyMetadata); foreach (var propertyValidator in propertyValidators) { foreach (var propertyResult in propertyValidator.Validate(propertyValidationContext)) { var thisErrorKey = ModelBindingHelper.CreatePropertyModelName(propertyKeyRoot, propertyResult.MemberName); modelState.TryAddModelError(thisErrorKey, propertyResult.Message); } } } } }
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context) { var propertiesValid = true; var metadata = context.ModelMetadata; foreach (var propertyMetadata in metadata.Properties) { var propertyContext = new ModelValidationContext(context, propertyMetadata); foreach (var propertyValidator in _validators) { foreach (var validationResult in propertyValidator.Validate(propertyContext)) { propertiesValid = false; yield return CreateSubPropertyResult(propertyMetadata, validationResult); } } } if (propertiesValid) { foreach (var typeValidator in _validators) { foreach (var typeResult in typeValidator.Validate(context)) { yield return typeResult; } } } }
public void Validate([NotNull] ModelValidationContext validationContext, ModelValidationNode parentNode) { if (SuppressValidation || !validationContext.ModelState.CanAddErrors) { // Short circuit if validation does not need to be applied or if we've reached the max number of validation errors. return; } // pre-validation steps var validatingEventArgs = new ModelValidatingEventArgs(validationContext, parentNode); OnValidating(validatingEventArgs); if (validatingEventArgs.Cancel) { return; } ValidateChildren(validationContext); ValidateThis(validationContext, parentNode); // post-validation steps var validatedEventArgs = new ModelValidatedEventArgs(validationContext, parentNode); OnValidated(validatedEventArgs); var modelState = validationContext.ModelState; if (modelState.GetFieldValidationState(ModelStateKey) != ModelValidationState.Invalid) { // If a node or its subtree were not marked invalid, we can consider it valid at this point. modelState.MarkFieldValid(ModelStateKey); } }
public void SetProperty_PropertyIsSettable_CallsSetter() { // Arrange var model = new Person(); var bindingContext = CreateContext(GetMetadataForObject(model)); var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth"); var validationNode = new ModelValidationNode(propertyMetadata, "foo"); var dtoResult = new ComplexModelDtoResult(new DateTime(2001, 1, 1), validationNode); var requiredValidator = bindingContext.ValidatorProviders .SelectMany(v => v.GetValidators(propertyMetadata)) .Where(v => v.IsRequired) .FirstOrDefault(); var validationContext = new ModelValidationContext(bindingContext, propertyMetadata); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator); // Assert validationNode.Validate(validationContext); Assert.Equal(true, bindingContext.ModelState.IsValid); Assert.Equal(new DateTime(2001, 1, 1), model.DateOfBirth); }
public IEnumerable <ModelValidationResult> Validate(ModelValidationContext context) { var propertiesValid = true; var metadata = context.ModelMetadata; foreach (var propertyMetadata in metadata.Properties) { var propertyContext = new ModelValidationContext(context, propertyMetadata); foreach (var propertyValidator in _validators) { foreach (var validationResult in propertyValidator.Validate(propertyContext)) { propertiesValid = false; yield return(CreateSubPropertyResult(propertyMetadata, validationResult)); } } } if (propertiesValid) { foreach (var typeValidator in _validators) { foreach (var typeResult in typeValidator.Validate(context)) { yield return(typeResult); } } } }
private void ValidateThis(ModelValidationContext validationContext, ModelValidationNode parentNode) { var modelState = validationContext.ModelState; if (modelState.GetFieldValidationState(ModelStateKey) == ModelValidationState.Invalid) { // If any item in the key's subtree has been identified as invalid, short-circuit return; } // If the Model at the current node is null and there is no parent, we cannot validate, and the // DataAnnotationsModelValidator will throw. So we intercept here to provide a catch-all value-required // validation error if (parentNode == null && ModelMetadata.Model == null) { modelState.TryAddModelError(ModelStateKey, Resources.Validation_ValueNotFound); return; } var container = TryConvertContainerToMetadataType(parentNode); var validators = GetValidators(validationContext, ModelMetadata).ToArray(); for (var i = 0; i < validators.Length; i++) { var validator = validators[i]; foreach (var validationResult in validator.Validate(validationContext)) { var currentModelStateKey = ModelBindingHelper.CreatePropertyModelName(ModelStateKey, validationResult.MemberName); modelState.TryAddModelError(currentModelStateKey, validationResult.Message); } } }
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext validationContext) { var metadata = validationContext.ModelMetadata; var memberName = metadata.PropertyName ?? metadata.ModelType.Name; var containerMetadata = validationContext.ContainerMetadata; var container = containerMetadata != null ? containerMetadata.Model : null; var context = new ValidationContext(container ?? metadata.Model) { DisplayName = metadata.GetDisplayName(), MemberName = memberName }; var result = Attribute.GetValidationResult(metadata.Model, context); if (result != ValidationResult.Success) { // ModelValidationResult.MemberName is used by invoking validators (such as ModelValidator) to // construct the ModelKey for ModelStateDictionary. When validating at type level we want to append // the returned MemberNames if specified (e.g. person.Address.FirstName). For property validation, the // ModelKey can be constructed using the ModelMetadata and we should ignore MemberName (we don't want // (person.Name.Name). However the invoking validator does not have a way to distinguish between these // two cases. Consequently we'll only set MemberName if this validation returns a MemberName that is // different from the property being validated. var errorMemberName = result.MemberNames.FirstOrDefault(); if (string.Equals(errorMemberName, memberName, StringComparison.Ordinal)) { errorMemberName = null; } var validationResult = new ModelValidationResult(errorMemberName, result.ErrorMessage); return new ModelValidationResult[] { validationResult }; } return Enumerable.Empty<ModelValidationResult>(); }
public virtual async Task <bool> BindModelAsync([NotNull] ModelBindingContext bindingContext) { var newBindingContext = CreateNewBindingContext(bindingContext, bindingContext.ModelName, reuseValidationNode: true); var boundSuccessfully = await TryBind(newBindingContext); if (!boundSuccessfully && !string.IsNullOrEmpty(bindingContext.ModelName) && bindingContext.FallbackToEmptyPrefix) { // fallback to empty prefix? newBindingContext = CreateNewBindingContext(bindingContext, modelName: string.Empty, reuseValidationNode: false); boundSuccessfully = await TryBind(newBindingContext); } if (!boundSuccessfully) { return(false); // something went wrong } // Only perform validation at the root of the object graph. ValidationNode will recursively walk the graph. // Ignore ComplexModelDto since it essentially wraps the primary object. if (newBindingContext.IsModelSet && IsBindingAtRootOfObjectGraph(newBindingContext)) { // run validation and return the model // If we fell back to an empty prefix above and are dealing with simple types, // propagate the non-blank model name through for user clarity in validation errors. // Complex types will reveal their individual properties as model names and do not require this. if (!newBindingContext.ModelMetadata.IsComplexType && string.IsNullOrEmpty(newBindingContext.ModelName)) { newBindingContext.ValidationNode = new ModelValidationNode(newBindingContext.ModelMetadata, bindingContext.ModelName); } var validationContext = new ModelValidationContext( bindingContext.OperationBindingContext.MetadataProvider, bindingContext.OperationBindingContext.ValidatorProvider, bindingContext.ModelState, bindingContext.ModelMetadata, containerMetadata: null); newBindingContext.ValidationNode.Validate(validationContext, parentNode: null); } bindingContext.OperationBindingContext.BodyBindingState = newBindingContext.OperationBindingContext.BodyBindingState; if (newBindingContext.IsModelSet) { bindingContext.Model = newBindingContext.Model; } return(true); }
public ModelValidationContext([NotNull] ModelValidationContext parentContext, [NotNull] ModelMetadata metadata) { ModelMetadata = metadata; ContainerMetadata = parentContext.ModelMetadata; ModelState = parentContext.ModelState; MetadataProvider = parentContext.MetadataProvider; ValidatorProviders = parentContext.ValidatorProviders; }
private void ValidateChildren(ModelValidationContext validationContext) { for (var i = 0; i < _childNodes.Count; i++) { var child = _childNodes[i]; var childValidationContext = new ModelValidationContext(validationContext, child.ModelMetadata); child.Validate(childValidationContext, this); } if (ValidateAllProperties) { ValidateProperties(validationContext); } }
public virtual async Task<bool> BindModelAsync([NotNull] ModelBindingContext bindingContext) { var newBindingContext = CreateNewBindingContext(bindingContext, bindingContext.ModelName, reuseValidationNode: true); var boundSuccessfully = await TryBind(newBindingContext); if (!boundSuccessfully && !string.IsNullOrEmpty(bindingContext.ModelName) && bindingContext.FallbackToEmptyPrefix) { // fallback to empty prefix? newBindingContext = CreateNewBindingContext(bindingContext, modelName: string.Empty, reuseValidationNode: false); boundSuccessfully = await TryBind(newBindingContext); } if (!boundSuccessfully) { return false; // something went wrong } // Only perform validation at the root of the object graph. ValidationNode will recursively walk the graph. // Ignore ComplexModelDto since it essentially wraps the primary object. if (newBindingContext.ModelMetadata.ContainerType == null && newBindingContext.ModelMetadata.ModelType != typeof(ComplexModelDto)) { // run validation and return the model // If we fell back to an empty prefix above and are dealing with simple types, // propagate the non-blank model name through for user clarity in validation errors. // Complex types will reveal their individual properties as model names and do not require this. if (!newBindingContext.ModelMetadata.IsComplexType && string.IsNullOrEmpty(newBindingContext.ModelName)) { newBindingContext.ValidationNode = new ModelValidationNode(newBindingContext.ModelMetadata, bindingContext.ModelName); } var validationContext = new ModelValidationContext(bindingContext.MetadataProvider, bindingContext.ValidatorProviders, bindingContext.ModelState, bindingContext.ModelMetadata, containerMetadata: null); newBindingContext.ValidationNode.Validate(validationContext, parentNode: null); } bindingContext.Model = newBindingContext.Model; return true; }
/// <inheritdoc /> public bool Validate( [NotNull] ModelValidationContext modelValidationContext, string keyPrefix) { var metadata = modelValidationContext.ModelMetadata; var validationContext = new ValidationContext() { ModelValidationContext = modelValidationContext, Visited = new HashSet <object>(ReferenceEqualityComparer.Instance), KeyBuilders = new Stack <IKeyBuilder>(), RootPrefix = keyPrefix }; return(ValidateNonVisitedNodeAndChildren(metadata, validationContext, validators: null)); }
public void DoesNotReadPropertyValue() { // Arrange var provider = new DataAnnotationsModelValidatorProvider(); var model = new ObservableModel(); var metadata = _metadataProvider.GetMetadataForProperty(() => model.TheProperty, typeof(ObservableModel), "TheProperty"); var context = new ModelValidationContext(null, null, null, metadata, null); // Act var validators = provider.GetValidators(metadata).ToArray(); var results = validators.SelectMany(o => o.Validate(context)).ToArray(); // Assert Assert.Empty(validators); Assert.False(model.PropertyWasRead()); }
// Validates a single node (not including children) // Returns true if validation passes successfully private static bool ShallowValidate( ModelMetadata metadata, ValidationContext validationContext, [NotNull] IEnumerable <IModelValidator> validators) { var isValid = true; string modelKey = null; // When the are no validators we bail quickly. This saves a GetEnumerator allocation. // In a large array (tens of thousands or more) scenario it's very significant. var validatorsAsCollection = validators as ICollection; if (validatorsAsCollection != null && validatorsAsCollection.Count == 0) { return(isValid); } var modelValidationContext = new ModelValidationContext(validationContext.ModelValidationContext, metadata); foreach (var validator in validators) { foreach (var error in validator.Validate(modelValidationContext)) { if (modelKey == null) { modelKey = validationContext.RootPrefix; // This constructs the object heirarchy // Example: prefix.Parent.Child foreach (var keyBuilder in validationContext.KeyBuilders.Reverse()) { modelKey = keyBuilder.AppendTo(modelKey); } } var errorKey = ModelBindingHelper.CreatePropertyModelName(modelKey, error.MemberName); validationContext.ModelValidationContext.ModelState.AddModelError(errorKey, error.Message); isValid = false; } } return(isValid); }
public void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorNotPresent_RegistersValidationCallback() { // Arrange var bindingContext = CreateContext(GetMetadataForObject(new Person())); var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth"); var validationNode = new ModelValidationNode(propertyMetadata, "foo"); var dtoResult = new ComplexModelDtoResult(model: null, validationNode: validationNode); var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata); var validationContext = new ModelValidationContext(bindingContext, propertyMetadata); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator); // Assert Assert.Equal(true, bindingContext.ModelState.IsValid); validationNode.Validate(validationContext, bindingContext.ValidationNode); Assert.Equal(false, bindingContext.ModelState.IsValid); }
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context) { var model = context.ModelMetadata.Model; if (model == null) { return Enumerable.Empty<ModelValidationResult>(); } var validatable = model as IValidatableObject; if (validatable == null) { var message = Resources.FormatValidatableObjectAdapter_IncompatibleType( typeof(IValidatableObject).Name, model.GetType()); throw new InvalidOperationException(message); } var validationContext = new ValidationContext(validatable, serviceProvider: null, items: null); return ConvertResults(validatable.Validate(validationContext)); }
// Returns true if validator execution adds a model error. private static bool RunValidator(IModelValidator validator, ModelBindingContext bindingContext, ModelMetadata propertyMetadata, string modelStateKey) { var validationContext = new ModelValidationContext(bindingContext, propertyMetadata); var addedError = false; foreach (var validationResult in validator.Validate(validationContext)) { bindingContext.ModelState.TryAddModelError(modelStateKey, validationResult.Message); addedError = true; } if (!addedError) { bindingContext.ModelState.MarkFieldValid(modelStateKey); } return(addedError); }
public void NullCheckFailedHandler_ModelStateValid_AddsErrorString() { // Arrange var modelState = new ModelStateDictionary(); var modelMetadata = GetMetadataForType(typeof(Person)); var validationNode = new ModelValidationNode(modelMetadata, "foo"); var validationContext = new ModelValidationContext(new DataAnnotationsModelMetadataProvider(), Mock.Of <IModelValidatorProvider>(), modelState, modelMetadata, null); var e = new ModelValidatedEventArgs(validationContext, parentNode: null); // Act var handler = MutableObjectModelBinder.CreateNullCheckFailedHandler(modelMetadata, incomingValue: null); handler(validationNode, e); // Assert Assert.True(modelState.ContainsKey("foo")); Assert.Equal("A value is required.", modelState["foo"].Errors[0].ErrorMessage); }
public IEnumerable <ModelValidationResult> Validate(ModelValidationContext context) { var model = context.ModelMetadata.Model; if (model == null) { return(Enumerable.Empty <ModelValidationResult>()); } var validatable = model as IValidatableObject; if (validatable == null) { var message = Resources.FormatValidatableObjectAdapter_IncompatibleType( typeof(IValidatableObject).Name, model.GetType()); throw new InvalidOperationException(message); } var validationContext = new ValidationContext(validatable, serviceProvider: null, items: null); return(ConvertResults(validatable.Validate(validationContext))); }
public void NullCheckFailedHandler_ModelStateAlreadyInvalid_DoesNothing() { // Arrange var modelState = new ModelStateDictionary(); modelState.AddModelError("foo.bar", "Some existing error."); var modelMetadata = GetMetadataForType(typeof(Person)); var validationNode = new ModelValidationNode(modelMetadata, "foo"); var validationContext = new ModelValidationContext(new DataAnnotationsModelMetadataProvider(), Mock.Of <IModelValidatorProvider>(), modelState, modelMetadata, null); var e = new ModelValidatedEventArgs(validationContext, parentNode: null); // Act var handler = MutableObjectModelBinder.CreateNullCheckFailedHandler(modelMetadata, incomingValue: null); handler(validationNode, e); // Assert Assert.False(modelState.ContainsKey("foo")); }
public IEnumerable <ModelValidationResult> Validate(ModelValidationContext validationContext) { var metadata = validationContext.ModelMetadata; var memberName = metadata.PropertyName ?? metadata.ModelType.Name; var containerMetadata = validationContext.ContainerMetadata; var container = containerMetadata != null ? containerMetadata.Model : null; var context = new ValidationContext(container ?? metadata.Model) { DisplayName = metadata.GetDisplayName(), MemberName = memberName }; var result = Attribute.GetValidationResult(metadata.Model, context); if (result != ValidationResult.Success) { // ModelValidationResult.MemberName is used by invoking validators (such as ModelValidator) to // construct the ModelKey for ModelStateDictionary. When validating at type level we want to append // the returned MemberNames if specified (e.g. person.Address.FirstName). For property validation, the // ModelKey can be constructed using the ModelMetadata and we should ignore MemberName (we don't want // (person.Name.Name). However the invoking validator does not have a way to distinguish between these // two cases. Consequently we'll only set MemberName if this validation returns a MemberName that is // different from the property being validated. var errorMemberName = result.MemberNames.FirstOrDefault(); if (string.Equals(errorMemberName, memberName, StringComparison.Ordinal)) { errorMemberName = null; } var validationResult = new ModelValidationResult(errorMemberName, result.ErrorMessage); return(new ModelValidationResult[] { validationResult }); } return(Enumerable.Empty <ModelValidationResult>()); }
public void Validate(ModelValidationContext validationContext) { Validate(validationContext, parentNode: null); }
private static IEnumerable<IModelValidator> GetValidators(ModelValidationContext validationContext, ModelMetadata metadata) { return validationContext.ValidatorProviders.SelectMany(vp => vp.GetValidators(metadata)); }
public void NullCheckFailedHandler_ModelStateAlreadyInvalid_DoesNothing() { // Arrange var modelState = new ModelStateDictionary(); modelState.AddModelError("foo.bar", "Some existing error."); var modelMetadata = GetMetadataForType(typeof(Person)); var validationNode = new ModelValidationNode(modelMetadata, "foo"); var validationContext = new ModelValidationContext(new DataAnnotationsModelMetadataProvider(), Enumerable.Empty<IModelValidatorProvider>(), modelState, modelMetadata, null); var e = new ModelValidatedEventArgs(validationContext, parentNode: null); // Act var handler = MutableObjectModelBinder.CreateNullCheckFailedHandler(modelMetadata, incomingValue: null); handler(validationNode, e); // Assert Assert.False(modelState.ContainsKey("foo")); }
public void ProcessDto_BindRequiredFieldNull_RaisesModelError() { // Arrange var model = new ModelWithBindRequired { Name = "original value", Age = -20 }; var containerMetadata = GetMetadataForObject(model); var bindingContext = new ModelBindingContext() { ModelMetadata = containerMetadata, ModelName = "theModel", ModelState = new ModelStateDictionary(), ValidatorProviders = Enumerable.Empty<IModelValidatorProvider>() }; var validationContext = new ModelValidationContext(new EmptyModelMetadataProvider(), bindingContext.ValidatorProviders, bindingContext.ModelState, containerMetadata, null); var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties); var testableBinder = new TestableMutableObjectModelBinder(); var propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Name"); dto.Results[propertyMetadata] = new ComplexModelDtoResult("John Doe", new ModelValidationNode(propertyMetadata, "theModel.Name")); // Attempt to set non-Nullable property to null. BindRequiredAttribute should not be relevant in this // case because the binding exists. propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Age"); dto.Results[propertyMetadata] = new ComplexModelDtoResult(null, new ModelValidationNode(propertyMetadata, "theModel.Age")); // Act; must also Validate because null-check error handler is late-bound testableBinder.ProcessDto(bindingContext, dto); bindingContext.ValidationNode.Validate(validationContext); // Assert var modelStateDictionary = bindingContext.ModelState; Assert.Equal(false, modelStateDictionary.IsValid); Assert.Equal(2, modelStateDictionary.Count); // Check Name field ModelState modelState; Assert.True(modelStateDictionary.TryGetValue("theModel.Name", out modelState)); Assert.Equal(0, modelState.Errors.Count); Assert.Equal(ModelValidationState.Valid, modelState.ValidationState); // Check Age error. Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState)); Assert.Equal(1, modelState.Errors.Count); Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState); var modelError = modelState.Errors[0]; Assert.Null(modelError.Exception); Assert.NotNull(modelError.ErrorMessage); Assert.Equal("A value is required.", modelError.ErrorMessage); }
public IEnumerable <ModelValidationResult> Validate(ModelValidationContext context) { return(Enumerable.Empty <ModelValidationResult>()); }
public ModelValidatingEventArgs([NotNull] ModelValidationContext validationContext, [NotNull] ModelValidationNode parentNode) { ValidationContext = validationContext; ParentNode = parentNode; }
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context) { return Enumerable.Empty<ModelValidationResult>(); }
protected virtual void SetProperty( ModelBindingContext bindingContext, ModelExplorer modelExplorer, ModelMetadata propertyMetadata, ModelBindingResult dtoResult, IModelValidator requiredValidator) { var bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase; var property = bindingContext.ModelType.GetProperty( propertyMetadata.PropertyName, bindingFlags); if (property == null || !property.CanWrite) { // nothing to do return; } object value; var hasDefaultValue = false; if (dtoResult.IsModelSet) { value = dtoResult.Model; } else { hasDefaultValue = TryGetPropertyDefaultValue(property, out value); } // 'Required' validators need to run first so that we can provide useful error messages if // the property setters throw, e.g. if we're setting entity keys to null. if (value == null) { var modelStateKey = dtoResult.Key; var validationState = bindingContext.ModelState.GetFieldValidationState(modelStateKey); if (validationState == ModelValidationState.Unvalidated) { if (requiredValidator != null) { var propertyExplorer = modelExplorer.GetExplorerForExpression(propertyMetadata, model: null); var validationContext = new ModelValidationContext(bindingContext, propertyExplorer); foreach (var validationResult in requiredValidator.Validate(validationContext)) { bindingContext.ModelState.TryAddModelError(modelStateKey, validationResult.Message); } } } } if (!dtoResult.IsModelSet && !hasDefaultValue) { // If we don't have a value, don't set it on the model and trounce a pre-initialized // value. return; } if (value != null || property.PropertyType.AllowsNullValue()) { try { property.SetValue(bindingContext.Model, value); } catch (Exception ex) { // don't display a duplicate error message if a binding error has already occurred for this field var targetInvocationException = ex as TargetInvocationException; if (targetInvocationException != null && targetInvocationException.InnerException != null) { ex = targetInvocationException.InnerException; } var modelStateKey = dtoResult.Key; var validationState = bindingContext.ModelState.GetFieldValidationState(modelStateKey); if (validationState == ModelValidationState.Unvalidated) { bindingContext.ModelState.AddModelError(modelStateKey, ex); } } } else { // trying to set a non-nullable value type to null, need to make sure there's a message var modelStateKey = dtoResult.Key; var validationState = bindingContext.ModelState.GetFieldValidationState(modelStateKey); if (validationState == ModelValidationState.Unvalidated) { var errorMessage = Resources.ModelBinderConfig_ValueRequired; bindingContext.ModelState.TryAddModelError(modelStateKey, errorMessage); } } }
public IEnumerable <ModelValidationResult> Validate(ModelValidationContext context) { throw new InvalidOperationException(_errorMessage); }
public void ProcessDto_BindRequiredFieldNull_RaisesModelError() { // Arrange var model = new ModelWithBindRequired { Name = "original value", Age = -20 }; var containerMetadata = GetMetadataForObject(model); var bindingContext = new ModelBindingContext() { ModelMetadata = containerMetadata, ModelName = "theModel", ModelState = new ModelStateDictionary(), ValidatorProvider = Mock.Of <IModelValidatorProvider>() }; var validationContext = new ModelValidationContext(new EmptyModelMetadataProvider(), bindingContext.ValidatorProvider, bindingContext.ModelState, containerMetadata, null); var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties); var testableBinder = new TestableMutableObjectModelBinder(); var propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Name"); dto.Results[propertyMetadata] = new ComplexModelDtoResult("John Doe", new ModelValidationNode(propertyMetadata, "theModel.Name")); // Attempt to set non-Nullable property to null. BindRequiredAttribute should not be relevant in this // case because the binding exists. propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Age"); dto.Results[propertyMetadata] = new ComplexModelDtoResult(null, new ModelValidationNode(propertyMetadata, "theModel.Age")); // Act; must also Validate because null-check error handler is late-bound testableBinder.ProcessDto(bindingContext, dto); bindingContext.ValidationNode.Validate(validationContext); // Assert var modelStateDictionary = bindingContext.ModelState; Assert.Equal(false, modelStateDictionary.IsValid); Assert.Equal(2, modelStateDictionary.Count); // Check Name field ModelState modelState; Assert.True(modelStateDictionary.TryGetValue("theModel.Name", out modelState)); Assert.Equal(0, modelState.Errors.Count); Assert.Equal(ModelValidationState.Valid, modelState.ValidationState); // Check Age error. Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState)); Assert.Equal(1, modelState.Errors.Count); Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState); var modelError = modelState.Errors[0]; Assert.Null(modelError.Exception); Assert.NotNull(modelError.ErrorMessage); Assert.Equal("A value is required.", modelError.ErrorMessage); }
protected virtual void SetProperty(ModelBindingContext bindingContext, ModelMetadata propertyMetadata, ComplexModelDtoResult dtoResult, IModelValidator requiredValidator) { var bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase; var property = bindingContext.ModelType .GetProperty(propertyMetadata.PropertyName, bindingFlags); if (property == null || !property.CanWrite) { // nothing to do return; } var value = dtoResult.Model ?? GetPropertyDefaultValue(property); propertyMetadata.Model = value; // 'Required' validators need to run first so that we can provide useful error messages if // the property setters throw, e.g. if we're setting entity keys to null. if (value == null) { var modelStateKey = dtoResult.ValidationNode.ModelStateKey; var validationState = bindingContext.ModelState.GetFieldValidationState(modelStateKey); if (validationState == ModelValidationState.Unvalidated) { if (requiredValidator != null) { var validationContext = new ModelValidationContext(bindingContext, propertyMetadata); foreach (var validationResult in requiredValidator.Validate(validationContext)) { bindingContext.ModelState.AddModelError(modelStateKey, validationResult.Message); } } } } if (value != null || property.PropertyType.AllowsNullValue()) { try { property.SetValue(bindingContext.Model, value); } catch (Exception ex) { // don't display a duplicate error message if a binding error has already occurred for this field var targetInvocationException = ex as TargetInvocationException; if (targetInvocationException != null && targetInvocationException.InnerException != null) { ex = targetInvocationException.InnerException; } var modelStateKey = dtoResult.ValidationNode.ModelStateKey; var validationState = bindingContext.ModelState.GetFieldValidationState(modelStateKey); if (validationState == ModelValidationState.Unvalidated) { bindingContext.ModelState.AddModelError(modelStateKey, ex); } } } else { // trying to set a non-nullable value type to null, need to make sure there's a message var modelStateKey = dtoResult.ValidationNode.ModelStateKey; var validationState = bindingContext.ModelState.GetFieldValidationState(modelStateKey); if (validationState == ModelValidationState.Unvalidated) { dtoResult.ValidationNode.Validated += CreateNullCheckFailedHandler(propertyMetadata, value); } } }
// Returns true if validator execution adds a model error. private static bool RunValidator(IModelValidator validator, ModelBindingContext bindingContext, ModelMetadata propertyMetadata, string modelStateKey) { var validationContext = new ModelValidationContext(bindingContext, propertyMetadata); var addedError = false; foreach (var validationResult in validator.Validate(validationContext)) { bindingContext.ModelState.AddModelError(modelStateKey, validationResult.Message); addedError = true; } if (!addedError) { bindingContext.ModelState.MarkFieldValid(modelStateKey); } return addedError; }
protected virtual void SetProperty(ModelBindingContext bindingContext, ModelMetadata propertyMetadata, ComplexModelDtoResult dtoResult, IModelValidator requiredValidator) { var bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase; var property = bindingContext.ModelType .GetProperty(propertyMetadata.PropertyName, bindingFlags); if (property == null || !property.CanWrite) { // nothing to do return; } var value = dtoResult.Model ?? GetPropertyDefaultValue(property); propertyMetadata.Model = value; // 'Required' validators need to run first so that we can provide useful error messages if // the property setters throw, e.g. if we're setting entity keys to null. if (value == null) { var modelStateKey = dtoResult.ValidationNode.ModelStateKey; var validationState = bindingContext.ModelState.GetFieldValidationState(modelStateKey); if (validationState == ModelValidationState.Unvalidated) { if (requiredValidator != null) { var validationContext = new ModelValidationContext(bindingContext, propertyMetadata); foreach (var validationResult in requiredValidator.Validate(validationContext)) { bindingContext.ModelState.TryAddModelError(modelStateKey, validationResult.Message); } } } } if (value != null || property.PropertyType.AllowsNullValue()) { try { property.SetValue(bindingContext.Model, value); } catch (Exception ex) { // don't display a duplicate error message if a binding error has already occurred for this field var targetInvocationException = ex as TargetInvocationException; if (targetInvocationException != null && targetInvocationException.InnerException != null) { ex = targetInvocationException.InnerException; } var modelStateKey = dtoResult.ValidationNode.ModelStateKey; var validationState = bindingContext.ModelState.GetFieldValidationState(modelStateKey); if (validationState == ModelValidationState.Unvalidated) { bindingContext.ModelState.AddModelError(modelStateKey, ex); } } } else { // trying to set a non-nullable value type to null, need to make sure there's a message var modelStateKey = dtoResult.ValidationNode.ModelStateKey; var validationState = bindingContext.ModelState.GetFieldValidationState(modelStateKey); if (validationState == ModelValidationState.Unvalidated) { dtoResult.ValidationNode.Validated += CreateNullCheckFailedHandler(propertyMetadata, value); } } }
public void NullCheckFailedHandler_ModelStateValid_AddsErrorString() { // Arrange var modelState = new ModelStateDictionary(); var modelMetadata = GetMetadataForType(typeof(Person)); var validationNode = new ModelValidationNode(modelMetadata, "foo"); var validationContext = new ModelValidationContext(new DataAnnotationsModelMetadataProvider(), Enumerable.Empty<IModelValidatorProvider>(), modelState, modelMetadata, null); var e = new ModelValidatedEventArgs(validationContext, parentNode: null); // Act var handler = MutableObjectModelBinder.CreateNullCheckFailedHandler(modelMetadata, incomingValue: null); handler(validationNode, e); // Assert Assert.True(modelState.ContainsKey("foo")); Assert.Equal("A value is required.", modelState["foo"].Errors[0].ErrorMessage); }
private void ValidateProperties(ModelValidationContext validationContext) { var modelState = validationContext.ModelState; var model = ModelMetadata.Model; var updatedMetadata = validationContext.MetadataProvider.GetMetadataForType(() => model, ModelMetadata.ModelType); foreach (var propertyMetadata in updatedMetadata.Properties) { // Only want to add errors to ModelState if something doesn't already exist for the property node, // else we could end up with duplicate or irrelevant error messages. var propertyKeyRoot = ModelBindingHelper.CreatePropertyModelName(ModelStateKey, propertyMetadata.PropertyName); if (modelState.GetFieldValidationState(propertyKeyRoot) == ModelValidationState.Unvalidated) { var propertyValidators = GetValidators(validationContext, propertyMetadata); var propertyValidationContext = new ModelValidationContext(validationContext, propertyMetadata); foreach (var propertyValidator in propertyValidators) { foreach (var propertyResult in propertyValidator.Validate(propertyValidationContext)) { var thisErrorKey = ModelBindingHelper.CreatePropertyModelName(propertyKeyRoot, propertyResult.MemberName); modelState.AddModelError(thisErrorKey, propertyResult.Message); } } } } }
private void ValidateThis(ModelValidationContext validationContext, ModelValidationNode parentNode) { var modelState = validationContext.ModelState; if (modelState.GetFieldValidationState(ModelStateKey) == ModelValidationState.Invalid) { // If any item in the key's subtree has been identified as invalid, short-circuit return; } // If the Model at the current node is null and there is no parent, we cannot validate, and the // DataAnnotationsModelValidator will throw. So we intercept here to provide a catch-all value-required // validation error var modelStateKey = ModelBindingHelper.CreatePropertyModelName(ModelStateKey, ModelMetadata.GetDisplayName()); if (parentNode == null && ModelMetadata.Model == null) { modelState.AddModelError(modelStateKey, Resources.Validation_ValueNotFound); return; } var container = TryConvertContainerToMetadataType(parentNode); var validators = GetValidators(validationContext, ModelMetadata).ToArray(); for (var i = 0; i < validators.Length; i++) { var validator = validators[i]; foreach (var validationResult in validator.Validate(validationContext)) { var currentModelStateKey = ModelBindingHelper.CreatePropertyModelName(ModelStateKey, validationResult.MemberName); modelState.AddModelError(currentModelStateKey, validationResult.Message); } } }
private static IEnumerable <IModelValidator> GetValidators(ModelValidationContext validationContext, ModelMetadata metadata) { return(validationContext.ValidatorProviders.SelectMany(vp => vp.GetValidators(metadata))); }
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context) { throw new InvalidOperationException(_errorMessage); }