예제 #1
0
        public void GetKeysFromPrefixAsync_ReturnsEmptyDictionaryIfNoValueProviderReturnsValues()
        {
            // Arrange
            var provider1 = Mock.Of<IValueProvider>();
            var provider2 = Mock.Of<IValueProvider>();
            var provider = new CompositeValueProvider() { provider1, provider2 };

            // Act
            var values = provider.GetKeysFromPrefix("prefix");

            // Assert
            Assert.Empty(values);
        }
예제 #2
0
        public void FilterReturnsItself_ForAnyClassRegisteredAsGenericParam(IBindingSourceMetadata metadata)
        {
            // Arrange
            var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

            var valueProvider1 = GetMockValueProvider("Test");
            var valueProvider2 = GetMockValueProvider("Unrelated");

            var provider = new CompositeValueProvider() { valueProvider1.Object, valueProvider2.Object };

            // Act
            var result = provider.Filter(metadata.BindingSource);

            // Assert
            var valueProvider = Assert.IsType<CompositeValueProvider>(result);
            var filteredProvider = Assert.Single(valueProvider);

            // should not be unrelated metadata.
            Assert.Same(valueProvider1.Object, filteredProvider);
        }
        public void GetKeysFromPrefixAsync_ReturnsResultFromFirstValueProviderThatReturnsValues()
        {
            // Arrange
            var provider1 = Mock.Of<IValueProvider>();
            var dictionary = new Dictionary<string, string>(StringComparer.Ordinal)
            {
                { "prefix-test", "some-value" },
            };
            var provider2 = new Mock<IEnumerableValueProvider>();
            provider2.Setup(p => p.GetKeysFromPrefix("prefix"))
                     .Returns(dictionary)
                     .Verifiable();
            var provider = new CompositeValueProvider() { provider1, provider2.Object };

            // Act
            var values = provider.GetKeysFromPrefix("prefix");

            // Assert
            var result = Assert.Single(values);
            Assert.Equal("prefix-test", result.Key);
            Assert.Equal("some-value", result.Value);
            provider2.Verify();
        }
예제 #4
0
 private static CompositeValueProvider CreateDefaultValueProvider()
 {
     var result = new CompositeValueProvider();
     result.Add(new RouteValueProvider(BindingSource.Path, new RouteValueDictionary()));
     result.Add(new QueryStringValueProvider(
         BindingSource.Query,
         new QueryCollection(),
         CultureInfo.InvariantCulture));
     result.Add(new FormValueProvider(
         BindingSource.Form,
         new FormCollection(new Dictionary<string, StringValues>()),
         CultureInfo.CurrentCulture));
     return result;
 }
        public void ValueProviderToString_With_CompositeProvider_Formats()
        {
            // Arrange
            List<IValueProvider> providers = new List<IValueProvider>()
                                                 {
                                                    new ElementalValueProvider("unused", 1, CultureInfo.CurrentCulture),
                                                    new NameValueCollectionValueProvider(() => null, CultureInfo.CurrentCulture)
                                                 };

            CompositeValueProvider compositeProvider = new CompositeValueProvider(providers);
            string expected = String.Format(
                                "{0}({1}, {2})", 
                                typeof(CompositeValueProvider).Name, 
                                typeof(ElementalValueProvider).Name,
                                typeof(NameValueCollectionValueProvider).Name);

            // Act
            string actual = FormattingUtilities.ValueProviderToString(compositeProvider);

            // Assert
            Assert.Equal(expected, actual);
        }
