Ejemplo n.º 1
0
        public void ProcessDto_Success()
        {
            // Arrange
            var dob   = new DateTime(2001, 1, 1);
            var model = new PersonWithBindExclusion
            {
                DateOfBirth = dob
            };
            var containerMetadata = GetMetadataForObject(model);

            var bindingContext = CreateContext(containerMetadata);
            var dto            = new ComplexModelDto(containerMetadata, containerMetadata.Properties);

            var firstNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "FirstName");

            dto.Results[firstNameProperty] = new ComplexModelDtoResult("John", new ModelValidationNode(firstNameProperty, ""));
            var lastNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "LastName");

            dto.Results[lastNameProperty] = new ComplexModelDtoResult("Doe", new ModelValidationNode(lastNameProperty, ""));
            var dobProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "DateOfBirth");

            dto.Results[dobProperty] = null;

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            Assert.Equal("John", model.FirstName);
            Assert.Equal("Doe", model.LastName);
            Assert.Equal(dob, model.DateOfBirth);
            Assert.Equal(true, bindingContext.ModelState.IsValid);
        }
Ejemplo n.º 2
0
        internal void ProcessDto(ModelBindingContext bindingContext, ComplexModelDto dto)
        {
            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer    = metadataProvider.GetModelExplorerForType(bindingContext.ModelType, bindingContext.Model);

            var validationInfo = GetPropertyValidationInfo(bindingContext);

            // Eliminate provided properties from requiredProperties; leaving just *missing* required properties.
            var boundProperties = dto.Results.Where(p => p.Value.IsModelSet).Select(p => p.Key.PropertyName);

            validationInfo.RequiredProperties.ExceptWith(boundProperties);

            foreach (var missingRequiredProperty in validationInfo.RequiredProperties)
            {
                var addedError = false;

                // We want to provide the 'null' value, not the value of model.Property,
                // so avoiding modelExplorer.GetProperty here which would call the actual getter on the
                // model. This avoids issues with value types, or properties with pre-initialized values.
                var propertyExplorer = modelExplorer.GetExplorerForProperty(missingRequiredProperty, model: null);

                var propertyName  = propertyExplorer.Metadata.BinderModelName ?? missingRequiredProperty;
                var modelStateKey = ModelBindingHelper.CreatePropertyModelName(
                    bindingContext.ModelName,
                    propertyName);

                // Execute validator (if any) to get custom error message.
                IModelValidator validator;
                if (validationInfo.RequiredValidators.TryGetValue(missingRequiredProperty, out validator))
                {
                    addedError = RunValidator(validator, bindingContext, propertyExplorer, modelStateKey);
                }

                // Fall back to default message if BindingBehaviorAttribute required this property or validator
                // (oddly) succeeded.
                if (!addedError)
                {
                    bindingContext.ModelState.TryAddModelError(
                        modelStateKey,
                        Resources.FormatMissingRequiredMember(propertyName));
                }
            }

            // For each property that ComplexModelDtoModelBinder attempted to bind, call the setter, recording
            // exceptions as necessary.
            foreach (var entry in dto.Results)
            {
                var dtoResult = entry.Value;
                if (dtoResult != null)
                {
                    var             propertyMetadata = entry.Key;
                    IModelValidator requiredValidator;
                    validationInfo.RequiredValidators.TryGetValue(
                        propertyMetadata.PropertyName,
                        out requiredValidator);

                    SetProperty(bindingContext, modelExplorer, propertyMetadata, dtoResult, requiredValidator);
                }
            }
        }
