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)); }
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 }
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); } } }
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); } } } }
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; } } } }
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); } } } }
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 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); }