예제 #6
0
        public static ControllerBinderDelegate?CreateBinderDelegate(
            ParameterBinder parameterBinder,
            IModelBinderFactory modelBinderFactory,
            IModelMetadataProvider modelMetadataProvider,
            ControllerActionDescriptor actionDescriptor,
            MvcOptions mvcOptions)
        {
            if (parameterBinder == null)
            {
                throw new ArgumentNullException(nameof(parameterBinder));
            }

            if (modelBinderFactory == null)
            {
                throw new ArgumentNullException(nameof(modelBinderFactory));
            }

            if (modelMetadataProvider == null)
            {
                throw new ArgumentNullException(nameof(modelMetadataProvider));
            }

            if (actionDescriptor == null)
            {
                throw new ArgumentNullException(nameof(actionDescriptor));
            }

            if (mvcOptions == null)
            {
                throw new ArgumentNullException(nameof(mvcOptions));
            }

            var parameterBindingInfo = GetParameterBindingInfo(
                modelBinderFactory,
                modelMetadataProvider,
                actionDescriptor);
            var propertyBindingInfo = GetPropertyBindingInfo(modelBinderFactory, modelMetadataProvider, actionDescriptor);

            if (parameterBindingInfo == null && propertyBindingInfo == null)
            {
                return(null);
            }

            var parameters = actionDescriptor.Parameters switch
            {
                List <ParameterDescriptor> list => list.ToArray(),
                _ => actionDescriptor.Parameters.ToArray()
            };

            var properties = actionDescriptor.BoundProperties switch
            {
                List <ParameterDescriptor> list => list.ToArray(),
                _ => actionDescriptor.BoundProperties.ToArray()
            };

            return(Bind);

            async Task Bind(ControllerContext controllerContext, object controller, Dictionary <string, object?> arguments)
            {
                var(success, valueProvider) = await CompositeValueProvider.TryCreateAsync(controllerContext, controllerContext.ValueProviderFactories);

                if (!success)
                {
                    return;
                }

                Debug.Assert(valueProvider is not null);

                for (var i = 0; i < parameters.Length; i++)
                {
                    var parameter     = parameters[i];
                    var bindingInfo   = parameterBindingInfo ![i];
                    var modelMetadata = bindingInfo.ModelMetadata;

                    if (!modelMetadata.IsBindingAllowed)
                    {
                        continue;
                    }

                    var result = await parameterBinder.BindModelAsync(
                        controllerContext,
                        bindingInfo.ModelBinder,
                        valueProvider,
                        parameter,
                        modelMetadata,
                        value : null,
                        container : null); // Parameters do not have containers.

                    if (result.IsModelSet)
                    {
                        arguments[parameter.Name] = result.Model;
                    }
                }

                for (var i = 0; i < properties.Length; i++)
                {
                    var property      = properties[i];
                    var bindingInfo   = propertyBindingInfo ![i];
        public static ControllerBinderDelegate CreateBinderDelegate(
            ParameterBinder parameterBinder,
            IModelBinderFactory modelBinderFactory,
            IModelMetadataProvider modelMetadataProvider,
            ControllerActionDescriptor actionDescriptor,
            MvcOptions mvcOptions)
        {
            if (parameterBinder == null)
            {
                throw new ArgumentNullException(nameof(parameterBinder));
            }

            if (modelBinderFactory == null)
            {
                throw new ArgumentNullException(nameof(modelBinderFactory));
            }

            if (modelMetadataProvider == null)
            {
                throw new ArgumentNullException(nameof(modelMetadataProvider));
            }

            if (actionDescriptor == null)
            {
                throw new ArgumentNullException(nameof(actionDescriptor));
            }

            if (mvcOptions == null)
            {
                throw new ArgumentNullException(nameof(mvcOptions));
            }

            var parameterBindingInfo = GetParameterBindingInfo(
                modelBinderFactory,
                modelMetadataProvider,
                actionDescriptor,
                mvcOptions);
            var propertyBindingInfo = GetPropertyBindingInfo(modelBinderFactory, modelMetadataProvider, actionDescriptor);

            if (parameterBindingInfo == null && propertyBindingInfo == null)
            {
                return(null);
            }

            return(Bind);

            async Task Bind(ControllerContext controllerContext, object controller, Dictionary <string, object> arguments)
            {
                var valueProvider = await CompositeValueProvider.CreateAsync(controllerContext);

                var parameters = actionDescriptor.Parameters;

                for (var i = 0; i < parameters.Count; i++)
                {
                    var parameter     = parameters[i];
                    var bindingInfo   = parameterBindingInfo[i];
                    var modelMetadata = bindingInfo.ModelMetadata;

                    if (!modelMetadata.IsBindingAllowed)
                    {
                        continue;
                    }

                    var result = await parameterBinder.BindModelAsync(
                        controllerContext,
                        bindingInfo.ModelBinder,
                        valueProvider,
                        parameter,
                        modelMetadata,
                        value : null);

                    if (result.IsModelSet)
                    {
                        arguments[parameter.Name] = result.Model;
                    }
                }

                var properties = actionDescriptor.BoundProperties;

                for (var i = 0; i < properties.Count; i++)
                {
                    var property      = properties[i];
                    var bindingInfo   = propertyBindingInfo[i];
                    var modelMetadata = bindingInfo.ModelMetadata;

                    if (!modelMetadata.IsBindingAllowed)
                    {
                        continue;
                    }

                    var result = await parameterBinder.BindModelAsync(
                        controllerContext,
                        bindingInfo.ModelBinder,
                        valueProvider,
                        property,
                        modelMetadata,
                        value : null);

                    if (result.IsModelSet)
                    {
                        PropertyValueSetter.SetValue(bindingInfo.ModelMetadata, controller, result.Model);
                    }
                }
            }
        }
        GetBindingInfo(ActionDescriptor actionDescriptor, RouteContext routeContext)
        {
            if (actionDescriptor == null)
            {
                throw new ArgumentNullException(nameof(actionDescriptor));
            }

            var bindingResult = (new Dictionary <string, object>(), new Dictionary <ModelMetadata, object>());

            var parameterBindingInfo =
                GetParameterBindingInfo(
                    modelBinderFactory,
                    modelMetadataProvider,
                    actionDescriptor,
                    mvcOptions);

            BinderItem[] propertyBindingInfo = null;
            if (actionDescriptor is ControllerActionDescriptor)
            {
                propertyBindingInfo =
                    GetPropertyBindingInfo(
                        modelBinderFactory,
                        modelMetadataProvider,
                        actionDescriptor as ControllerActionDescriptor);
            }

            if (parameterBindingInfo == null && propertyBindingInfo == null)
            {
                return(bindingResult);
            }

            //Bind(ControllerContext controllerContext, object controller, Dictionary<string, object> arguments)

            var actionContext = new ActionContext(routeContext.HttpContext, routeContext.RouteData, actionDescriptor);

            var controllerContext = new ControllerContext(actionContext)
            {
                ValueProviderFactories =
                    new CopyOnWriteList <IValueProviderFactory>(mvcOptions.ValueProviderFactories.ToArray()),
            };

            controllerContext.ModelState.MaxAllowedErrors = mvcOptions.MaxModelValidationErrors;

            var valueProvider = await CompositeValueProvider.CreateAsync(controllerContext);

            var parameters = actionDescriptor.Parameters;

            if (parameterBindingInfo != null)
            {
                for (var i = 0; i < parameters.Count; i++)
                {
                    var parameter     = parameters[i];
                    var bindingInfo   = parameterBindingInfo[i];
                    var modelMetadata = bindingInfo.ModelMetadata;

                    if (!modelMetadata.IsBindingAllowed)
                    {
                        continue;
                    }

                    var result = await parameterBinder.BindModelAsync(
                        controllerContext,
                        bindingInfo.ModelBinder,
                        valueProvider,
                        parameter,
                        modelMetadata,
                        value : null);

                    if (result.IsModelSet)
                    {
                        bindingResult.Item1[parameter.Name] = result.Model;
                    }
                }
            }

            var properties = actionDescriptor.BoundProperties;

            if (propertyBindingInfo != null && properties != null)
            {
                for (var i = 0; i < properties.Count; i++)
                {
                    var property      = properties[i];
                    var bindingInfo   = propertyBindingInfo[i];
                    var modelMetadata = bindingInfo.ModelMetadata;

                    if (!modelMetadata.IsBindingAllowed)
                    {
                        continue;
                    }

                    var result = await parameterBinder.BindModelAsync(
                        controllerContext,
                        bindingInfo.ModelBinder,
                        valueProvider,
                        property,
                        modelMetadata,
                        value : null);

                    if (result.IsModelSet)
                    {
                        //PropertyValueSetter.SetValue(bindingInfo.ModelMetadata, controller, result.Model);
                        bindingResult.Item2[bindingInfo.ModelMetadata] = result.Model;
                    }
                }
            }
            return(bindingResult);
        }
예제 #9
0
        public static PageHandlerBinderDelegate CreateHandlerBinder(
            ParameterBinder parameterBinder,
            IModelMetadataProvider modelMetadataProvider,
            IModelBinderFactory modelBinderFactory,
            CompiledPageActionDescriptor actionDescriptor,
            HandlerMethodDescriptor handler,
            MvcOptions mvcOptions)
        {
            if (handler.Parameters == null || handler.Parameters.Count == 0)
            {
                return(NullHandlerBinder);
            }

            var handlerType          = actionDescriptor.HandlerTypeInfo.AsType();
            var parameterBindingInfo = new BinderItem[handler.Parameters.Count];

            for (var i = 0; i < parameterBindingInfo.Length; i++)
            {
                var           parameter = handler.Parameters[i];
                ModelMetadata metadata;
                if (mvcOptions.AllowValidatingTopLevelNodes &&
                    modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase)
                {
                    // The default model metadata provider derives from ModelMetadataProvider
                    // and can therefore supply information about attributes applied to parameters.
                    metadata = modelMetadataProviderBase.GetMetadataForParameter(parameter.ParameterInfo);
                }
                else
                {
                    // For backward compatibility, if there's a custom model metadata provider that
                    // only implements the older IModelMetadataProvider interface, access the more
                    // limited metadata information it supplies. In this scenario, validation attributes
                    // are not supported on parameters.
                    metadata = modelMetadataProvider.GetMetadataForType(parameter.ParameterType);
                }

                var binder = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext
                {
                    BindingInfo = parameter.BindingInfo,
                    Metadata    = metadata,
                    CacheToken  = parameter,
                });

                parameterBindingInfo[i] = new BinderItem(binder, metadata);
            }

            return(Bind);

            async Task Bind(PageContext pageContext, IDictionary <string, object> arguments)
            {
                var valueProvider = await CompositeValueProvider.CreateAsync(pageContext, pageContext.ValueProviderFactories);

                for (var i = 0; i < parameterBindingInfo.Length; i++)
                {
                    var parameter     = handler.Parameters[i];
                    var bindingInfo   = parameterBindingInfo[i];
                    var modelMetadata = bindingInfo.ModelMetadata;

                    if (!modelMetadata.IsBindingAllowed)
                    {
                        continue;
                    }

                    var result = await parameterBinder.BindModelAsync(
                        pageContext,
                        bindingInfo.ModelBinder,
                        valueProvider,
                        parameter,
                        modelMetadata,
                        value : null);

                    if (result.IsModelSet)
                    {
                        arguments[parameter.Name] = result.Model;
                    }
                }
            }
        }
예제 #10
0
        public static Func <PageContext, object, Task> CreatePropertyBinder(
            ParameterBinder parameterBinder,
            IModelMetadataProvider modelMetadataProvider,
            IModelBinderFactory modelBinderFactory,
            CompiledPageActionDescriptor actionDescriptor)
        {
            if (parameterBinder == null)
            {
                throw new ArgumentNullException(nameof(parameterBinder));
            }

            if (actionDescriptor == null)
            {
                throw new ArgumentNullException(nameof(actionDescriptor));
            }

            var properties = actionDescriptor.BoundProperties;

            if (properties == null || properties.Count == 0)
            {
                return(NullPropertyBinder);
            }

            var handlerType         = actionDescriptor.HandlerTypeInfo.AsType();
            var propertyBindingInfo = new BinderItem[properties.Count];

            for (var i = 0; i < properties.Count; i++)
            {
                var property = properties[i];
                var metadata = modelMetadataProvider.GetMetadataForProperty(handlerType, property.Name);
                var binder   = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext
                {
                    BindingInfo = property.BindingInfo,
                    Metadata    = metadata,
                    CacheToken  = property,
                });

                propertyBindingInfo[i] = new BinderItem(binder, metadata);
            }

            return(Bind);

            async Task Bind(PageContext pageContext, object instance)
            {
                var valueProvider = await CompositeValueProvider.CreateAsync(pageContext, pageContext.ValueProviderFactories);

                for (var i = 0; i < properties.Count; i++)
                {
                    var property      = properties[i];
                    var bindingInfo   = propertyBindingInfo[i];
                    var modelMetadata = bindingInfo.ModelMetadata;

                    if (!modelMetadata.IsBindingAllowed)
                    {
                        continue;
                    }

                    var result = await parameterBinder.BindModelAsync(
                        pageContext,
                        bindingInfo.ModelBinder,
                        valueProvider,
                        property,
                        modelMetadata,
                        value : null);

                    if (result.IsModelSet)
                    {
                        PropertyValueSetter.SetValue(bindingInfo.ModelMetadata, instance, result.Model);
                    }
                }
            }
        }
예제 #11
0
        public JObject ParseProperties(string modelName, CompositeValueProvider valueProvider, JObject model)
        {
            IDictionary <string, string> properties = valueProvider.GetKeysFromPrefix(modelName);

            _logger.LogDebug("DynamicModelBinder property count is " + properties.Count() + " for " + modelName);

            List <string> subModelNames = new List <string>();
            List <string> arrModelNames = new List <string>();

            foreach (var property in properties)
            {
                var subProperties = valueProvider.GetKeysFromPrefix(property.Value);

                var key      = property.Value;
                var propName = property.Key;

                if (subProperties.Count == 0)
                {
                    if (!propName.Contains("RequestVerification"))
                    {
                        if (!model.ContainsKey(propName))
                        {
                            model.Add(propName, GetValue(valueProvider, key));
                        }
                        else
                        {
                            model[propName] = GetValue(valueProvider, key);
                        }
                    }
                }
                else if (subProperties.Any(sp => sp.Value.Contains("[")))
                {
                    if (!arrModelNames.Contains(propName))
                    {
                        arrModelNames.Add(propName);
                    }
                }
                else
                {
                    if (!subModelNames.Contains(propName))
                    {
                        subModelNames.Add(propName);
                    }
                }
            }

            foreach (var subModelName in subModelNames)
            {
                var key = properties[subModelName];

                JObject val = ParseProperties(key, valueProvider, model.ContainsKey(subModelName) ? (JObject)model[subModelName] : new JObject());
                if (!model.ContainsKey(subModelName))
                {
                    model.Add(subModelName, val);
                }
                else
                {
                    model[subModelName] = val;
                }
            }

            foreach (var arrModelName in arrModelNames)
            {
                var key            = properties[arrModelName];
                var arrKeys        = valueProvider.GetKeysFromPrefix(key);
                var isComplexArray = false;
                foreach (var arrKey in arrKeys)
                {
                    var subProperties = valueProvider.GetKeysFromPrefix(arrKey.Value);
                    if (subProperties.Count > 0)
                    {
                        isComplexArray = true;
                    }
                }

                JToken arrResult = null;

                List <object> vals = new List <object>();

                vals.Cast <Object>().ToList();
                if (isComplexArray)
                {
                    foreach (var arrKey in arrKeys)
                    {
                        var arrItemKey = arrKey.Value;

                        var subProperties = valueProvider.GetKeysFromPrefix(arrItemKey);
                        if (subProperties.Count > 0)
                        {
                            object val = ParseProperties(arrItemKey, valueProvider, new JObject());
                            vals.Add(val);
                        }
                    }

                    arrResult = new JArray(vals);
                }
                else
                {
                    foreach (var arrKey in arrKeys)
                    {
                        var arrItemKey = arrKey.Value;
                        vals.Add(GetValue(valueProvider, arrItemKey));
                    }

                    arrResult = new JArray(vals);

                    bool castToType = true;
                    Type itemType   = vals[0].GetType();
                    foreach (var item in vals)
                    {
                        if (item.GetType() != itemType)
                        {
                            castToType = false;
                            break;
                        }
                    }

                    if (castToType)
                    {
                        var ienumerable = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(itemType).Invoke(null, new object[] { vals });
                        arrResult = new JArray(typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(itemType).Invoke(null, new object[] { ienumerable }));
                    }
                }

                if (!model.ContainsKey(arrModelName))
                {
                    model.Add(arrModelName, arrResult);
                }
                else
                {
                    model[arrModelName] = arrResult;
                }
            }

            return(model);
        }
예제 #12
0
        public async Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }

            var modelName = bindingContext.ModelName;

            var valueProvider = await CompositeValueProvider.CreateAsync(bindingContext.ActionContext, new CopyOnWriteList <IValueProviderFactory>(_valueProviderFactories));

            JObject model = bindingContext.Model != null ? (JObject)bindingContext.Model : new JObject();

            //Form/Route/Query
            if (bindingContext.ModelMetadata.BindingSource == null || bindingContext.ModelMetadata.BindingSource.CanAcceptDataFrom(BindingSource.Form) || bindingContext.ModelMetadata.BindingSource.CanAcceptDataFrom(BindingSource.Query) || bindingContext.ModelMetadata.BindingSource.CanAcceptDataFrom(BindingSource.Path))
            {
                model = ParseProperties(modelName, valueProvider, model);
            }

            //Route
            if (bindingContext.IsTopLevelObject)
            {
                if (bindingContext.ModelMetadata.BindingSource == null || bindingContext.ModelMetadata.BindingSource.CanAcceptDataFrom(BindingSource.Path))
                {
                    foreach (var kvp in bindingContext.ActionContext.RouteData.Values)
                    {
                        if (kvp.Key != "area" && kvp.Key != "controller" && kvp.Key != "action" && !model.ContainsKey(kvp.Key))
                        {
                            var stringValue = kvp.Value as string ?? Convert.ToString(kvp.Value, CultureInfo.InvariantCulture) ?? string.Empty;
                            if (!model.ContainsKey(kvp.Key))
                            {
                                model.Add(kvp.Key, GetValue(stringValue));
                            }
                            else if (bindingContext.HttpContext.Request.Query.ContainsKey(kvp.Key) && !bindingContext.HttpContext.Request.Form.ContainsKey(kvp.Key))
                            {
                                model[kvp.Key] = GetValue(stringValue);
                            }
                        }
                    }
                }
            }

            //Query
            if (bindingContext.IsTopLevelObject)
            {
                if (bindingContext.ModelMetadata.BindingSource == null || bindingContext.ModelMetadata.BindingSource.CanAcceptDataFrom(BindingSource.Query))
                {
                    foreach (var kvp in bindingContext.HttpContext.Request.Query)
                    {
                        if (!model.ContainsKey(kvp.Key))
                        {
                            model.Add(kvp.Key, GetValue(kvp.Value));
                        }
                        else if (!bindingContext.HttpContext.Request.Form.ContainsKey(kvp.Key) && !bindingContext.ActionContext.RouteData.Values.ContainsKey(kvp.Key))
                        {
                            model[kvp.Key] = GetValue(kvp.Value);
                        }
                    }
                }
            }

            bindingContext.Result = ModelBindingResult.Success(model);
        }