Ejemplo n.º 3
0
        public void ProcessDto_RequiredFieldMissing_RaisesModelErrorWithMessage()
        {
            // Arrange
            var model             = new Person();
            var containerMetadata = GetMetadataForObject(model);
            var bindingContext    = CreateContext(containerMetadata);

            // Set no properties though ValueTypeRequired (a non-Nullable struct) property is required.
            var dto            = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;

            Assert.Equal(false, modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check ValueTypeRequired error.
            ModelState modelState;

            Assert.True(modelStateDictionary.TryGetValue("theModel.ValueTypeRequired", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            var modelError = modelState.Errors[0];

            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("Sample message", modelError.ErrorMessage);
        }
Ejemplo n.º 4
0
        internal void ProcessDto(ModelBindingContext bindingContext, ComplexModelDto dto)
        {
            var validationInfo = GetPropertyValidationInfo(bindingContext);

            // Eliminate provided properties from requiredProperties; leaving just *missing* required properties.
            var boundProperties = dto.Results.Where(p => p.Value.IsModelBound).Select(p => p.Key.PropertyName);

            validationInfo.RequiredProperties.ExceptWith(boundProperties);

            foreach (var missingRequiredProperty in validationInfo.RequiredProperties)
            {
                var addedError    = false;
                var modelStateKey = ModelBindingHelper.CreatePropertyModelName(
                    bindingContext.ValidationNode.ModelStateKey, missingRequiredProperty);

                // Update Model as SetProperty() would: Place null value where validator will check for non-null. This
                // ensures a failure result from a required validator (if any) even for a non-nullable property.
                // (Otherwise, propertyMetadata.Model is likely already null.)
                var propertyMetadata = bindingContext.PropertyMetadata[missingRequiredProperty];
                propertyMetadata.Model = null;

                // Execute validator (if any) to get custom error message.
                IModelValidator validator;
                if (validationInfo.RequiredValidators.TryGetValue(missingRequiredProperty, out validator))
                {
                    addedError = RunValidator(validator, bindingContext, propertyMetadata, modelStateKey);
                }

                // Fall back to default message if BindingBehaviorAttribute required this property or validator
                // (oddly) succeeded.
                if (!addedError)
                {
                    bindingContext.ModelState.TryAddModelError(
                        modelStateKey,
                        Resources.FormatMissingRequiredMember(missingRequiredProperty));
                }
            }

            // for each property that was bound, call the setter, recording exceptions as necessary
            foreach (var entry in dto.Results)
            {
                var propertyMetadata = entry.Key;
                var dtoResult        = entry.Value;
                if (dtoResult != null)
                {
                    IModelValidator requiredValidator;
                    validationInfo.RequiredValidators.TryGetValue(propertyMetadata.PropertyName,
                                                                  out requiredValidator);
                    SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
                    bindingContext.ValidationNode.ChildNodes.Add(dtoResult.ValidationNode);
                }
            }
        }
Ejemplo n.º 5
0
        // Internal for testing.
        internal ModelValidationNode ProcessDto(
            ModelBindingContext bindingContext,
            ComplexModelDto dto,
            ModelValidationNode validationNode)
        {
            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer    = metadataProvider.GetModelExplorerForType(bindingContext.ModelType, bindingContext.Model);
            var validationInfo   = GetPropertyValidationInfo(bindingContext);

            // Eliminate provided properties from RequiredProperties; leaving just *missing* required properties.
            var boundProperties = dto.Results.Where(p => p.Value.IsModelSet).Select(p => p.Key.PropertyName);

            validationInfo.RequiredProperties.ExceptWith(boundProperties);

            foreach (var missingRequiredProperty in validationInfo.RequiredProperties)
            {
                var propertyExplorer = modelExplorer.GetExplorerForProperty(missingRequiredProperty);
                var propertyName     = propertyExplorer.Metadata.BinderModelName ?? missingRequiredProperty;
                var modelStateKey    = ModelNames.CreatePropertyModelName(bindingContext.ModelName, propertyName);

                bindingContext.ModelState.TryAddModelError(
                    modelStateKey,
                    Resources.FormatModelBinding_MissingBindRequiredMember(propertyName));
            }

            // For each property that ComplexModelDtoModelBinder attempted to bind, call the setter, recording
            // exceptions as necessary.
            foreach (var entry in dto.Results)
            {
                var dtoResult = entry.Value;
                if (dtoResult != null)
                {
                    var propertyMetadata = entry.Key;
                    SetProperty(bindingContext, modelExplorer, propertyMetadata, dtoResult);

                    var dtoValidationNode = dtoResult.ValidationNode;
                    if (dtoValidationNode == null)
                    {
                        // Make sure that irrespective of if the properties of the model were bound with a value,
                        // create a validation node so that these get validated.
                        dtoValidationNode = new ModelValidationNode(dtoResult.Key, entry.Key, dtoResult.Model);
                    }

                    validationNode.ChildNodes.Add(dtoValidationNode);
                }
            }

            return(validationNode);
        }
Ejemplo n.º 6
0
        private async Task <ComplexModelDto> CreateAndPopulateDto(ModelBindingContext bindingContext,
                                                                  IEnumerable <ModelMetadata> propertyMetadatas)
        {
            // create a DTO and call into the DTO binder
            var originalDto             = new ComplexModelDto(bindingContext.ModelMetadata, propertyMetadatas);
            var complexModelDtoMetadata =
                bindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(() => originalDto,
                                                                                           typeof(ComplexModelDto));
            var dtoBindingContext =
                new ModelBindingContext(bindingContext, bindingContext.ModelName, complexModelDtoMetadata);

            await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(dtoBindingContext);

            return((ComplexModelDto)dtoBindingContext.Model);
        }
Ejemplo n.º 7
0
        public void ProcessDto_BindRequiredFieldMissing_RaisesModelError()
        {
            // Arrange
            var model = new ModelWithBindRequired
            {
                Name = "original value",
                Age  = -20
            };

            var containerMetadata = GetMetadataForObject(model);
            var bindingContext    = new ModelBindingContext
            {
                ModelMetadata     = containerMetadata,
                ModelName         = "theModel",
                ValidatorProvider = Mock.Of <IModelValidatorProvider>()
            };
            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);

            var nameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "Name");

            dto.Results[nameProperty] = new ComplexModelDtoResult("John Doe", new ModelValidationNode(nameProperty, ""));

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;

            Assert.Equal(false, modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check Age error.
            ModelState modelState;

            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            var modelError = modelState.Errors[0];

            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("The 'Age' property is required.", modelError.ErrorMessage);
        }
Ejemplo n.º 8
0
        public void ProcessDto_RequiredFieldMissing_RaisesModelError()
        {
            // Arrange
            var model             = new ModelWithRequired();
            var containerMetadata = GetMetadataForObject(model);
            var bindingContext    = CreateContext(containerMetadata);

            // Set no properties though Age (a non-Nullable struct) and City (a class) properties are required.
            var dto            = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;

            Assert.Equal(false, modelStateDictionary.IsValid);
            Assert.Equal(2, modelStateDictionary.Count);

            // Check Age error.
            ModelState modelState;

            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            var modelError = modelState.Errors[0];

            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            var expected = ValidationAttributeUtil.GetRequiredErrorMessage(nameof(ModelWithRequired.Age));

            Assert.Equal(expected, modelError.ErrorMessage);

            // Check City error.
            Assert.True(modelStateDictionary.TryGetValue("theModel.City", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            expected = ValidationAttributeUtil.GetRequiredErrorMessage(nameof(ModelWithRequired.City));
            Assert.Equal(expected, modelError.ErrorMessage);
        }
Ejemplo n.º 9
0
        private async Task <ModelBindingResult> CreateAndPopulateDto(
            ModelBindingContext bindingContext,
            IEnumerable <ModelMetadata> propertyMetadatas)
        {
            // create a DTO and call into the DTO binder
            var dto = new ComplexModelDto(bindingContext.ModelMetadata, propertyMetadatas);

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var dtoMetadata      = metadataProvider.GetMetadataForType(typeof(ComplexModelDto));

            var childContext = ModelBindingContext.GetChildModelBindingContext(
                bindingContext,
                bindingContext.ModelName,
                dtoMetadata);

            childContext.Model = dto;

            return(await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(childContext));
        }
Ejemplo n.º 10
0
        public void ProcessDto_RequiredFieldNull_RaisesModelError()
        {
            // Arrange
            var model             = new ModelWithRequired();
            var containerMetadata = GetMetadataForObject(model);
            var bindingContext    = CreateContext(containerMetadata);

            var dto            = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Make Age valid and City invalid.
            var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "Age");

            dto.Results[propertyMetadata] =
                new ComplexModelDtoResult(23, new ModelValidationNode(propertyMetadata, "theModel.Age"));
            propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "City");
            dto.Results[propertyMetadata] =
                new ComplexModelDtoResult(null, new ModelValidationNode(propertyMetadata, "theModel.City"));

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;

            Assert.Equal(false, modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check City error.
            ModelState modelState;

            Assert.True(modelStateDictionary.TryGetValue("theModel.City", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            var modelError = modelState.Errors[0];

            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            var expected = ValidationAttributeUtil.GetRequiredErrorMessage(nameof(ModelWithRequired.City));

            Assert.Equal(expected, modelError.ErrorMessage);
        }
Ejemplo n.º 11
0
        public void ProcessDto_RequiredFieldNull_RaisesModelErrorWithMessage()
        {
            // Arrange
            var model             = new Person();
            var containerMetadata = GetMetadataForObject(model);

            var bindingContext = CreateContext(containerMetadata);

            var dto            = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Make ValueTypeRequired invalid.
            var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "ValueTypeRequired");

            dto.Results[propertyMetadata] =
                new ComplexModelDtoResult(null, new ModelValidationNode(propertyMetadata, "theModel.ValueTypeRequired"));

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;

            Assert.Equal(false, modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check ValueTypeRequired error.
            ModelState modelState;

            Assert.True(modelStateDictionary.TryGetValue("theModel.ValueTypeRequired", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            var modelError = modelState.Errors[0];

            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("Sample message", modelError.ErrorMessage);
        }
        public void ProcessDto_Success()
        {
            // Arrange
            var dob = new DateTime(2001, 1, 1);
            var model = new PersonWithBindExclusion
            {
                DateOfBirth = dob
            };
            var containerMetadata = GetMetadataForType(model.GetType());

            var bindingContext = CreateContext(containerMetadata, model);
            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);

            var firstNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "FirstName");
            dto.Results[firstNameProperty] = new ModelBindingResult(
                "John",
                isModelSet: true,
                key: "");

            var lastNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "LastName");
            dto.Results[lastNameProperty] = new ModelBindingResult(
                "Doe",
                isModelSet: true,
                key: "");

            var dobProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "DateOfBirth");
            dto.Results[dobProperty] = null;
            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(bindingContext, dto, modelValidationNode);

            // Assert
            Assert.Equal("John", model.FirstName);
            Assert.Equal("Doe", model.LastName);
            Assert.Equal(dob, model.DateOfBirth);
            Assert.True(bindingContext.ModelState.IsValid);

            // Ensure that we add child nodes for all the nodes which have a result (irrespective of if they
            // are bound or not).
            Assert.Equal(2, modelValidationNode.ChildNodes.Count());

            var validationNode = modelValidationNode.ChildNodes[0];
            Assert.Equal("", validationNode.Key);
            Assert.Equal("John", validationNode.Model);

            validationNode = modelValidationNode.ChildNodes[1];
            Assert.Equal("", validationNode.Key);
            Assert.Equal("Doe", validationNode.Model);
        }
Ejemplo n.º 13
0
        internal void ProcessDto(ModelBindingContext bindingContext, ComplexModelDto dto)
        {
            var validationInfo = GetPropertyValidationInfo(bindingContext);

            // Eliminate provided properties from requiredProperties; leaving just *missing* required properties.
            validationInfo.RequiredProperties.ExceptWith(dto.Results.Select(r => r.Key.PropertyName));

            foreach (var missingRequiredProperty in validationInfo.RequiredProperties)
            {
                var addedError = false;
                var modelStateKey = ModelBindingHelper.CreatePropertyModelName(
                    bindingContext.ValidationNode.ModelStateKey, missingRequiredProperty);

                // Update Model as SetProperty() would: Place null value where validator will check for non-null. This
                // ensures a failure result from a required validator (if any) even for a non-nullable property.
                // (Otherwise, propertyMetadata.Model is likely already null.)
                var propertyMetadata = bindingContext.PropertyMetadata[missingRequiredProperty];
                propertyMetadata.Model = null;

                // Execute validator (if any) to get custom error message.
                IModelValidator validator;
                if (validationInfo.RequiredValidators.TryGetValue(missingRequiredProperty, out validator))
                {
                    addedError = RunValidator(validator, bindingContext, propertyMetadata, modelStateKey);
                }

                // Fall back to default message if BindingBehaviorAttribute required this property or validator
                // (oddly) succeeded.
                if (!addedError)
                {
                    bindingContext.ModelState.AddModelError(
                        modelStateKey,
                        Resources.FormatMissingRequiredMember(missingRequiredProperty));
                }
            }

            // for each property that was bound, call the setter, recording exceptions as necessary
            foreach (var entry in dto.Results)
            {
                var propertyMetadata = entry.Key;
                var dtoResult = entry.Value;
                if (dtoResult != null)
                {
                    IModelValidator requiredValidator;
                    validationInfo.RequiredValidators.TryGetValue(propertyMetadata.PropertyName,
                                                                  out requiredValidator);
                    SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
                    bindingContext.ValidationNode.ChildNodes.Add(dtoResult.ValidationNode);
                }
            }
        }
        public void ProcessDto_PropertyWithRequiredAttribute_NoPropertiesSet_NoError()
        {
            // Arrange
            var model = new Person();
            var containerMetadata = GetMetadataForType(model.GetType());
            var bindingContext = CreateContext(containerMetadata, model);

            // Set no properties though ValueTypeRequired (a non-Nullable struct) property is required.
            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();
            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);

            // Act
            testableBinder.ProcessDto(bindingContext, dto, modelValidationNode);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.True(modelStateDictionary.IsValid);
        }
Ejemplo n.º 15
0
        public void ProcessDto_Success()
        {
            // Arrange
            var dob = new DateTime(2001, 1, 1);
            var model = new PersonWithBindExclusion
            {
                DateOfBirth = dob
            };
            var containerMetadata = GetMetadataForObject(model);

            var bindingContext = CreateContext(containerMetadata);
            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);

            var firstNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "FirstName");
            dto.Results[firstNameProperty] = new ComplexModelDtoResult("John", new ModelValidationNode(firstNameProperty, ""));
            var lastNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "LastName");
            dto.Results[lastNameProperty] = new ComplexModelDtoResult("Doe", new ModelValidationNode(lastNameProperty, ""));
            var dobProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "DateOfBirth");
            dto.Results[dobProperty] = null;

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            Assert.Equal("John", model.FirstName);
            Assert.Equal("Doe", model.LastName);
            Assert.Equal(dob, model.DateOfBirth);
            Assert.Equal(true, bindingContext.ModelState.IsValid);
        }
        public void ProcessDto_ValueTypeProperty_NoValue_NoError()
        {
            // Arrange
            var model = new Person();
            var containerMetadata = GetMetadataForType(model.GetType());

            var bindingContext = CreateContext(containerMetadata, model);
            var modelStateDictionary = bindingContext.ModelState;

            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Make ValueTypeRequired invalid.
            var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == nameof(Person.ValueTypeRequired));
            dto.Results[propertyMetadata] = new ModelBindingResult(
                null,
                isModelSet: false,
                key: "theModel." + nameof(Person.ValueTypeRequired));

            // Make ValueTypeRequiredWithDefaultValue invalid
            propertyMetadata = dto.PropertyMetadata
                .Single(p => p.PropertyName == nameof(Person.ValueTypeRequiredWithDefaultValue));
            dto.Results[propertyMetadata] = new ModelBindingResult(
                model: null,
                isModelSet: false,
                key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue));

            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);

            // Act
            testableBinder.ProcessDto(bindingContext, dto, modelValidationNode);

            // Assert
            Assert.True(modelStateDictionary.IsValid);
            Assert.Empty(modelStateDictionary);
        }
        public void ProcessDto_RequiredFieldNull_RaisesModelError()
        {
            // Arrange
            var model = new ModelWithRequired();
            var containerMetadata = GetMetadataForType(model.GetType());
            var bindingContext = CreateContext(containerMetadata, model);

            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Make Age valid and City invalid.
            var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "Age");
            dto.Results[propertyMetadata] = new ModelBindingResult(
                23,
                isModelSet: true,
                key: "theModel.Age");

            propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "City");
            dto.Results[propertyMetadata] = new ModelBindingResult(
                null,
                isModelSet: true,
                key: "theModel.City");

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.False(modelStateDictionary.IsValid);
            Assert.Single(modelStateDictionary);

            // Check City error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel." + nameof(ModelWithRequired.City), out modelState));

            var modelError = Assert.Single(modelState.Errors);
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            var expected = ValidationAttributeUtil.GetRequiredErrorMessage(nameof(ModelWithRequired.City));
            Assert.Equal(expected, modelError.ErrorMessage);
        }
        public void ProcessDto_DataMemberIsRequiredFieldMissing_RaisesModelError()
        {
            // Arrange
            var model = new ModelWithDataMemberIsRequired
            {
                Name = "original value",
                Age = -20
            };

            var containerMetadata = GetMetadataForType(model.GetType());
            var bindingContext = new ModelBindingContext
            {
                Model = model,
                ModelMetadata = containerMetadata,
                ModelName = "theModel",
                OperationBindingContext = new OperationBindingContext
                {
                    MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
                    ValidatorProvider = Mock.Of<IModelValidatorProvider>()
                }
            };
            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);

            var nameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "Name");
            dto.Results[nameProperty] = new ModelBindingResult(
                "John Doe",
                isModelSet: true,
                key: "");

            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(bindingContext, dto, modelValidationNode);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.False(modelStateDictionary.IsValid);
            Assert.Single(modelStateDictionary);

            // Check Age error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState));
            var modelError = Assert.Single(modelState.Errors);
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("A value for the 'Age' property was not provided.", modelError.ErrorMessage);
        }
        public void ProcessDto_ProvideRequiredFields_Success()
        {
            // Arrange
            var model = new Person();
            var containerMetadata = GetMetadataForType(model.GetType());

            var bindingContext = CreateContext(containerMetadata, model);
            var modelStateDictionary = bindingContext.ModelState;

            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Make ValueTypeRequired valid.
            var propertyMetadata = dto.PropertyMetadata
                .Single(p => p.PropertyName == nameof(Person.ValueTypeRequired));
            dto.Results[propertyMetadata] = new ModelBindingResult(
                41,
                isModelSet: true,
                key: "theModel." + nameof(Person.ValueTypeRequired));

            // Make ValueTypeRequiredWithDefaultValue valid.
            propertyMetadata = dto.PropertyMetadata
                .Single(p => p.PropertyName == nameof(Person.ValueTypeRequiredWithDefaultValue));
            dto.Results[propertyMetadata] = new ModelBindingResult(
                model: 57,
                isModelSet: true,
                key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue));

            // Also remind ProcessDto about PropertyWithDefaultValue -- as ComplexModelDtoModelBinder would.
            propertyMetadata = dto.PropertyMetadata
                .Single(p => p.PropertyName == nameof(Person.PropertyWithDefaultValue));
            dto.Results[propertyMetadata] = new ModelBindingResult(
                model: null,
                isModelSet: false,
                key: "theModel." + nameof(Person.PropertyWithDefaultValue));
            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);

            // Act
            testableBinder.ProcessDto(bindingContext, dto, modelValidationNode);

            // Assert
            Assert.True(modelStateDictionary.IsValid);
            Assert.Empty(modelStateDictionary);

            // Model gets provided values.
            Assert.Equal(41, model.ValueTypeRequired);
            Assert.Equal(57, model.ValueTypeRequiredWithDefaultValue);
            Assert.Equal(0m, model.PropertyWithDefaultValue);     // [DefaultValue] has no effect
        }
        internal void ProcessDto(ModelBindingContext bindingContext, ComplexModelDto dto)
        {
            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer = metadataProvider.GetModelExplorerForType(bindingContext.ModelType, bindingContext.Model);

            var validationInfo = GetPropertyValidationInfo(bindingContext);

            // Eliminate provided properties from requiredProperties; leaving just *missing* required properties.
            var boundProperties = dto.Results.Where(p => p.Value.IsModelSet).Select(p => p.Key.PropertyName);
            validationInfo.RequiredProperties.ExceptWith(boundProperties);

            foreach (var missingRequiredProperty in validationInfo.RequiredProperties)
            {
                var addedError = false;

                // We want to provide the 'null' value, not the value of model.Property,
                // so avoiding modelExplorer.GetProperty here which would call the actual getter on the
                // model. This avoids issues with value types, or properties with pre-initialized values.
                var propertyExplorer = modelExplorer.GetExplorerForProperty(missingRequiredProperty, model: null);

                var propertyName = propertyExplorer.Metadata.BinderModelName ?? missingRequiredProperty;
                var modelStateKey = ModelBindingHelper.CreatePropertyModelName(
                    bindingContext.ModelName,
                    propertyName);

                // Execute validator (if any) to get custom error message.
                IModelValidator validator;
                if (validationInfo.RequiredValidators.TryGetValue(missingRequiredProperty, out validator))
                {
                    addedError = RunValidator(validator, bindingContext, propertyExplorer, modelStateKey);
                }

                // Fall back to default message if BindingBehaviorAttribute required this property or validator
                // (oddly) succeeded.
                if (!addedError)
                {
                    bindingContext.ModelState.TryAddModelError(
                        modelStateKey,
                        Resources.FormatMissingRequiredMember(propertyName));
                }
            }

            // For each property that ComplexModelDtoModelBinder attempted to bind, call the setter, recording
            // exceptions as necessary.
            foreach (var entry in dto.Results)
            {
                var dtoResult = entry.Value;
                if (dtoResult != null)
                {
                    var propertyMetadata = entry.Key;
                    IModelValidator requiredValidator;
                    validationInfo.RequiredValidators.TryGetValue(
                        propertyMetadata.PropertyName,
                        out requiredValidator);

                    SetProperty(bindingContext, modelExplorer, propertyMetadata, dtoResult, requiredValidator);
                }
            }
        }
        public void ProcessDto_ValueTypePropertyWithBindRequired_SetToNull_CapturesException()
        {
            // Arrange
            var model = new ModelWithBindRequired
            {
                Name = "original value",
                Age = -20
            };

            var containerMetadata = GetMetadataForType(model.GetType());
            var bindingContext = new ModelBindingContext()
            {
                Model = model,
                ModelMetadata = containerMetadata,
                ModelName = "theModel",
                ModelState = new ModelStateDictionary(),
                OperationBindingContext = new OperationBindingContext
                {
                    MetadataProvider = TestModelMetadataProvider.CreateDefaultProvider(),
                    ValidatorProvider = Mock.Of<IModelValidatorProvider>()
                }
            };

            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            var propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Name");
            dto.Results[propertyMetadata] = new ModelBindingResult(
                "John Doe",
                isModelSet: true,
                key: "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 ModelBindingResult(
                null,
                isModelSet: true,
                key: "theModel.Age");

            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);

            // Act
            testableBinder.ProcessDto(bindingContext, dto, modelValidationNode);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.False(modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check Age error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState));
            Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState);

            var modelError = Assert.Single(modelState.Errors);
            Assert.Equal(string.Empty, modelError.ErrorMessage);
            Assert.IsType<NullReferenceException>(modelError.Exception);
        }
        private async Task<ModelBindingResult> CreateAndPopulateDto(
            ModelBindingContext bindingContext,
            IEnumerable<ModelMetadata> propertyMetadatas)
        {
            // create a DTO and call into the DTO binder
            var dto = new ComplexModelDto(bindingContext.ModelMetadata, propertyMetadatas);

            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var dtoMetadata = metadataProvider.GetMetadataForType(typeof(ComplexModelDto));

            var childContext = ModelBindingContext.GetChildModelBindingContext(
                bindingContext,
                bindingContext.ModelName,
                dtoMetadata);

            childContext.Model = dto;

            return await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(childContext);
        }
        public void ProcessDto_MissingDataForRequiredFields_NoErrors()
        {
            // Arrange
            var model = new ModelWithRequired();
            var containerMetadata = GetMetadataForType(model.GetType());

            var bindingContext = CreateContext(containerMetadata, model);

            // Set no properties though Age (a non-Nullable struct) and City (a class) properties are required.
            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();
            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);

            // Act
            testableBinder.ProcessDto(bindingContext, dto, modelValidationNode);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.True(modelStateDictionary.IsValid);
            Assert.Empty(modelStateDictionary);
        }
        public void ProcessDto_ValueTypeProperty_TriesToSetNullModel_CapturesException()
        {
            // Arrange
            var model = new Person();
            var containerMetadata = GetMetadataForType(model.GetType());

            var bindingContext = CreateContext(containerMetadata, model);
            var modelStateDictionary = bindingContext.ModelState;

            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // The [DefaultValue] on ValueTypeRequiredWithDefaultValue is ignored by model binding.
            var expectedValue = 0;

            // Make ValueTypeRequired invalid.
            var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == nameof(Person.ValueTypeRequired));
            dto.Results[propertyMetadata] = new ModelBindingResult(
                null,
                isModelSet: true,
                key: "theModel." + nameof(Person.ValueTypeRequired));

            // Make ValueTypeRequiredWithDefaultValue invalid
            propertyMetadata = dto.PropertyMetadata
                .Single(p => p.PropertyName == nameof(Person.ValueTypeRequiredWithDefaultValue));
            dto.Results[propertyMetadata] = new ModelBindingResult(
                model: null,
                isModelSet: true,
                key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue));

            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);

            // Act
            testableBinder.ProcessDto(bindingContext, dto, modelValidationNode);

            // Assert
            Assert.False(modelStateDictionary.IsValid);

            // Check ValueTypeRequired error.
            var modelStateEntry = Assert.Single(
                modelStateDictionary,
                entry => entry.Key == "theModel." + nameof(Person.ValueTypeRequired));
            Assert.Equal("theModel." + nameof(Person.ValueTypeRequired), modelStateEntry.Key);

            var modelState = modelStateEntry.Value;
            Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState);

            var error = Assert.Single(modelState.Errors);
            Assert.Equal(string.Empty, error.ErrorMessage);
            Assert.IsType<NullReferenceException>(error.Exception);

            // Check ValueTypeRequiredWithDefaultValue error.
            modelStateEntry = Assert.Single(
                modelStateDictionary,
                entry => entry.Key == "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue));
            Assert.Equal("theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue), modelStateEntry.Key);

            modelState = modelStateEntry.Value;
            Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState);

            error = Assert.Single(modelState.Errors);
            Assert.Equal(string.Empty, error.ErrorMessage);
            Assert.IsType<NullReferenceException>(error.Exception);

            Assert.Equal(0, model.ValueTypeRequired);
            Assert.Equal(expectedValue, model.ValueTypeRequiredWithDefaultValue);
        }
