Ejemplo n.º 1
0
    public async Task BindModelAsync_ForProperty_UsesValidationFromActualModel_WhenDerivedModelIsSet()
    {
        // Arrange
        var property            = typeof(TestController).GetProperty(nameof(TestController.Model));
        var parameterDescriptor = new ControllerBoundPropertyDescriptor
        {
            PropertyInfo = property,
            Name         = property.Name,
        };

        var actionContext         = GetControllerContext();
        var modelMetadataProvider = new TestModelMetadataProvider();

        var model = new DerivedModel();
        var modelBindingResult = ModelBindingResult.Success(model);

        var parameterBinder = new ParameterBinder(
            modelMetadataProvider,
            Mock.Of <IModelBinderFactory>(),
            new DefaultObjectValidator(
                modelMetadataProvider,
                new[] { TestModelValidatorProvider.CreateDefaultProvider() },
                new MvcOptions()),
            _optionsAccessor,
            NullLoggerFactory.Instance);

        var modelMetadata = modelMetadataProvider.GetMetadataForProperty(property.DeclaringType, property.Name);
        var modelBinder   = CreateMockModelBinder(modelBindingResult);

        // Act
        var result = await parameterBinder.BindModelAsync(
            actionContext,
            modelBinder,
            CreateMockValueProvider(),
            parameterDescriptor,
            modelMetadata,
            value : null);

        // Assert
        Assert.True(result.IsModelSet);
        Assert.Same(model, result.Model);

        Assert.False(actionContext.ModelState.IsValid);
        Assert.Collection(
            actionContext.ModelState,
            kvp =>
        {
            Assert.Equal($"{property.Name}.{nameof(DerivedPerson.DerivedProperty)}", kvp.Key);
            var error = Assert.Single(kvp.Value.Errors);
            Assert.Equal("The DerivedProperty field is required.", error.ErrorMessage);
        });
    }
        protected override async Task <ModelBindingResult> BindAsync(PageContext pageContext, object value, string name, Type type)
        {
            var valueProvider = await GetCompositeValueProvider(pageContext);

            var parameterDescriptor = new ParameterDescriptor
            {
                BindingInfo   = null,
                Name          = name,
                ParameterType = type,
            };

            return(await _parameterBinder.BindModelAsync(pageContext, valueProvider, parameterDescriptor, value));
        }
Ejemplo n.º 3
0
        protected override async Task <ModelBindingResult> BindAsync(PageContext pageContext, object value, string name, Type type)
        {
            var valueProvider = await GetCompositeValueProvider(pageContext);

            var parameterDescriptor = new ParameterDescriptor
            {
                BindingInfo   = null,
                Name          = name,
                ParameterType = type,
            };

#pragma warning disable CS0618 // Type or member is obsolete
            return(await _parameterBinder.BindModelAsync(pageContext, valueProvider, parameterDescriptor, value));

#pragma warning restore CS0618 // Type or member is obsolete
        }
Ejemplo n.º 4
0
        private static async Task BindPropertiesAsync(
            ParameterBinder parameterBinder,
            PageContext pageContext,
            object instance,
            IList <ParameterDescriptor> properties,
            IList <ModelMetadata> metadata)
        {
            var valueProvider = await CompositeValueProvider.CreateAsync(pageContext, pageContext.ValueProviderFactories);

            for (var i = 0; i < properties.Count; i++)
            {
                var result = await parameterBinder.BindModelAsync(pageContext, valueProvider, properties[i]);

                if (result.IsModelSet)
                {
                    PropertyValueSetter.SetValue(metadata[i], instance, result.Model);
                }
            }
        }
Ejemplo n.º 5
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,
                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);
                    }
                }
            }
        }
Ejemplo n.º 6
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;
                    }
                }
            }
        }
Ejemplo n.º 7
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);
                    }
                }
            }
        }
Ejemplo n.º 8
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];
Ejemplo n.º 9
0
    public async Task BindModelAsync_ForOverlappingParametersWithSuppressions_InValid_WithInValidSecondParameter()
    {
        // Arrange
        var parameterDescriptor = new ParameterDescriptor
        {
            Name          = "patchDocument",
            ParameterType = typeof(IJsonPatchDocument),
        };

        var actionContext = GetControllerContext();
        var modelState    = actionContext.ModelState;

        // First ModelState key is not empty to match SimpleTypeModelBinder.
        modelState.SetModelValue("id", "notAGuid", "notAGuid");
        modelState.AddModelError("id", "This is not valid.");

        // Second ModelState key is empty to match BodyModelBinder.
        modelState.AddModelError(string.Empty, "This is also not valid.");

        var modelMetadataProvider = new TestModelMetadataProvider();

        modelMetadataProvider.ForType <IJsonPatchDocument>().ValidationDetails(v => v.ValidateChildren = false);
        var modelMetadata = modelMetadataProvider.GetMetadataForType(typeof(IJsonPatchDocument));

        var parameterBinder = new ParameterBinder(
            modelMetadataProvider,
            Mock.Of <IModelBinderFactory>(),
            new DefaultObjectValidator(
                modelMetadataProvider,
                new[] { TestModelValidatorProvider.CreateDefaultProvider() },
                new MvcOptions()),
            _optionsAccessor,
            NullLoggerFactory.Instance);

        var modelBindingResult = ModelBindingResult.Failed();
        var modelBinder        = CreateMockModelBinder(modelBindingResult);

        // Act
        var result = await parameterBinder.BindModelAsync(
            actionContext,
            modelBinder,
            new SimpleValueProvider(),
            parameterDescriptor,
            modelMetadata,
            value : null);

        // Assert
        Assert.False(result.IsModelSet);
        Assert.False(modelState.IsValid);
        Assert.Collection(
            modelState,
            kvp =>
        {
            Assert.Empty(kvp.Key);
            Assert.Equal(ModelValidationState.Invalid, kvp.Value.ValidationState);
            var error = Assert.Single(kvp.Value.Errors);
            Assert.Equal("This is also not valid.", error.ErrorMessage);
        },
            kvp =>
        {
            Assert.Equal("id", kvp.Key);
            Assert.Equal(ModelValidationState.Invalid, kvp.Value.ValidationState);
            var error = Assert.Single(kvp.Value.Errors);
            Assert.Equal("This is not valid.", error.ErrorMessage);
        });
    }
        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);
        }