/// <summary> /// Attempts to bind the model using formatters. /// </summary> /// <param name="bindingContext">The <see cref="ModelBindingContext"/>.</param> /// <returns> /// A <see cref="Task{ModelBindingResult}"/> which when completed returns a <see cref="ModelBindingResult"/>. /// </returns> private async Task <ModelBindingResult> BindModelCoreAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } // For compatibility with MVC 5.0 for top level object we want to consider an empty key instead of // the parameter name/a custom name. In all other cases (like when binding body to a property) we // consider the entire ModelName as a prefix. var modelBindingKey = bindingContext.IsTopLevelObject ? string.Empty : bindingContext.ModelName; var httpContext = bindingContext.OperationBindingContext.HttpContext; var formatterContext = new InputFormatterContext( httpContext, modelBindingKey, bindingContext.ModelState, bindingContext.ModelMetadata, _readerFactory); var formatters = bindingContext.OperationBindingContext.InputFormatters; var formatter = formatters.FirstOrDefault(f => f.CanRead(formatterContext)); if (formatter == null) { var unsupportedContentType = Resources.FormatUnsupportedContentType( bindingContext.OperationBindingContext.HttpContext.Request.ContentType); bindingContext.ModelState.AddModelError(modelBindingKey, unsupportedContentType); // This model binder is the only handler for the Body binding source and it cannot run twice. Always // tell the model binding system to skip other model binders and never to fall back i.e. indicate a // fatal error. return(ModelBindingResult.Failed(modelBindingKey)); } try { var previousCount = bindingContext.ModelState.ErrorCount; var result = await formatter.ReadAsync(formatterContext); var model = result.Model; // Ensure a "modelBindingKey" entry exists whether or not formatting was successful. bindingContext.ModelState.SetModelValue(modelBindingKey, rawValue: model, attemptedValue: null); if (result.HasError) { // Formatter encountered an error. Do not use the model it returned. As above, tell the model // binding system to skip other model binders and never to fall back. return(ModelBindingResult.Failed(modelBindingKey)); } return(ModelBindingResult.Success(modelBindingKey, model)); } catch (Exception ex) { bindingContext.ModelState.AddModelError(modelBindingKey, ex, bindingContext.ModelMetadata); // This model binder is the only handler for the Body binding source and it cannot run twice. Always // tell the model binding system to skip other model binders and never to fall back i.e. indicate a // fatal error. return(ModelBindingResult.Failed(modelBindingKey)); } }
/// <inheritdoc /> public virtual async Task <ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext) { ModelBindingHelper.ValidateBindingContext(bindingContext); var model = bindingContext.Model; if (!bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) { // If we failed to find data for a top-level model, then generate a // default 'empty' model (or use existing Model) and return it. if (bindingContext.IsTopLevelObject) { if (model == null) { model = CreateEmptyCollection(bindingContext.ModelType); } return(ModelBindingResult.Success(bindingContext.ModelName, model)); } return(ModelBindingResult.NoResult); } var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); CollectionResult result; if (valueProviderResult == ValueProviderResult.None) { result = await BindComplexCollection(bindingContext); } else { result = await BindSimpleCollection(bindingContext, valueProviderResult); } var boundCollection = result.Model; if (model == null) { model = ConvertToCollectionType(bindingContext.ModelType, boundCollection); } else { // Special case for TryUpdateModelAsync(collection, ...) scenarios. Model is null in all other cases. CopyToModel(model, boundCollection); } Debug.Assert(model != null); if (result.ValidationStrategy != null) { bindingContext.ValidationState.Add(model, new ValidationStateEntry() { Strategy = result.ValidationStrategy, }); } if (valueProviderResult != ValueProviderResult.None) { // If we did simple binding, then modelstate should be updated to reflect what we bound for ModelName. // If we did complex binding, there will already be an entry for each index. bindingContext.ModelState.SetModelValue( bindingContext.ModelName, valueProviderResult); } return(ModelBindingResult.Success(bindingContext.ModelName, model)); }