Ejemplo n.º 25
0
        private ComplexModelDto CreateAndPopulateDto(ModelBindingContext bindingContext,
                                                     IEnumerable<ModelMetadata> propertyMetadatas)
        {
            // create a DTO and call into the DTO binder
            var originalDto = new ComplexModelDto(bindingContext.ModelMetadata, propertyMetadatas);
            var dtoBindingContext = new ModelBindingContext(bindingContext)
            {
                ModelMetadata = bindingContext.MetadataProvider.GetMetadataForType(() => originalDto,
                                                                                   typeof(ComplexModelDto)),
                ModelName = bindingContext.ModelName
            };

            bindingContext.ModelBinder.BindModelAsync(dtoBindingContext);
            return (ComplexModelDto)dtoBindingContext.Model;
        }
        public void ProcessDto_RequiredFieldMissing_RaisesModelError()
        {
            // Arrange
            var model = new ModelWithRequired();
            var containerMetadata = GetMetadataForType(model.GetType());

            var bindingContext = CreateContext(containerMetadata, model);

            // Set no properties though Age (a non-Nullable struct) and City (a class) properties are required.
            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.False(modelStateDictionary.IsValid);
            Assert.Equal(2, modelStateDictionary.Count);

            // Check Age error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel." + nameof(ModelWithRequired.Age), out modelState));

            var modelError = Assert.Single(modelState.Errors);
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            var expected = ValidationAttributeUtil.GetRequiredErrorMessage(nameof(ModelWithRequired.Age));
            Assert.Equal(expected, modelError.ErrorMessage);

            // Check City error.
            Assert.True(modelStateDictionary.TryGetValue("theModel." + nameof(ModelWithRequired.City), out modelState));

            modelError = Assert.Single(modelState.Errors);
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            expected = ValidationAttributeUtil.GetRequiredErrorMessage(nameof(ModelWithRequired.City));
            Assert.Equal(expected, modelError.ErrorMessage);
        }
        public void ProcessDto_ReferenceTypePropertyWithBindRequired_RequiredValidatorIgnored()
        {
            // Arrange
            var model = new ModelWithBindRequiredAndRequiredAttribute();
            var containerMetadata = GetMetadataForType(model.GetType());

            var bindingContext = CreateContext(containerMetadata, model);
            var modelStateDictionary = bindingContext.ModelState;

            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Make ValueTypeProperty have a value.
            var propertyMetadata = containerMetadata
                .Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ValueTypeProperty)];
            dto.Results[propertyMetadata] = new ModelBindingResult(
                17,
                isModelSet: true,
                key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ValueTypeProperty));

            // Make ReferenceTypeProperty not have a value.
            propertyMetadata = containerMetadata
                .Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty)];
            dto.Results[propertyMetadata] = new ModelBindingResult(
                model: null,
                isModelSet: false,
                key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty));

            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);

            // Act
            testableBinder.ProcessDto(bindingContext, dto, modelValidationNode);

            // Assert
            Assert.False(modelStateDictionary.IsValid);

            var entry = Assert.Single(
                modelStateDictionary,
                kvp => kvp.Key == "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty))
                .Value;
            var error = Assert.Single(entry.Errors);
            Assert.Null(error.Exception);
            Assert.Equal("A value for the 'ReferenceTypeProperty' property was not provided.", error.ErrorMessage);

            // Model gets provided values.
            Assert.Equal(17, model.ValueTypeProperty);
            Assert.Null(model.ReferenceTypeProperty);
        }
