aspnetcore/src/Microsoft.AspNet.Mvc.Core/DefaultControllerActionArgu...

158 lines
6.5 KiB
C#

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.Framework.Internal;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Provides a default implementation of <see cref="IControllerActionArgumentBinder"/>.
/// Uses ModelBinding to populate action parameters.
/// </summary>
public class DefaultControllerActionArgumentBinder : IControllerActionArgumentBinder
{
private readonly IModelMetadataProvider _modelMetadataProvider;
private readonly MvcOptions _options;
private readonly IObjectModelValidator _validator;
public DefaultControllerActionArgumentBinder(
IModelMetadataProvider modelMetadataProvider,
IObjectModelValidator validator,
IOptions<MvcOptions> optionsAccessor)
{
_modelMetadataProvider = modelMetadataProvider;
_options = optionsAccessor.Options;
_validator = validator;
}
public async Task<IDictionary<string, object>> BindActionArgumentsAsync(
ActionContext actionContext,
ActionBindingContext actionBindingContext,
object controller)
{
var actionDescriptor = actionContext.ActionDescriptor as ControllerActionDescriptor;
if (actionDescriptor == null)
{
throw new ArgumentException(
Resources.FormatActionDescriptorMustBeBasedOnControllerAction(
typeof(ControllerActionDescriptor)),
nameof(actionContext));
}
var operationBindingContext = GetOperationBindingContext(actionContext, actionBindingContext);
var controllerProperties = new Dictionary<string, object>(StringComparer.Ordinal);
await PopulateArgumentsAsync(
operationBindingContext,
actionContext.ModelState,
controllerProperties,
actionDescriptor.BoundProperties);
var controllerType = actionDescriptor.ControllerTypeInfo.AsType();
ActivateProperties(controller, controllerType, controllerProperties);
var actionArguments = new Dictionary<string, object>(StringComparer.Ordinal);
await PopulateArgumentsAsync(
operationBindingContext,
actionContext.ModelState,
actionArguments,
actionDescriptor.Parameters);
return actionArguments;
}
private void ActivateProperties(object controller, Type containerType, Dictionary<string, object> properties)
{
var propertyHelpers = PropertyHelper.GetProperties(controller);
foreach (var property in properties)
{
var propertyHelper = propertyHelpers.First(helper =>
string.Equals(helper.Name, property.Key, StringComparison.Ordinal));
if (propertyHelper.Property == null || !propertyHelper.Property.CanWrite)
{
// nothing to do
return;
}
propertyHelper.SetValue(controller, property.Value);
}
}
private async Task PopulateArgumentsAsync(
OperationBindingContext operationContext,
ModelStateDictionary modelState,
IDictionary<string, object> arguments,
IEnumerable<ParameterDescriptor> parameterMetadata)
{
modelState.MaxAllowedErrors = _options.MaxModelValidationErrors;
foreach (var parameter in parameterMetadata)
{
var metadata = _modelMetadataProvider.GetMetadataForType(parameter.ParameterType);
var parameterType = parameter.ParameterType;
var modelBindingContext = GetModelBindingContext(
parameter.Name,
metadata,
parameter.BindingInfo,
modelState,
operationContext);
var modelBindingResult = await operationContext.ModelBinder.BindModelAsync(modelBindingContext);
if (modelBindingResult != null && modelBindingResult.IsModelSet)
{
var modelExplorer = new ModelExplorer(
_modelMetadataProvider,
metadata,
modelBindingResult.Model);
arguments[parameter.Name] = modelBindingResult.Model;
var validationContext = new ModelValidationContext(
modelBindingResult.Key,
modelBindingContext.BindingSource,
operationContext.ValidatorProvider,
modelState,
modelExplorer);
_validator.Validate(validationContext);
}
}
}
private static ModelBindingContext GetModelBindingContext(
string parameterName,
ModelMetadata metadata,
BindingInfo bindingInfo,
ModelStateDictionary modelState,
OperationBindingContext operationBindingContext)
{
var modelBindingContext = ModelBindingContext.GetModelBindingContext(
metadata,
bindingInfo,
parameterName);
modelBindingContext.ModelState = modelState;
modelBindingContext.ValueProvider = operationBindingContext.ValueProvider;
modelBindingContext.OperationBindingContext = operationBindingContext;
return modelBindingContext;
}
private OperationBindingContext GetOperationBindingContext(
ActionContext actionContext,
ActionBindingContext bindingContext)
{
return new OperationBindingContext
{
ModelBinder = bindingContext.ModelBinder,
ValidatorProvider = bindingContext.ValidatorProvider,
MetadataProvider = _modelMetadataProvider,
HttpContext = actionContext.HttpContext,
ValueProvider = bindingContext.ValueProvider,
};
}
}
}