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 Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) { if(bindingContext.ModelType != typeof(Currency)) { return ModelBindingResult.NoResultAsync; } var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if(valueProviderResult == ValueProviderResult.None) { return ModelBindingResult.FailedAsync(bindingContext.ModelName); } bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); var value = valueProviderResult.FirstValue; if(string.IsNullOrEmpty(value)) { return ModelBindingResult.FailedAsync(bindingContext.ModelName); } var model = (Currency)value; if(model == null) { return ModelBindingResult.FailedAsync(bindingContext.ModelName); } var validationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, model ); return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, validationNode); }
/// <inheritdoc /> public async Task<ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext) { if (bindingContext.ModelType == typeof(IFormFile)) { var postedFiles = await GetFormFilesAsync(bindingContext); var value = postedFiles.FirstOrDefault(); var validationNode = new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value); return new ModelBindingResult( value, bindingContext.ModelName, isModelSet: value != null, validationNode: validationNode); } else if (typeof(IEnumerable<IFormFile>).GetTypeInfo().IsAssignableFrom( bindingContext.ModelType.GetTypeInfo())) { var postedFiles = await GetFormFilesAsync(bindingContext); var value = ModelBindingHelper.ConvertValuesToCollectionType(bindingContext.ModelType, postedFiles); var validationNode = new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value); return new ModelBindingResult( value, bindingContext.ModelName, isModelSet: value != null, validationNode: validationNode); } return null; }
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); } } }
/// <inheritdoc /> public async Task<ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext) { object value; if (bindingContext.ModelType == typeof(IFormFile)) { var postedFiles = await GetFormFilesAsync(bindingContext); value = postedFiles.FirstOrDefault(); } else if (typeof(IEnumerable<IFormFile>).IsAssignableFrom(bindingContext.ModelType)) { var postedFiles = await GetFormFilesAsync(bindingContext); value = ModelBindingHelper.ConvertValuesToCollectionType(bindingContext.ModelType, postedFiles); } else { // This binder does not support the requested type. return null; } ModelValidationNode validationNode = null; if (value != null) { validationNode = new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value); var valueProviderResult = new ValueProviderResult(rawValue: value); bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); } return new ModelBindingResult( value, bindingContext.ModelName, isModelSet: value != null, validationNode: validationNode); }
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); } }
/// <inheritdoc /> public virtual async Task <ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext) { ModelBindingHelper.ValidateBindingContext(bindingContext); if (!CanBindType(bindingContext.ModelMetadata)) { return(null); } var mutableObjectBinderContext = new MutableObjectBinderContext() { ModelBindingContext = bindingContext, PropertyMetadata = GetMetadataForProperties(bindingContext).ToArray(), }; if (!(await CanCreateModel(mutableObjectBinderContext))) { return(null); } EnsureModel(bindingContext); var result = await CreateAndPopulateDto(bindingContext, mutableObjectBinderContext.PropertyMetadata); var validationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, bindingContext.Model); // post-processing, e.g. property setters and hooking up validation ProcessDto(bindingContext, (ComplexModelDto)result.Model, validationNode); return(new ModelBindingResult( bindingContext.Model, bindingContext.ModelName, isModelSet: true, validationNode: validationNode)); }
// Used when the ValueProvider contains the collection to be bound as a single element, e.g. the raw value // is [ "1", "2" ] and needs to be converted to an int[]. internal async Task <CollectionResult> BindSimpleCollection( ModelBindingContext bindingContext, object rawValue, CultureInfo culture) { if (rawValue == null) { return(null); // nothing to do } var boundCollection = new List <TElement>(); var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider; var elementMetadata = metadataProvider.GetMetadataForType(typeof(TElement)); var validationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, boundCollection); var rawValueArray = RawValueToObjectArray(rawValue); foreach (var rawValueElement in rawValueArray) { var innerBindingContext = ModelBindingContext.GetChildModelBindingContext( bindingContext, bindingContext.ModelName, elementMetadata); innerBindingContext.ValueProvider = new CompositeValueProvider { // our temporary provider goes at the front of the list new ElementalValueProvider(bindingContext.ModelName, rawValueElement, culture), bindingContext.ValueProvider }; object boundValue = null; var result = await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(innerBindingContext); if (result != null && result.IsModelSet) { boundValue = result.Model; if (result.ValidationNode != null) { validationNode.ChildNodes.Add(result.ValidationNode); } } boundCollection.Add(ModelBindingHelper.CastOrDefault <TElement>(boundValue)); } return(new CollectionResult { ValidationNode = validationNode, Model = boundCollection }); }
/// <inheritdoc /> protected async override Task <ModelBindingResult> BindModelCoreAsync( [NotNull] ModelBindingContext bindingContext) { var httpContext = bindingContext.OperationBindingContext.HttpContext; var formatters = bindingContext.OperationBindingContext.InputFormatters; var formatterContext = new InputFormatterContext( httpContext, bindingContext.ModelState, bindingContext.ModelType); var formatter = formatters.FirstOrDefault(f => f.CanRead(formatterContext)); if (formatter == null) { var unsupportedContentType = Resources.FormatUnsupportedContentType( bindingContext.OperationBindingContext.HttpContext.Request.ContentType); bindingContext.ModelState.AddModelError(bindingContext.ModelName, unsupportedContentType); // This model binder is the only handler for the Body binding source. // Always tell the model binding system to skip other model binders i.e. return non-null. return(new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false)); } try { var model = await formatter.ReadAsync(formatterContext); var isTopLevelObject = bindingContext.ModelMetadata.ContainerType == null; // For compatibility with MVC 5.0 for top level object we want to consider an empty key instead of // the parameter name/a custom name. In all other cases (like when binding body to a property) we // consider the entire ModelName as a prefix. var modelBindingKey = isTopLevelObject ? string.Empty : bindingContext.ModelName; var validationNode = new ModelValidationNode(modelBindingKey, bindingContext.ModelMetadata, model) { ValidateAllProperties = true }; return(new ModelBindingResult( model, key: modelBindingKey, isModelSet: true, validationNode: validationNode)); } catch (Exception ex) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex); // This model binder is the only handler for the Body binding source. // Always tell the model binding system to skip other model binders i.e. return non-null. return(new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false)); } }
/// <inheritdoc /> protected async override Task<ModelBindingResult> BindModelCoreAsync( [NotNull] ModelBindingContext bindingContext) { var httpContext = bindingContext.OperationBindingContext.HttpContext; var formatters = bindingContext.OperationBindingContext.InputFormatters; var formatterContext = new InputFormatterContext( httpContext, bindingContext.ModelState, bindingContext.ModelType); var formatter = formatters.FirstOrDefault(f => f.CanRead(formatterContext)); if (formatter == null) { var unsupportedContentType = Resources.FormatUnsupportedContentType( bindingContext.OperationBindingContext.HttpContext.Request.ContentType); bindingContext.ModelState.AddModelError(bindingContext.ModelName, unsupportedContentType); // This model binder is the only handler for the Body binding source. // Always tell the model binding system to skip other model binders i.e. return non-null. return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false); } try { var model = await formatter.ReadAsync(formatterContext); var isTopLevelObject = bindingContext.ModelMetadata.ContainerType == null; // For compatibility with MVC 5.0 for top level object we want to consider an empty key instead of // the parameter name/a custom name. In all other cases (like when binding body to a property) we // consider the entire ModelName as a prefix. var modelBindingKey = isTopLevelObject ? string.Empty : bindingContext.ModelName; var validationNode = new ModelValidationNode(modelBindingKey, bindingContext.ModelMetadata, model) { ValidateAllProperties = true }; return new ModelBindingResult( model, key: modelBindingKey, isModelSet: true, validationNode: validationNode); } catch (Exception ex) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex); // This model binder is the only handler for the Body binding source. // Always tell the model binding system to skip other model binders i.e. return non-null. return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false); } }
public async Task <ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) { ModelBindingHelper.ValidateBindingContext(bindingContext); if (bindingContext.ModelMetadata.IsComplexType) { // this type cannot be converted return(null); } var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName); if (valueProviderResult == null) { return(null); // no entry } object newModel; bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); try { newModel = valueProviderResult.ConvertTo(bindingContext.ModelType); ModelBindingHelper.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref newModel); var validationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, newModel); var isModelSet = true; // When converting newModel a null value may indicate a failed conversion for an otherwise required // model (can't set a ValueType to null). This detects if a null model value is acceptable given the // current bindingContext. If not, an error is logged. if (newModel == null && !AllowsNullValue(bindingContext.ModelType)) { bindingContext.ModelState.TryAddModelError( bindingContext.ModelName, Resources.FormatCommon_ValueNotValidForProperty(newModel)); isModelSet = false; } return(new ModelBindingResult(newModel, bindingContext.ModelName, isModelSet, validationNode)); } catch (Exception ex) { bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, ex); } // Were able to find a converter for the type but conversion failed. // Tell the model binding system to skip other model binders i.e. return non-null. return(new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false)); }
public async Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) { ModelBindingHelper.ValidateBindingContext(bindingContext); if (bindingContext.ModelMetadata.IsComplexType) { // this type cannot be converted return null; } var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName); if (valueProviderResult == null) { return null; // no entry } object newModel; bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); try { newModel = valueProviderResult.ConvertTo(bindingContext.ModelType); ModelBindingHelper.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref newModel); var validationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, newModel); var isModelSet = true; // When converting newModel a null value may indicate a failed conversion for an otherwise required // model (can't set a ValueType to null). This detects if a null model value is acceptable given the // current bindingContext. If not, an error is logged. if (newModel == null && !AllowsNullValue(bindingContext.ModelType)) { bindingContext.ModelState.TryAddModelError( bindingContext.ModelName, Resources.FormatCommon_ValueNotValidForProperty(newModel)); isModelSet = false; } return new ModelBindingResult(newModel, bindingContext.ModelName, isModelSet, validationNode); } catch (Exception ex) { bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, ex); } // Were able to find a converter for the type but conversion failed. // Tell the model binding system to skip other model binders i.e. return non-null. return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false); }
// Internal for testing. internal ModelValidationNode ProcessResults( ModelBindingContext bindingContext, IDictionary <ModelMetadata, ModelBindingResult> results, 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 = 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 BindPropertiesAsync() attempted to bind, call the setter, recording // exceptions as necessary. foreach (var entry in results) { var result = entry.Value; if (result != null) { var propertyMetadata = entry.Key; SetProperty(bindingContext, modelExplorer, propertyMetadata, result); var propertyValidationNode = result.ValidationNode; if (propertyValidationNode == null) { // Make sure that irrespective of whether the properties of the model were bound with a value, // create a validation node so that these get validated. propertyValidationNode = new ModelValidationNode(result.Key, entry.Key, result.Model); } validationNode.ChildNodes.Add(propertyValidationNode); } } return(validationNode); }
public void CombineWith(ModelValidationNode otherNode) { if (otherNode != null && !otherNode.SuppressValidation) { Validated += otherNode.Validated; Validating += otherNode.Validating; var otherChildNodes = otherNode._childNodes; for (var i = 0; i < otherChildNodes.Count; i++) { var childNode = otherChildNodes[i]; _childNodes.Add(childNode); } } }
/// <inheritdoc /> protected override Task <ModelBindingResult> BindModelCoreAsync([NotNull] ModelBindingContext bindingContext) { var request = bindingContext.OperationBindingContext.HttpContext.Request; var modelMetadata = bindingContext.ModelMetadata; // Property name can be null if the model metadata represents a type (rather than a property or parameter). var headerName = bindingContext.BinderModelName ?? modelMetadata.PropertyName ?? bindingContext.ModelName; object model = null; if (bindingContext.ModelType == typeof(string)) { var value = request.Headers.Get(headerName); if (value != null) { model = value; } } else if (typeof(IEnumerable <string>).IsAssignableFrom(bindingContext.ModelType)) { var values = request.Headers.GetCommaSeparatedValues(headerName); if (values != null) { model = ModelBindingHelper.ConvertValuesToCollectionType( bindingContext.ModelType, values); } } ModelValidationNode validationNode = null; if (model != null) { validationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, model); var attemptedValue = (model as string) ?? request.Headers.Get(headerName); var valueProviderResult = new ValueProviderResult(model, attemptedValue, CultureInfo.InvariantCulture); bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); } return(Task.FromResult( new ModelBindingResult( model, bindingContext.ModelName, isModelSet: model != null, validationNode: validationNode))); }
/// <inheritdoc /> public async Task <ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext) { // Check if this binder applies. if (bindingContext.ModelType != typeof(byte[])) { return(null); } // Check for missing data case 1: There was no <input ... /> element containing this data. var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName); if (valueProviderResult == null) { return(new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false)); } bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); // Check for missing data case 2: There was an <input ... /> element but it was left blank. var value = valueProviderResult.AttemptedValue; if (string.IsNullOrEmpty(value)) { return(new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false)); } try { var model = Convert.FromBase64String(value); var validationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, model); return(new ModelBindingResult( model, bindingContext.ModelName, isModelSet: true, validationNode: validationNode)); } catch (Exception ex) { bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, ex); } // Matched the type (byte[]) only this binder supports. As in missing data cases, always tell the model // binding system to skip other model binders i.e. return non-null. return(new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false)); }
public void PropertiesAreSet() { // Arrange var metadata = GetModelMetadata(); var modelStateKey = "someKey"; // Act var node = new ModelValidationNode(metadata, modelStateKey); // Assert Assert.Equal(metadata, node.ModelMetadata); Assert.Equal(modelStateKey, node.ModelStateKey); Assert.NotNull(node.ChildNodes); Assert.Empty(node.ChildNodes); }
/// <inheritdoc /> public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext.ModelType == typeof(CancellationToken)) { var model = bindingContext.OperationBindingContext.HttpContext.RequestAborted; var validationNode = new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model); return Task.FromResult(new ModelBindingResult( model, bindingContext.ModelName, isModelSet: true, validationNode: validationNode)); } return Task.FromResult<ModelBindingResult>(null); }
/// <inheritdoc /> public Task <ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext.ModelType == typeof(CancellationToken)) { var model = bindingContext.OperationBindingContext.HttpContext.RequestAborted; var validationNode = new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model); return(Task.FromResult(new ModelBindingResult( model, bindingContext.ModelName, isModelSet: true, validationNode: validationNode))); } return(Task.FromResult <ModelBindingResult>(null)); }
public void SetProperty_PropertyIsReadOnly_DoesNothing() { // Arrange var bindingContext = CreateContext(GetMetadataForObject(new Person())); var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "NonUpdateableProperty"); var validationNode = new ModelValidationNode(propertyMetadata, "foo"); var dtoResult = new ComplexModelDtoResult(model: null, validationNode: validationNode); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator: null); // Assert // If didn't throw, success! }
/// <inheritdoc /> protected override Task<ModelBindingResult> BindModelCoreAsync([NotNull] ModelBindingContext bindingContext) { var requestServices = bindingContext.OperationBindingContext.HttpContext.RequestServices; var model = requestServices.GetRequiredService(bindingContext.ModelType); var validationNode = new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model) { SuppressValidation = true }; return Task.FromResult(new ModelBindingResult( model, bindingContext.ModelName, isModelSet: true, validationNode: validationNode)); }
/// <inheritdoc /> public async Task<ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext) { // Check if this binder applies. if (bindingContext.ModelType != typeof(byte[])) { return null; } // Check for missing data case 1: There was no <input ... /> element containing this data. var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName); if (valueProviderResult == null) { return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false); } bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); // Check for missing data case 2: There was an <input ... /> element but it was left blank. var value = valueProviderResult.AttemptedValue; if (string.IsNullOrEmpty(value)) { return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false); } try { var model = Convert.FromBase64String(value); var validationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, model); return new ModelBindingResult( model, bindingContext.ModelName, isModelSet: true, validationNode: validationNode); } catch (Exception ex) { bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, ex); } // Matched the type (byte[]) only this binder supports. As in missing data cases, always tell the model // binding system to skip other model binders i.e. return non-null. return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false); }
public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) { if (typeof(OrderStatus).IsAssignableFrom(bindingContext.ModelType)) { var request = bindingContext.OperationBindingContext.HttpContext.Request; // Doing something slightly different here to make sure we don't get accidentally bound // by the type converter binder. OrderStatus model; var isModelSet = Enum.TryParse<OrderStatus>("Status" + request.Query.Get("status"), out model); var validationNode = new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model); return Task.FromResult(new ModelBindingResult(model, "status", isModelSet, validationNode)); } return Task.FromResult<ModelBindingResult>(null); }
public void ConstructorSetsCollectionInstance() { // Arrange var metadata = GetModelMetadata(); var modelStateKey = "someKey"; var childNodes = new[] { new ModelValidationNode(metadata, "someKey0"), new ModelValidationNode(metadata, "someKey1") }; // Act var node = new ModelValidationNode(metadata, modelStateKey, childNodes); // Assert Assert.Equal(childNodes, node.ChildNodes.ToArray()); }
public async Task <ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) { ModelBindingHelper.ValidateBindingContext(bindingContext); if (bindingContext.ModelMetadata.IsComplexType) { // this type cannot be converted return(null); } var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName); if (valueProviderResult == null) { return(null); // no entry } object newModel; bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); try { newModel = valueProviderResult.ConvertTo(bindingContext.ModelType); ModelBindingHelper.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref newModel); var validationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, newModel); return(new ModelBindingResult( newModel, bindingContext.ModelName, isModelSet: true, validationNode: validationNode)); } catch (Exception ex) { bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, ex); } // Were able to find a converter for the type but conversion failed. // Tell the model binding system to skip other model binders i.e. return non-null. return(new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false)); }
/// <inheritdoc /> protected override Task<ModelBindingResult> BindModelCoreAsync([NotNull] ModelBindingContext bindingContext) { var request = bindingContext.OperationBindingContext.HttpContext.Request; var modelMetadata = bindingContext.ModelMetadata; // Property name can be null if the model metadata represents a type (rather than a property or parameter). var headerName = bindingContext.BinderModelName ?? modelMetadata.PropertyName ?? bindingContext.ModelName; object model = null; if (bindingContext.ModelType == typeof(string)) { var value = request.Headers.Get(headerName); if (value != null) { model = value; } } else if (typeof(IEnumerable<string>).GetTypeInfo().IsAssignableFrom( bindingContext.ModelType.GetTypeInfo())) { var values = request.Headers.GetCommaSeparatedValues(headerName); if (values != null) { model = ModelBindingHelper.ConvertValuesToCollectionType( bindingContext.ModelType, values); } } ModelValidationNode validationNode = null; if (model != null) { validationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, model); } return Task.FromResult( new ModelBindingResult( model, bindingContext.ModelName, isModelSet: model != null, validationNode: validationNode)); }
/// <inheritdoc /> public async Task <ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext) { object value; if (bindingContext.ModelType == typeof(IFormFile)) { var postedFiles = await GetFormFilesAsync(bindingContext); value = postedFiles.FirstOrDefault(); } else if (typeof(IEnumerable <IFormFile>).IsAssignableFrom(bindingContext.ModelType)) { var postedFiles = await GetFormFilesAsync(bindingContext); value = ModelBindingHelper.ConvertValuesToCollectionType(bindingContext.ModelType, postedFiles); } else { // This binder does not support the requested type. return(null); } ModelValidationNode validationNode = null; if (value != null) { validationNode = new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value) { SuppressValidation = true, }; var valueProviderResult = new ValueProviderResult(rawValue: value); bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); } return(new ModelBindingResult( value, bindingContext.ModelName, isModelSet: value != null, validationNode: validationNode)); }
/// <inheritdoc /> public async Task <ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext) { if (bindingContext.ModelType != typeof(IFormCollection) && bindingContext.ModelType != typeof(FormCollection)) { return(null); } object model; var request = bindingContext.OperationBindingContext.HttpContext.Request; if (request.HasFormContentType) { var form = await request.ReadFormAsync(); if (bindingContext.ModelType.GetTypeInfo().IsAssignableFrom(form.GetType().GetTypeInfo())) { model = form; } else { var formValuesLookup = form.ToDictionary(p => p.Key, p => p.Value); model = new FormCollection(formValuesLookup, form.Files); } } else { model = new FormCollection(new Dictionary <string, string[]>()); } var validationNode = new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model) { SuppressValidation = true, }; return(new ModelBindingResult( model, bindingContext.ModelName, isModelSet: true, validationNode: validationNode)); }
public async Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) { ModelBindingHelper.ValidateBindingContext(bindingContext); if (bindingContext.ModelMetadata.IsComplexType) { // this type cannot be converted return null; } var valueProviderResult = await bindingContext.ValueProvider.GetValueAsync(bindingContext.ModelName); if (valueProviderResult == null) { return null; // no entry } object newModel; bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); try { newModel = valueProviderResult.ConvertTo(bindingContext.ModelType); ModelBindingHelper.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref newModel); var validationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, newModel); return new ModelBindingResult( newModel, bindingContext.ModelName, isModelSet: true, validationNode: validationNode); } catch (Exception ex) { bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, ex); } // Were able to find a converter for the type but conversion failed. // Tell the model binding system to skip other model binders i.e. return non-null. return new ModelBindingResult(model: null, key: bindingContext.ModelName, isModelSet: false); }
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 void SetProperty_SettingNonNullableValueTypeToNull_RequiredValidatorPresent_AddsModelError() { // Arrange var bindingContext = CreateContext(GetMetadataForObject(new Person())); bindingContext.ModelName = " foo"; var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "ValueTypeRequired"); var validationNode = new ModelValidationNode(propertyMetadata, "foo.ValueTypeRequired"); var dtoResult = new ComplexModelDtoResult(model: null, validationNode: validationNode); var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator); // Assert Assert.Equal(false, bindingContext.ModelState.IsValid); Assert.Equal("Sample message", bindingContext.ModelState["foo.ValueTypeRequired"].Errors[0].ErrorMessage); }
private object TryConvertContainerToMetadataType(ModelValidationNode parentNode) { if (parentNode != null) { var containerInstance = parentNode.ModelMetadata.Model; if (containerInstance != null) { var expectedContainerType = ModelMetadata.ContainerType; if (expectedContainerType != null) { if (expectedContainerType.IsCompatibleWith(containerInstance)) { return containerInstance; } } } } return null; }
private object TryConvertContainerToMetadataType(ModelValidationNode parentNode) { if (parentNode != null) { var containerInstance = parentNode.ModelMetadata.Model; if (containerInstance != null) { var expectedContainerType = ModelMetadata.ContainerType; if (expectedContainerType != null) { if (expectedContainerType.IsCompatibleWith(containerInstance)) { return(containerInstance); } } } } return(null); }
/// <inheritdoc /> public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext.ModelType == typeof(HttpRequestMessage)) { var model = bindingContext.OperationBindingContext.HttpContext.GetHttpRequestMessage(); var validationNode = new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model) { SuppressValidation = true, }; return Task.FromResult(new ModelBindingResult( model, bindingContext.ModelName, isModelSet: true, validationNode: validationNode)); } return Task.FromResult<ModelBindingResult>(null); }
public void CombineWith() { // Arrange var expected = new[] { "Validating parent1.", "Validating parent2.", "Validated parent1.", "Validated parent2." }; var log = new List <string>(); var allChildNodes = new[] { new ModelValidationNode(GetModelMetadata(), "key1"), new ModelValidationNode(GetModelMetadata(), "key2"), new ModelValidationNode(GetModelMetadata(), "key3"), }; var parentNode1 = new ModelValidationNode(GetModelMetadata(), "parent1"); parentNode1.ChildNodes.Add(allChildNodes[0]); parentNode1.Validating += (sender, e) => log.Add("Validating parent1."); parentNode1.Validated += (sender, e) => log.Add("Validated parent1."); var parentNode2 = new ModelValidationNode(GetModelMetadata(), "parent2"); parentNode2.ChildNodes.Add(allChildNodes[1]); parentNode2.ChildNodes.Add(allChildNodes[2]); parentNode2.Validating += (sender, e) => log.Add("Validating parent2."); parentNode2.Validated += (sender, e) => log.Add("Validated parent2."); var context = CreateContext(); // Act parentNode1.CombineWith(parentNode2); parentNode1.Validate(context); // Assert Assert.Equal(expected, log); Assert.Equal(allChildNodes, parentNode1.ChildNodes.ToArray()); }
public void SetProperty_SettingNullableTypeToNull_RequiredValidatorPresent_PropertySetterThrows_AddsRequiredMessageString() { // Arrange var bindingContext = CreateContext(GetMetadataForObject(new ModelWhosePropertySetterThrows())); bindingContext.ModelName = "foo"; var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "Name"); var validationNode = new ModelValidationNode(propertyMetadata, "foo.Name"); var dtoResult = new ComplexModelDtoResult(model: null, validationNode: validationNode); var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator); // Assert Assert.Equal(false, bindingContext.ModelState.IsValid); Assert.Equal(1, bindingContext.ModelState["foo.Name"].Errors.Count); Assert.Equal("This message comes from the [Required] attribute.", bindingContext.ModelState["foo.Name"].Errors[0].ErrorMessage); }
public void Validate_SkipsValidationIfSuppressed() { // Arrange var log = new List <string>(); var model = new LoggingValidatableObject(log); var modelMetadata = GetModelMetadata(model); var node = new ModelValidationNode(modelMetadata, "theKey") { SuppressValidation = true }; node.Validating += (sender, e) => log.Add("In OnValidating()"); node.Validated += (sender, e) => log.Add("In OnValidated()"); var context = CreateContext(); // Act node.Validate(context); // Assert Assert.Empty(log); }
public void Validate_ValidatesIfModelIsNull() { // Arrange var modelMetadata = GetModelMetadata(typeof(ValidateAllPropertiesModel)); var node = new ModelValidationNode(modelMetadata, "theKey"); var context = CreateContext(modelMetadata); // Act node.Validate(context); // Assert var modelState = Assert.Single(context.ModelState); Assert.Equal("theKey", modelState.Key); Assert.Equal(ModelValidationState.Invalid, modelState.Value.ValidationState); var error = Assert.Single(modelState.Value.Errors); Assert.Equal("A value is required but was not present in the request.", error.ErrorMessage); }
/// <inheritdoc /> public async Task<ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext) { if (bindingContext.ModelType != typeof(IFormCollection) && bindingContext.ModelType != typeof(FormCollection)) { return null; } object model; var request = bindingContext.OperationBindingContext.HttpContext.Request; if (request.HasFormContentType) { var form = await request.ReadFormAsync(); if (bindingContext.ModelType.GetTypeInfo().IsAssignableFrom(form.GetType().GetTypeInfo())) { model = form; } else { var formValuesLookup = form.ToDictionary(p => p.Key, p => p.Value); model = new FormCollection(formValuesLookup, form.Files); } } else { model = new FormCollection(new Dictionary<string, string[]>()); } var validationNode = new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model) { SuppressValidation = true, }; return new ModelBindingResult( model, bindingContext.ModelName, isModelSet: true, validationNode: validationNode); }
public void Validate_SkipsValidationIfHandlerCancels() { // Arrange var log = new List <string>(); var model = new LoggingValidatableObject(log); var modelMetadata = GetModelMetadata(model); var node = new ModelValidationNode(modelMetadata, "theKey"); node.Validating += (sender, e) => { log.Add("In OnValidating()"); e.Cancel = true; }; node.Validated += (sender, e) => log.Add("In OnValidated()"); var context = CreateContext(modelMetadata); // Act node.Validate(context); // Assert Assert.Equal(new[] { "In OnValidating()" }, log.ToArray()); }
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 void CombineWith() { // Arrange var expected = new[] { "Validating parent1.", "Validating parent2.", "Validated parent1.", "Validated parent2." }; var log = new List<string>(); var allChildNodes = new[] { new ModelValidationNode(GetModelMetadata(), "key1"), new ModelValidationNode(GetModelMetadata(), "key2"), new ModelValidationNode(GetModelMetadata(), "key3"), }; var parentNode1 = new ModelValidationNode(GetModelMetadata(), "parent1"); parentNode1.ChildNodes.Add(allChildNodes[0]); parentNode1.Validating += (sender, e) => log.Add("Validating parent1."); parentNode1.Validated += (sender, e) => log.Add("Validated parent1."); var parentNode2 = new ModelValidationNode(GetModelMetadata(), "parent2"); parentNode2.ChildNodes.Add(allChildNodes[1]); parentNode2.ChildNodes.Add(allChildNodes[2]); parentNode2.Validating += (sender, e) => log.Add("Validating parent2."); parentNode2.Validated += (sender, e) => log.Add("Validated parent2."); var context = CreateContext(); // Act parentNode1.CombineWith(parentNode2); parentNode1.Validate(context); // Assert Assert.Equal(expected, log); Assert.Equal(allChildNodes, parentNode1.ChildNodes.ToArray()); }
/// <inheritdoc /> public virtual async Task <ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext) { ModelBindingHelper.ValidateBindingContext(bindingContext); if (!CanBindType(bindingContext.ModelMetadata)) { return(null); } var mutableObjectBinderContext = new MutableObjectBinderContext() { ModelBindingContext = bindingContext, PropertyMetadata = GetMetadataForProperties(bindingContext).ToArray(), }; if (!(await CanCreateModel(mutableObjectBinderContext))) { return(null); } // Create model first (if necessary) to avoid reporting errors about properties when activation fails. var model = GetModel(bindingContext); var results = await BindPropertiesAsync(bindingContext, mutableObjectBinderContext.PropertyMetadata); var validationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, model); // Post-processing e.g. property setters and hooking up validation. bindingContext.Model = model; ProcessResults(bindingContext, results, validationNode); return(new ModelBindingResult( model, bindingContext.ModelName, isModelSet: true, validationNode: validationNode)); }
public async Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) { var valueProviderResult = await GetCompatibleValueProviderResult(bindingContext); if (valueProviderResult == null) { // conversion would have failed return null; } bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); var model = valueProviderResult.RawValue; ModelBindingHelper.ReplaceEmptyStringWithNull(bindingContext.ModelMetadata, ref model); var validationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, model); return new ModelBindingResult( model, bindingContext.ModelName, isModelSet: true, validationNode: validationNode); }
public void SetProperty_PropertyIsSettable_SetterThrows_RecordsError() { // Arrange var model = new Person { DateOfBirth = new DateTime(1900, 1, 1) }; var bindingContext = CreateContext(GetMetadataForObject(model)); var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfDeath"); var validationNode = new ModelValidationNode(propertyMetadata, "foo"); var dtoResult = new ComplexModelDtoResult(new DateTime(1800, 1, 1), validationNode); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator: null); // Assert Assert.Equal("Date of death can't be before date of birth." + Environment.NewLine + "Parameter name: value", bindingContext.ModelState["foo"].Errors[0].Exception.Message); }
public void ProcessResults_NullableValueTypeProperty_NoValueSet_NoError() { // Arrange var model = new NullableValueTypeProperty(); var containerMetadata = GetMetadataForType(model.GetType()); var bindingContext = CreateContext(containerMetadata, model); var results = containerMetadata.Properties.ToDictionary( property => property, property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false)); var testableBinder = new TestableMutableObjectModelBinder(); var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model); // Act testableBinder.ProcessResults(bindingContext, results, modelValidationNode); // Assert var modelStateDictionary = bindingContext.ModelState; Assert.True(modelStateDictionary.IsValid); }
public virtual async Task<ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext) { var newBindingContext = CreateNewBindingContext(bindingContext, bindingContext.ModelName); var modelBindingResult = await TryBind(newBindingContext); if (modelBindingResult == null && bindingContext.FallbackToEmptyPrefix && !string.IsNullOrEmpty(bindingContext.ModelName)) { // Fall back to empty prefix. newBindingContext = CreateNewBindingContext(bindingContext, modelName: string.Empty); modelBindingResult = await TryBind(newBindingContext); } if (modelBindingResult == null) { return null; // something went wrong } bindingContext.OperationBindingContext.BodyBindingState = newBindingContext.OperationBindingContext.BodyBindingState; var bindingKey = bindingContext.ModelName; if (modelBindingResult.IsModelSet) { // Update the model state key if we are bound using an empty prefix and it is a complex type. // This is needed as validation uses the model state key to log errors. The client validation expects // the errors with property names rather than the full name. if (newBindingContext.ModelMetadata.IsComplexType && string.IsNullOrEmpty(modelBindingResult.Key)) { // For non-complex types, if we fell back to the empty prefix, we should still be using the name // of the parameter/property. Complex types have their own property names which acts as model // state keys and do not need special treatment. // For example : // // public class Model // { // public int SimpleType { get; set; } // } // public void Action(int id, Model model) // { // } // // In this case, for the model parameter the key would be SimpleType instead of model.SimpleType. // (i.e here the prefix for the model key is empty). // For the id parameter the key would be id. bindingKey = string.Empty; } } // Update the model validation node if the model binding result was set but no validation node was provided. // This would typically be the case where leaf level model binders, do not have to add a validation node // for validation to take effect. The composite being the entry point for model binders, takes care or // adding missing validation nodes. var modelValidationNode = modelBindingResult.ValidationNode; if (modelBindingResult.IsModelSet && modelValidationNode == null) { modelValidationNode = new ModelValidationNode( bindingKey, bindingContext.ModelMetadata, modelBindingResult.Model); } return new ModelBindingResult( modelBindingResult.Model, bindingKey, modelBindingResult.IsModelSet, modelValidationNode); }
public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) { var model = "Success"; bindingContext.ModelState.SetModelValue( bindingContext.ModelName, new ValueProviderResult(model, model, CultureInfo.CurrentCulture)); var modelValidationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, model); return Task.FromResult(new ModelBindingResult(model, bindingContext.ModelName, true, modelValidationNode)); }
public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext.ModelType != typeof(Address)) { return null; } var address = new Address() { Street = "SomeStreet" }; bindingContext.ModelState.SetModelValue( ModelNames.CreatePropertyModelName(bindingContext.ModelName, "Street"), new ValueProviderResult( address.Street, address.Street, CultureInfo.CurrentCulture)); var validationNode = new ModelValidationNode( bindingContext.ModelName, bindingContext.ModelMetadata, address) { ValidateAllProperties = true }; return Task.FromResult(new ModelBindingResult(address, bindingContext.ModelName, true, validationNode)); }
public async Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) { if (typeof(Product).IsAssignableFrom(bindingContext.ModelType)) { var model = (Product)Activator.CreateInstance(bindingContext.ModelType); model.BinderType = GetType(); var key = string.IsNullOrEmpty(bindingContext.ModelName) ? "productId" : bindingContext.ModelName + "." + "productId"; var value = await bindingContext.ValueProvider.GetValueAsync(key); model.ProductId = (int)value.ConvertTo(typeof(int)); var validationNode = new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value); return new ModelBindingResult(model, key, true, validationNode); } return null; }
public void ProcessResults_ProvideRequiredFields_Success() { // Arrange var model = new Person(); var containerMetadata = GetMetadataForType(model.GetType()); var bindingContext = CreateContext(containerMetadata, model); var modelStateDictionary = bindingContext.ModelState; var results = containerMetadata.Properties.ToDictionary( property => property, property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false)); var testableBinder = new TestableMutableObjectModelBinder(); // Make ValueTypeRequired valid. var propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequired)]; results[propertyMetadata] = new ModelBindingResult( model: 41, isModelSet: true, key: "theModel." + nameof(Person.ValueTypeRequired)); // Make ValueTypeRequiredWithDefaultValue valid. propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequiredWithDefaultValue)]; results[propertyMetadata] = new ModelBindingResult( model: 57, isModelSet: true, key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue)); // Also remind ProcessResults about PropertyWithDefaultValue -- as BindPropertiesAsync() would. propertyMetadata = containerMetadata.Properties[nameof(Person.PropertyWithDefaultValue)]; results[propertyMetadata] = new ModelBindingResult( model: null, isModelSet: false, key: "theModel." + nameof(Person.PropertyWithDefaultValue)); var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model); // Act testableBinder.ProcessResults(bindingContext, results, 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 }
public void ProcessResults_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 results = containerMetadata.Properties.ToDictionary( property => property, property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false)); var propertyMetadata = containerMetadata.Properties[nameof(model.Name)]; 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 = containerMetadata.Properties[nameof(model.Age)]; results[propertyMetadata] = new ModelBindingResult(model: null, isModelSet: true, key: "theModel.Age"); var testableBinder = new TestableMutableObjectModelBinder(); var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model); // Act testableBinder.ProcessResults(bindingContext, results, 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); }
public void ProcessResults_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 results = containerMetadata.Properties.ToDictionary( property => property, property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false)); var firstNameProperty = containerMetadata.Properties[nameof(model.FirstName)]; results[firstNameProperty] = new ModelBindingResult( model: "John", isModelSet: true, key: nameof(model.FirstName)); var lastNameProperty = containerMetadata.Properties[nameof(model.LastName)]; results[lastNameProperty] = new ModelBindingResult( model: "Doe", isModelSet: true, key: nameof(model.LastName)); var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.ProcessResults(bindingContext, results, 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(5, modelValidationNode.ChildNodes.Count); Assert.Collection(modelValidationNode.ChildNodes, child => { Assert.Equal(nameof(model.DateOfBirth), child.Key); Assert.Equal(null, child.Model); }, child => { Assert.Equal(nameof(model.DateOfDeath), child.Key); Assert.Equal(null, child.Model); }, child => { Assert.Equal(nameof(model.FirstName), child.Key); Assert.Equal("John", child.Model); }, child => { Assert.Equal(nameof(model.LastName), child.Key); Assert.Equal("Doe", child.Model); }, child => { Assert.Equal(nameof(model.NonUpdateableProperty), child.Key); Assert.Equal(null, child.Model); }); }
public void ProcessResults_ReferenceTypePropertyWithBindRequired_RequiredValidatorIgnored() { // Arrange var model = new ModelWithBindRequiredAndRequiredAttribute(); var containerMetadata = GetMetadataForType(model.GetType()); var bindingContext = CreateContext(containerMetadata, model); var modelStateDictionary = bindingContext.ModelState; var results = containerMetadata.Properties.ToDictionary( property => property, property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false)); var testableBinder = new TestableMutableObjectModelBinder(); // Make ValueTypeProperty have a value. var propertyMetadata = containerMetadata .Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ValueTypeProperty)]; results[propertyMetadata] = new ModelBindingResult( model: 17, isModelSet: true, key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ValueTypeProperty)); // Make ReferenceTypeProperty not have a value. propertyMetadata = containerMetadata .Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty)]; results[propertyMetadata] = new ModelBindingResult( model: null, isModelSet: false, key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty)); var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model); // Act testableBinder.ProcessResults(bindingContext, results, 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); }
public void ProcessResults_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 results = containerMetadata.Properties.ToDictionary( property => property, property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false)); var nameProperty = containerMetadata.Properties[nameof(model.Name)]; results[nameProperty] = new ModelBindingResult("John Doe", isModelSet: true, key: string.Empty); var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model); var testableBinder = new TestableMutableObjectModelBinder(); // Act testableBinder.ProcessResults(bindingContext, results, 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 ProcessResults_ValueTypeProperty_TriesToSetNullModel_CapturesException() { // Arrange var model = new Person(); var containerMetadata = GetMetadataForType(model.GetType()); var bindingContext = CreateContext(containerMetadata, model); var modelStateDictionary = bindingContext.ModelState; var results = containerMetadata.Properties.ToDictionary( property => property, property => new ModelBindingResult(model: null, key: property.PropertyName, isModelSet: false)); var testableBinder = new TestableMutableObjectModelBinder(); // The [DefaultValue] on ValueTypeRequiredWithDefaultValue is ignored by model binding. var expectedValue = 0; // Make ValueTypeRequired invalid. var propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequired)]; results[propertyMetadata] = new ModelBindingResult( model: null, isModelSet: true, key: "theModel." + nameof(Person.ValueTypeRequired)); // Make ValueTypeRequiredWithDefaultValue invalid propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequiredWithDefaultValue)]; results[propertyMetadata] = new ModelBindingResult( model: null, isModelSet: true, key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue)); var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model); // Act testableBinder.ProcessResults(bindingContext, results, 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); }