Ejemplo n.º 28
0
        public void ProcessDto_RequiredFieldNull_RaisesModelError()
        {
            // Arrange
            var model = new ModelWithRequired();
            var containerMetadata = GetMetadataForObject(model);
            var bindingContext = CreateContext(containerMetadata);

            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Make Age valid and City invalid.
            var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "Age");
            dto.Results[propertyMetadata] =
                new ComplexModelDtoResult(23, new ModelValidationNode(propertyMetadata, "theModel.Age"));
            propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "City");
            dto.Results[propertyMetadata] =
                new ComplexModelDtoResult(null, new ModelValidationNode(propertyMetadata, "theModel.City"));

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.Equal(false, modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check City error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel.City", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            var modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("The City field is required.", modelError.ErrorMessage);
        }
Ejemplo n.º 29
0
        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);
        }
Ejemplo n.º 30
0
        public void ProcessDto_RequiredFieldMissing_RaisesModelErrorWithMessage()
        {
            // Arrange
            var model = new Person();
            var containerMetadata = GetMetadataForObject(model);
            var bindingContext = CreateContext(containerMetadata);

            // Set no properties though ValueTypeRequired (a non-Nullable struct) property is required.
            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.Equal(false, modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check ValueTypeRequired error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel.ValueTypeRequired", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            var modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("Sample message", modelError.ErrorMessage);
        }
        public void ProcessDto_RequiredFieldMissing_RaisesModelErrorWithMessage()
        {
            // Arrange
            var model = new Person();
            var containerMetadata = GetMetadataForType(model.GetType());
            var bindingContext = CreateContext(containerMetadata, model);

            // Set no properties though ValueTypeRequired (a non-Nullable struct) property is required.
            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.False(modelStateDictionary.IsValid);
            Assert.Equal(2, modelStateDictionary.Count);

            // Check ValueTypeRequired error.
            var modelStateEntry = Assert.Single(
                modelStateDictionary,
                entry => entry.Key == "theModel." + nameof(Person.ValueTypeRequired));
            Assert.Equal("theModel." + nameof(Person.ValueTypeRequired), modelStateEntry.Key);

            var modelState = modelStateEntry.Value;
            Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState);

            var modelError = Assert.Single(modelState.Errors);
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("Sample message", modelError.ErrorMessage);

            // Check ValueTypeRequiredWithDefaultValue error.
            modelStateEntry = Assert.Single(
                modelStateDictionary,
                entry => entry.Key == "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue));
            Assert.Equal("theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue), modelStateEntry.Key);

            modelState = modelStateEntry.Value;
            Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState);

            modelError = Assert.Single(modelState.Errors);
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("Another sample message", modelError.ErrorMessage);
        }
Ejemplo n.º 32
0
        public void ProcessDto_RequiredFieldNull_RaisesModelErrorWithMessage()
        {
            // Arrange
            var model = new Person();
            var containerMetadata = GetMetadataForObject(model);

            var bindingContext = CreateContext(containerMetadata);

            ComplexModelDto dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            TestableMutableObjectModelBinder testableBinder = new TestableMutableObjectModelBinder();

            // Make ValueTypeRequired invalid.
            var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "ValueTypeRequired");
            dto.Results[propertyMetadata] =
                new ComplexModelDtoResult(null, new ModelValidationNode(propertyMetadata, "theModel.ValueTypeRequired"));

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            ModelStateDictionary modelStateDictionary = bindingContext.ModelState;
            Assert.Equal(false, modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check ValueTypeRequired error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel.ValueTypeRequired", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            ModelError modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("Sample message", modelError.ErrorMessage);
        }
Ejemplo n.º 33
0
        public void ProcessDto_BindRequiredFieldMissing_RaisesModelError()
        {
            // Arrange
            var model = new ModelWithBindRequired
            {
                Name = "original value",
                Age = -20
            };

            var containerMetadata = GetMetadataForObject(model);
            var bindingContext = new ModelBindingContext
            {
                ModelMetadata = containerMetadata,
                ModelName = "theModel",
                ValidatorProviders = Enumerable.Empty<IModelValidatorProvider>()
            };
            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);

            var nameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "Name");
            dto.Results[nameProperty] = new ComplexModelDtoResult("John Doe", new ModelValidationNode(nameProperty, ""));

            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.Equal(false, modelStateDictionary.IsValid);
            Assert.Equal(1, modelStateDictionary.Count);

            // Check Age error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            var modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("The 'Age' property is required.", modelError.ErrorMessage);
        }
        public void ProcessDto_ValueTypeProperty_WithRequiredAttribute_SetToNull_NoError()
        {
            // Arrange
            var model = new ModelWithRequired();
            var containerMetadata = GetMetadataForType(model.GetType());
            var bindingContext = CreateContext(containerMetadata, model);

            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Make Age valid and City invalid.
            var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "Age");
            dto.Results[propertyMetadata] = new ModelBindingResult(
                23,
                isModelSet: true,
                key: "theModel.Age");

            propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "City");
            dto.Results[propertyMetadata] = new ModelBindingResult(
                null,
                isModelSet: true,
                key: "theModel.City");
            var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);

            // Act
            testableBinder.ProcessDto(bindingContext, dto, modelValidationNode);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.True(modelStateDictionary.IsValid);
            Assert.Empty(modelStateDictionary);
        }
