/// <summary> /// Constructs a new instance of <see cref="BindingInfo"/> from the given <paramref name="attributes"/>. /// </summary> /// <param name="attributes">A collection of attributes which are used to construct <see cref="BindingInfo"/> /// </param> /// <returns>A new instance of <see cref="BindingInfo"/>.</returns> public static BindingInfo GetBindingInfo(IEnumerable<object> attributes) { var bindingInfo = new BindingInfo(); var isBindingInfoPresent = false; // BinderModelName foreach (var binderModelNameAttribute in attributes.OfType<IModelNameProvider>()) { isBindingInfoPresent = true; if (binderModelNameAttribute?.Name != null) { bindingInfo.BinderModelName = binderModelNameAttribute.Name; break; } } // BinderType foreach (var binderTypeAttribute in attributes.OfType<IBinderTypeProviderMetadata>()) { isBindingInfoPresent = true; if (binderTypeAttribute.BinderType != null) { bindingInfo.BinderType = binderTypeAttribute.BinderType; break; } } // BindingSource foreach (var bindingSourceAttribute in attributes.OfType<IBindingSourceMetadata>()) { isBindingInfoPresent = true; if (bindingSourceAttribute.BindingSource != null) { bindingInfo.BindingSource = bindingSourceAttribute.BindingSource; break; } } // PropertyFilterProvider var propertyFilterProviders = attributes.OfType<IPropertyFilterProvider>().ToArray(); if (propertyFilterProviders.Length == 1) { isBindingInfoPresent = true; bindingInfo.PropertyFilterProvider = propertyFilterProviders[0]; } else if (propertyFilterProviders.Length > 1) { isBindingInfoPresent = true; bindingInfo.PropertyFilterProvider = new CompositePropertyFilterProvider(propertyFilterProviders); } return isBindingInfoPresent ? bindingInfo : null; }
public TestModelBinderProviderContext(Type modelType) { Metadata = CachedMetadataProvider.GetMetadataForType(modelType); MetadataProvider = CachedMetadataProvider; BindingInfo = new BindingInfo() { BinderModelName = Metadata.BinderModelName, BinderType = Metadata.BinderType, BindingSource = Metadata.BindingSource, PropertyFilterProvider = Metadata.PropertyFilterProvider, }; }
/// <summary> /// Creates a copy of a <see cref="BindingInfo"/>. /// </summary> /// <param name="other">The <see cref="BindingInfo"/> to copy.</param> public BindingInfo(BindingInfo other) { if (other == null) { throw new ArgumentNullException(nameof(other)); } BindingSource = other.BindingSource; BinderModelName = other.BinderModelName; BinderType = other.BinderType; PropertyFilterProvider = other.PropertyFilterProvider; }
public TestModelBinderProviderContext(ModelMetadata metadata, BindingInfo bindingInfo) { Metadata = metadata; BindingInfo = bindingInfo ?? new BindingInfo { BinderModelName = metadata.BinderModelName, BinderType = metadata.BinderType, BindingSource = metadata.BindingSource, PropertyFilterProvider = metadata.PropertyFilterProvider, }; MetadataProvider = CachedMetadataProvider; }
/// <summary> /// Creates a new <see cref="DefaultModelBindingContext"/> for top-level model binding operation. /// </summary> /// <param name="operationBindingContext"> /// The <see cref="OperationBindingContext"/> associated with the binding operation. /// </param> /// <param name="metadata"><see cref="ModelMetadata"/> associated with the model.</param> /// <param name="bindingInfo"><see cref="BindingInfo"/> associated with the model.</param> /// <param name="modelName">The name of the property or parameter being bound.</param> /// <returns>A new instance of <see cref="DefaultModelBindingContext"/>.</returns> public static ModelBindingContext CreateBindingContext( OperationBindingContext operationBindingContext, ModelMetadata metadata, BindingInfo bindingInfo, string modelName) { if (operationBindingContext == null) { throw new ArgumentNullException(nameof(operationBindingContext)); } if (metadata == null) { throw new ArgumentNullException(nameof(metadata)); } if (modelName == null) { throw new ArgumentNullException(nameof(modelName)); } var binderModelName = bindingInfo?.BinderModelName ?? metadata.BinderModelName; var propertyFilterProvider = bindingInfo?.PropertyFilterProvider ?? metadata.PropertyFilterProvider; var valueProvider = operationBindingContext.ValueProvider; var bindingSource = bindingInfo?.BindingSource ?? metadata.BindingSource; if (bindingSource != null && !bindingSource.IsGreedy) { valueProvider = FilterValueProvider(operationBindingContext.ValueProvider, bindingSource); } return new DefaultModelBindingContext() { BinderModelName = binderModelName, BindingSource = bindingSource, PropertyFilter = propertyFilterProvider?.PropertyFilter, // Because this is the top-level context, FieldName and ModelName should be the same. FieldName = binderModelName ?? modelName, ModelName = binderModelName ?? modelName, IsTopLevelObject = true, ModelMetadata = metadata, ModelState = operationBindingContext.ActionContext.ModelState, OperationBindingContext = operationBindingContext, ValueProvider = valueProvider, ValidationState = new ValidationStateDictionary(), }; }
public void GetBindingInfo_WithAttributesAndModelMetadata_UsesPropertyPredicateProviderFromModelMetadata_WhenNotFoundViaAttributes() { // Arrange var attributes = new object[] { new ModelBinderAttribute(typeof(object)), new ControllerAttribute(), new BindNeverAttribute(), }; var propertyFilterProvider = Mock.Of <IPropertyFilterProvider>(); var modelType = typeof(Guid); var provider = new TestModelMetadataProvider(); provider.ForType(modelType).BindingDetails(metadata => { metadata.PropertyFilterProvider = propertyFilterProvider; }); var modelMetadata = provider.GetMetadataForType(modelType); // Act var bindingInfo = BindingInfo.GetBindingInfo(attributes, modelMetadata); // Assert Assert.NotNull(bindingInfo); Assert.Same(propertyFilterProvider, bindingInfo.PropertyFilterProvider); }
public override IModelBinder CreateBinder(ModelMetadata metadata, BindingInfo bindingInfo) { if (metadata == null) { throw new ArgumentNullException(nameof(metadata)); } if (bindingInfo == null) { throw new ArgumentNullException(nameof(bindingInfo)); } // For non-root nodes we use the ModelMetadata as the cache token. This ensures that all non-root // nodes with the same metadata will have the same binder. This is OK because for an non-root // node there's no opportunity to customize binding info like there is for a parameter. var token = metadata; var nestedContext = new DefaultModelBinderProviderContext(this, metadata, bindingInfo); return(_factory.CreateBinderCoreCached(nestedContext, token)); }
public DefaultModelBinderProviderContext( ModelBinderFactory factory, ModelBinderFactoryContext factoryContext) { _factory = factory; Metadata = factoryContext.Metadata; BindingInfo bindingInfo; if (factoryContext.BindingInfo != null) { bindingInfo = new BindingInfo(factoryContext.BindingInfo); } else { bindingInfo = new BindingInfo(); } bindingInfo.TryApplyBindingInfo(Metadata); BindingInfo = bindingInfo; MetadataProvider = _factory._metadataProvider; Visited = new Dictionary<Key, IModelBinder?>(); }
public void GetBindingInfo_WithAttributesAndModelMetadata_UsesBinderNameFromModelMetadata_WhenNotFoundViaAttributes() { // Arrange var attributes = new object[] { new ModelBinderAttribute(typeof(object)), new ControllerAttribute(), new BindNeverAttribute(), }; var modelType = typeof(Guid); var provider = new TestModelMetadataProvider(); provider.ForType(modelType).BindingDetails(metadata => { metadata.BindingSource = BindingSource.Special; metadata.BinderType = typeof(string); metadata.BinderModelName = "Different"; }); var modelMetadata = provider.GetMetadataForType(modelType); // Act var bindingInfo = BindingInfo.GetBindingInfo(attributes, modelMetadata); // Assert Assert.NotNull(bindingInfo); Assert.Same(typeof(object), bindingInfo.BinderType); Assert.Same("Different", bindingInfo.BinderModelName); Assert.Same(BindingSource.Custom, bindingInfo.BindingSource); }
/// <summary> /// Constructs a new instance of <see cref="BindingInfo"/> from the given <paramref name="attributes"/> and <paramref name="modelMetadata"/>. /// </summary> /// <param name="attributes">A collection of attributes which are used to construct <see cref="BindingInfo"/>.</param> /// <param name="modelMetadata">The <see cref="ModelMetadata"/>.</param> /// <returns>A new instance of <see cref="BindingInfo"/> if any binding metadata was discovered; otherwise or <see langword="null"/>.</returns> public static BindingInfo GetBindingInfo(IEnumerable <object> attributes, ModelMetadata modelMetadata) { if (attributes == null) { throw new ArgumentNullException(nameof(attributes)); } if (modelMetadata == null) { throw new ArgumentNullException(nameof(modelMetadata)); } var bindingInfo = GetBindingInfo(attributes); var isBindingInfoPresent = bindingInfo != null; if (bindingInfo == null) { bindingInfo = new BindingInfo(); } isBindingInfoPresent |= bindingInfo.TryApplyBindingInfo(modelMetadata); return(isBindingInfoPresent ? bindingInfo : null); }
public override IModelBinder CreateBinder(ModelMetadata metadata, BindingInfo bindingInfo) { _bindingInfo = bindingInfo; return(this.CreateBinder(metadata)); }
public async Task ObsoleteBindModelAsync_PassesExpectedBindingInfoAndMetadata_IfPrefixDoesNotMatch( BindingInfo parameterBindingInfo, string metadataBinderModelName, string parameterName, string expectedModelName) { // Arrange var binderExecuted = false; var metadataProvider = new TestModelMetadataProvider(); metadataProvider.ForType <Person>().BindingDetails(binding => { binding.BinderModelName = metadataBinderModelName; }); var metadata = metadataProvider.GetMetadataForType(typeof(Person)); var modelBinder = new Mock <IModelBinder>(); modelBinder .Setup(b => b.BindModelAsync(It.IsAny <ModelBindingContext>())) .Callback((ModelBindingContext context) => { Assert.Equal(expectedModelName, context.ModelName, StringComparer.Ordinal); }) .Returns(Task.CompletedTask); var parameterDescriptor = new ParameterDescriptor { BindingInfo = parameterBindingInfo, Name = parameterName, ParameterType = typeof(Person), }; var factory = new Mock <IModelBinderFactory>(MockBehavior.Strict); factory .Setup(f => f.CreateBinder(It.IsAny <ModelBinderFactoryContext>())) .Callback((ModelBinderFactoryContext context) => { binderExecuted = true; // Confirm expected data is passed through to ModelBindingFactory. Assert.Same(parameterDescriptor.BindingInfo, context.BindingInfo); Assert.Same(parameterDescriptor, context.CacheToken); Assert.Equal(metadata, context.Metadata); }) .Returns(modelBinder.Object); var parameterBinder = new ParameterBinder( metadataProvider, factory.Object, Mock.Of <IObjectModelValidator>(), _optionsAccessor, NullLoggerFactory.Instance); var controllerContext = GetControllerContext(); // Act & Assert #pragma warning disable CS0618 // Type or member is obsolete await parameterBinder.BindModelAsync(controllerContext, new SimpleValueProvider(), parameterDescriptor); #pragma warning restore CS0618 // Type or member is obsolete Assert.True(binderExecuted); }
public async Task FromServicesOnParameterType_WithData_Succeeds(BindingInfo bindingInfo) { // Arrange // Similar to a custom IBindingSourceMetadata implementation or [ModelBinder] subclass on a custom service. var metadataProvider = new TestModelMetadataProvider(); metadataProvider .ForType<JsonOutputFormatter>() .BindingDetails(binding => binding.BindingSource = BindingSource.Services); var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(metadataProvider); var parameter = new ParameterDescriptor { Name = "parameter-name", BindingInfo = bindingInfo, ParameterType = typeof(JsonOutputFormatter), }; var testContext = ModelBindingTestHelper.GetTestContext(); testContext.MetadataProvider = metadataProvider; var modelState = testContext.ModelState; // Act var modelBindingResult = await argumentBinder.BindModelAsync(parameter, testContext); // Assert Assert.True(modelBindingResult.IsModelSet); Assert.IsType<JsonOutputFormatter>(modelBindingResult.Model); Assert.True(modelState.IsValid); Assert.Empty(modelState); }
public async Task BinderTypeOnProperty_WithData_EmptyPrefix_GetsBound(BindingInfo bindingInfo) { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor { Name = "Parameter1", BindingInfo = bindingInfo, ParameterType = typeof(Person3), }; var testContext = ModelBindingTestHelper.GetTestContext(); var modelState = testContext.ModelState; // Act var modelBindingResult = await argumentBinder.BindModelAsync(parameter, testContext); // Assert // ModelBindingResult Assert.True(modelBindingResult.IsModelSet); // Model var person = Assert.IsType<Person3>(modelBindingResult.Model); Assert.NotNull(person.Address); Assert.Equal("SomeStreet", person.Address.Street); // ModelState Assert.True(modelState.IsValid); var kvp = Assert.Single(modelState); Assert.Equal("Address.Street", kvp.Key); var entry = kvp.Value; Assert.NotNull(entry); Assert.Equal(ModelValidationState.Valid, entry.ValidationState); Assert.NotNull(entry.RawValue); // Value is set by test model binder, no need to validate it. }
public void CreateBinder_PassesExpectedBindingInfo( BindingInfo parameterBindingInfo, BindingMetadata bindingMetadata, BindingInfo expectedInfo) { // Arrange var metadataProvider = new TestModelMetadataProvider(); metadataProvider.ForType<Employee>().BindingDetails(binding => { binding.BinderModelName = bindingMetadata.BinderModelName; binding.BinderType = bindingMetadata.BinderType; binding.BindingSource = bindingMetadata.BindingSource; if (bindingMetadata.PropertyFilterProvider != null) { binding.PropertyFilterProvider = bindingMetadata.PropertyFilterProvider; } }); var modelBinder = Mock.Of<IModelBinder>(); var modelBinderProvider = new TestModelBinderProvider(context => { Assert.Equal(typeof(Employee), context.Metadata.ModelType); Assert.NotNull(context.BindingInfo); Assert.Equal(expectedInfo.BinderModelName, context.BindingInfo.BinderModelName, StringComparer.Ordinal); Assert.Equal(expectedInfo.BinderType, context.BindingInfo.BinderType); Assert.Equal(expectedInfo.BindingSource, context.BindingInfo.BindingSource); Assert.Same(expectedInfo.PropertyFilterProvider, context.BindingInfo.PropertyFilterProvider); return modelBinder; }); var options = new TestOptionsManager<MvcOptions>(); options.Value.ModelBinderProviders.Insert(0, modelBinderProvider); var factory = new ModelBinderFactory(metadataProvider, options); var factoryContext = new ModelBinderFactoryContext { BindingInfo = parameterBindingInfo, Metadata = metadataProvider.GetMetadataForType(typeof(Employee)), }; // Act & Assert var result = factory.CreateBinder(factoryContext); // Confirm our IModelBinderProvider was called. Assert.Same(modelBinder, result); }
public async Task FromBodyOnParameterType_WithData_Succeeds(BindingInfo bindingInfo) { // Arrange var inputText = "{ \"Street\" : \"someStreet\" }"; var metadataProvider = new TestModelMetadataProvider(); metadataProvider .ForType<Address6>() .BindingDetails(binding => binding.BindingSource = BindingSource.Body); var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(metadataProvider); var parameter = new ParameterDescriptor { Name = "parameter-name", BindingInfo = bindingInfo, ParameterType = typeof(Address6), }; var testContext = ModelBindingTestHelper.GetTestContext( request => { request.Body = new MemoryStream(Encoding.UTF8.GetBytes(inputText)); request.ContentType = "application/json"; }); testContext.MetadataProvider = metadataProvider; var modelState = testContext.ModelState; // Act var modelBindingResult = await argumentBinder.BindModelAsync(parameter, testContext); // Assert Assert.True(modelBindingResult.IsModelSet); var address = Assert.IsType<Address6>(modelBindingResult.Model); Assert.Equal("someStreet", address.Street, StringComparer.Ordinal); Assert.True(modelState.IsValid); Assert.Empty(modelState); }
/// <summary> /// Creates an <see cref="IModelBinder"/> for the given <paramref name="metadata"/> /// and <paramref name="bindingInfo"/>. /// </summary> /// <param name="metadata">The <see cref="ModelMetadata"/> for the model.</param> /// <param name="bindingInfo">The <see cref="BindingInfo"/> that should be used /// for creating the binder.</param> /// <returns>An <see cref="IModelBinder"/>.</returns> public virtual IModelBinder CreateBinder(ModelMetadata metadata, BindingInfo bindingInfo) { throw new NotSupportedException(); }
/// <summary> /// Creates a new <see cref="DefaultModelBindingContext"/> for top-level model binding operation. /// </summary> /// <param name="actionContext"> /// The <see cref="ActionContext"/> associated with the binding operation. /// </param> /// <param name="valueProvider">The <see cref="IValueProvider"/> to use for binding.</param> /// <param name="metadata"><see cref="ModelMetadata"/> associated with the model.</param> /// <param name="bindingInfo"><see cref="BindingInfo"/> associated with the model.</param> /// <param name="modelName">The name of the property or parameter being bound.</param> /// <returns>A new instance of <see cref="DefaultModelBindingContext"/>.</returns> public static ModelBindingContext CreateBindingContext( ActionContext actionContext, IValueProvider valueProvider, ModelMetadata metadata, BindingInfo bindingInfo, string modelName) { if (actionContext == null) { throw new ArgumentNullException(nameof(actionContext)); } if (valueProvider == null) { throw new ArgumentNullException(nameof(valueProvider)); } if (metadata == null) { throw new ArgumentNullException(nameof(metadata)); } if (modelName == null) { throw new ArgumentNullException(nameof(modelName)); } var binderModelName = bindingInfo?.BinderModelName ?? metadata.BinderModelName; var bindingSource = bindingInfo?.BindingSource ?? metadata.BindingSource; var propertyFilterProvider = bindingInfo?.PropertyFilterProvider ?? metadata.PropertyFilterProvider; var bindingContext = new DefaultModelBindingContext() { ActionContext = actionContext, BinderModelName = binderModelName, BindingSource = bindingSource, PropertyFilter = propertyFilterProvider?.PropertyFilter, ValidationState = new ValidationStateDictionary(), // Because this is the top-level context, FieldName and ModelName should be the same. FieldName = binderModelName ?? modelName, ModelName = binderModelName ?? modelName, OriginalModelName = binderModelName ?? modelName, IsTopLevelObject = true, ModelMetadata = metadata, ModelState = actionContext.ModelState, OriginalValueProvider = valueProvider, ValueProvider = FilterValueProvider(valueProvider, bindingSource), }; // mvcOptions may be null when this method is called in test scenarios. var mvcOptions = actionContext.HttpContext.RequestServices?.GetService <IOptions <MvcOptions> >(); if (mvcOptions != null) { bindingContext.MaxModelBindingRecursionDepth = mvcOptions.Value.MaxModelBindingRecursionDepth; } return(bindingContext); }
public async Task ModelNameOnPropertyType_WithData_Succeeds(BindingInfo bindingInfo) { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor { Name = "parameter-name", BindingInfo = bindingInfo, ParameterType = typeof(Person12), }; var testContext = ModelBindingTestHelper.GetTestContext( request => request.QueryString = new QueryString("?HomeAddress.Street=someStreet")); var modelState = testContext.ModelState; // Act var modelBindingResult = await argumentBinder.BindModelAsync(parameter, testContext); // Assert Assert.True(modelBindingResult.IsModelSet); var person = Assert.IsType<Person12>(modelBindingResult.Model); Assert.NotNull(person.Address); Assert.Equal("someStreet", person.Address.Street, StringComparer.Ordinal); Assert.True(modelState.IsValid); var kvp = Assert.Single(modelState); Assert.Equal("HomeAddress.Street", kvp.Key); var entry = kvp.Value; Assert.NotNull(entry); Assert.Empty(entry.Errors); Assert.Equal(ModelValidationState.Valid, entry.ValidationState); }
public async Task BindAttributeOnParameterType_WithData_Succeeds(BindingInfo bindingInfo) { // Arrange var argumentBinder = ModelBindingTestHelper.GetArgumentBinder(); var parameter = new ParameterDescriptor { Name = "parameter-name", BindingInfo = bindingInfo, ParameterType = typeof(Address13), }; var testContext = ModelBindingTestHelper.GetTestContext( request => request.QueryString = new QueryString("?Number=23&Street=someStreet&City=Redmond&State=WA")); var modelState = testContext.ModelState; // Act var modelBindingResult = await argumentBinder.BindModelAsync(parameter, testContext); // Assert Assert.True(modelBindingResult.IsModelSet); var address = Assert.IsType<Address13>(modelBindingResult.Model); Assert.Null(address.City); Assert.Equal(0, address.Number); Assert.Null(address.State); Assert.Equal("someStreet", address.Street, StringComparer.Ordinal); Assert.True(modelState.IsValid); var kvp = Assert.Single(modelState); Assert.Equal("Street", kvp.Key); var entry = kvp.Value; Assert.NotNull(entry); Assert.Empty(entry.Errors); Assert.Equal(ModelValidationState.Valid, entry.ValidationState); }
/// <summary> /// Constructs a new instance of <see cref="BindingInfo"/> from the given <paramref name="attributes"/>. /// <para> /// This overload does not account for <see cref="BindingInfo"/> specified via <see cref="ModelMetadata"/>. Consider using /// <see cref="GetBindingInfo(IEnumerable{object}, ModelMetadata)"/> overload, or <see cref="TryApplyBindingInfo(ModelMetadata)"/> /// on the result of this method to get a more accurate <see cref="BindingInfo"/> instance. /// </para> /// </summary> /// <param name="attributes">A collection of attributes which are used to construct <see cref="BindingInfo"/> /// </param> /// <returns>A new instance of <see cref="BindingInfo"/>.</returns> public static BindingInfo GetBindingInfo(IEnumerable <object> attributes) { var bindingInfo = new BindingInfo(); var isBindingInfoPresent = false; // BinderModelName foreach (var binderModelNameAttribute in attributes.OfType <IModelNameProvider>()) { isBindingInfoPresent = true; if (binderModelNameAttribute?.Name != null) { bindingInfo.BinderModelName = binderModelNameAttribute.Name; break; } } // BinderType foreach (var binderTypeAttribute in attributes.OfType <IBinderTypeProviderMetadata>()) { isBindingInfoPresent = true; if (binderTypeAttribute.BinderType != null) { bindingInfo.BinderType = binderTypeAttribute.BinderType; break; } } // BindingSource foreach (var bindingSourceAttribute in attributes.OfType <IBindingSourceMetadata>()) { isBindingInfoPresent = true; if (bindingSourceAttribute.BindingSource != null) { bindingInfo.BindingSource = bindingSourceAttribute.BindingSource; break; } } // PropertyFilterProvider var propertyFilterProviders = attributes.OfType <IPropertyFilterProvider>().ToArray(); if (propertyFilterProviders.Length == 1) { isBindingInfoPresent = true; bindingInfo.PropertyFilterProvider = propertyFilterProviders[0]; } else if (propertyFilterProviders.Length > 1) { isBindingInfoPresent = true; bindingInfo.PropertyFilterProvider = new CompositePropertyFilterProvider(propertyFilterProviders); } // RequestPredicate foreach (var requestPredicateProvider in attributes.OfType <IRequestPredicateProvider>()) { isBindingInfoPresent = true; if (requestPredicateProvider.RequestPredicate != null) { bindingInfo.RequestPredicate = requestPredicateProvider.RequestPredicate; break; } } return(isBindingInfoPresent ? bindingInfo : null); }