Ejemplo n.º 35
0
        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);
        }
Ejemplo n.º 36
0
        public void ProcessDto_RequiredFieldMissing_RaisesModelError()
        {
            // Arrange
            var model = new ModelWithRequired();
            var containerMetadata = GetMetadataForObject(model);
            var bindingContext = CreateContext(containerMetadata);

            // Set no properties though Age (a non-Nullable struct) and City (a class) properties are required.
            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            var modelStateDictionary = bindingContext.ModelState;
            Assert.Equal(false, modelStateDictionary.IsValid);
            Assert.Equal(2, modelStateDictionary.Count);

            // Check Age error.
            ModelState modelState;
            Assert.True(modelStateDictionary.TryGetValue("theModel.Age", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            var modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("The Age field is required.", modelError.ErrorMessage);

            // Check City error.
            Assert.True(modelStateDictionary.TryGetValue("theModel.City", out modelState));
            Assert.Equal(1, modelState.Errors.Count);

            modelError = modelState.Errors[0];
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("The City field is required.", modelError.ErrorMessage);
        }
Ejemplo n.º 37
0
        // Internal for testing.
        internal ModelValidationNode ProcessDto(
            ModelBindingContext bindingContext,
            ComplexModelDto dto,
            ModelValidationNode validationNode)
        {
            var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
            var modelExplorer    = metadataProvider.GetModelExplorerForType(bindingContext.ModelType, bindingContext.Model);
            var validationInfo   = GetPropertyValidationInfo(bindingContext);

            // Eliminate provided properties from requiredProperties; leaving just *missing* required properties.
            var boundProperties = dto.Results.Where(p => p.Value.IsModelSet).Select(p => p.Key.PropertyName);

            validationInfo.RequiredProperties.ExceptWith(boundProperties);

            foreach (var missingRequiredProperty in validationInfo.RequiredProperties)
            {
                var addedError = false;

                // We want to provide the 'null' value, not the value of model.Property,
                // so avoiding modelExplorer.GetProperty here which would call the actual getter on the
                // model. This avoids issues with value types, or properties with pre-initialized values.
                var propertyExplorer = modelExplorer.GetExplorerForProperty(missingRequiredProperty, model: null);

                var propertyName  = propertyExplorer.Metadata.BinderModelName ?? missingRequiredProperty;
                var modelStateKey = ModelNames.CreatePropertyModelName(
                    bindingContext.ModelName,
                    propertyName);

                // Get the first 'required' validator (if any) to get custom error message.
                var validatorProviderContext = new ModelValidatorProviderContext(propertyExplorer.Metadata);
                bindingContext.OperationBindingContext.ValidatorProvider.GetValidators(validatorProviderContext);
                var validator = validatorProviderContext.Validators.FirstOrDefault(v => v.IsRequired);

                if (validator != null)
                {
                    addedError = RunValidator(validator, bindingContext, propertyExplorer, modelStateKey);
                }

                // Fall back to default message if BindingBehaviorAttribute required this property and we have no
                // actual validator for it.
                if (!addedError)
                {
                    bindingContext.ModelState.TryAddModelError(
                        modelStateKey,
                        Resources.FormatModelBinding_MissingRequiredMember(propertyName));
                }
            }

            // For each property that ComplexModelDtoModelBinder attempted to bind, call the setter, recording
            // exceptions as necessary.
            foreach (var entry in dto.Results)
            {
                var dtoResult = entry.Value;
                if (dtoResult != null)
                {
                    var propertyMetadata = entry.Key;
                    SetProperty(bindingContext, modelExplorer, propertyMetadata, dtoResult);

                    var dtoValidationNode = dtoResult.ValidationNode;
                    if (dtoValidationNode == null)
                    {
                        // Make sure that irrespective of if the properties of the model were bound with a value,
                        // create a validation node so that these get validated.
                        dtoValidationNode = new ModelValidationNode(dtoResult.Key, entry.Key, dtoResult.Model);
                    }

                    validationNode.ChildNodes.Add(dtoValidationNode);
                }
            }

            return(validationNode);
        }
        public void ProcessDto_RequiredFieldNull_RaisesModelErrorWithMessage(bool isModelSet)
        {
            // Arrange
            var model = new Person();
            var containerMetadata = GetMetadataForType(model.GetType());

            var bindingContext = CreateContext(containerMetadata, model);
            var modelStateDictionary = bindingContext.ModelState;

            var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
            var testableBinder = new TestableMutableObjectModelBinder();

            // ValueTypeRequiredWithDefaultValue value comes from [DefaultValue] when !isModelSet.
            var expectedValue = isModelSet ? 0 : 42;

            // Make ValueTypeRequired invalid.
            var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == nameof(Person.ValueTypeRequired));
            dto.Results[propertyMetadata] = new ModelBindingResult(
                null,
                isModelSet: isModelSet,
                key: "theModel." + nameof(Person.ValueTypeRequired));

            // Make ValueTypeRequiredWithDefaultValue invalid
            propertyMetadata = dto.PropertyMetadata
                .Single(p => p.PropertyName == nameof(Person.ValueTypeRequiredWithDefaultValue));
            dto.Results[propertyMetadata] = new ModelBindingResult(
                model: null,
                isModelSet: isModelSet,
                key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue));

            // Act
            testableBinder.ProcessDto(bindingContext, dto);

            // Assert
            Assert.False(modelStateDictionary.IsValid);

            // Check ValueTypeRequired error.
            var modelStateEntry = Assert.Single(
                modelStateDictionary,
                entry => entry.Key == "theModel." + nameof(Person.ValueTypeRequired));
            Assert.Equal("theModel." + nameof(Person.ValueTypeRequired), modelStateEntry.Key);

            var modelState = modelStateEntry.Value;
            Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState);

            var modelError = Assert.Single(modelState.Errors);
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("Sample message", modelError.ErrorMessage);

            // Check ValueTypeRequiredWithDefaultValue error.
            modelStateEntry = Assert.Single(
                modelStateDictionary,
                entry => entry.Key == "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue));
            Assert.Equal("theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue), modelStateEntry.Key);

            modelState = modelStateEntry.Value;
            Assert.Equal(ModelValidationState.Invalid, modelState.ValidationState);

            modelError = Assert.Single(modelState.Errors);
            Assert.Null(modelError.Exception);
            Assert.NotNull(modelError.ErrorMessage);
            Assert.Equal("Another sample message", modelError.ErrorMessage);

            Assert.Equal(0, model.ValueTypeRequired);
            Assert.Equal(expectedValue, model.ValueTypeRequiredWithDefaultValue);
        }