211 lines
7.8 KiB
C#
211 lines
7.8 KiB
C#
// Copyright (c) .NET Foundation. 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.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|
|
|
namespace Microsoft.AspNetCore.Mvc.Internal
|
|
{
|
|
// Note: changes made to binding behavior in type should also be made to PageBinderFactory.
|
|
public static class ControllerBinderDelegateProvider
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static BinderItem[] GetParameterBindingInfo(
|
|
IModelBinderFactory modelBinderFactory,
|
|
IModelMetadataProvider modelMetadataProvider,
|
|
ControllerActionDescriptor actionDescriptor,
|
|
MvcOptions mvcOptions)
|
|
{
|
|
var parameters = actionDescriptor.Parameters;
|
|
if (parameters.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var parameterBindingInfo = new BinderItem[parameters.Count];
|
|
for (var i = 0; i < parameters.Count; i++)
|
|
{
|
|
var parameter = parameters[i];
|
|
|
|
ModelMetadata metadata;
|
|
if (mvcOptions.AllowValidatingTopLevelNodes &&
|
|
modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase &&
|
|
parameter is ControllerParameterDescriptor controllerParameterDescriptor)
|
|
{
|
|
// The default model metadata provider derives from ModelMetadataProvider
|
|
// and can therefore supply information about attributes applied to parameters.
|
|
metadata = modelMetadataProviderBase.GetMetadataForParameter(controllerParameterDescriptor.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 parameterBindingInfo;
|
|
}
|
|
|
|
private static BinderItem[] GetPropertyBindingInfo(
|
|
IModelBinderFactory modelBinderFactory,
|
|
IModelMetadataProvider modelMetadataProvider,
|
|
ControllerActionDescriptor actionDescriptor)
|
|
{
|
|
var properties = actionDescriptor.BoundProperties;
|
|
if (properties.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var propertyBindingInfo = new BinderItem[properties.Count];
|
|
var controllerType = actionDescriptor.ControllerTypeInfo.AsType();
|
|
for (var i = 0; i < properties.Count; i++)
|
|
{
|
|
var property = properties[i];
|
|
var metadata = modelMetadataProvider.GetMetadataForProperty(controllerType, property.Name);
|
|
var binder = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext
|
|
{
|
|
BindingInfo = property.BindingInfo,
|
|
Metadata = metadata,
|
|
CacheToken = property,
|
|
});
|
|
|
|
propertyBindingInfo[i] = new BinderItem(binder, metadata);
|
|
}
|
|
|
|
return propertyBindingInfo;
|
|
}
|
|
|
|
private struct BinderItem
|
|
{
|
|
public BinderItem(IModelBinder modelBinder, ModelMetadata modelMetadata)
|
|
{
|
|
ModelBinder = modelBinder;
|
|
ModelMetadata = modelMetadata;
|
|
}
|
|
|
|
public IModelBinder ModelBinder { get; }
|
|
|
|
public ModelMetadata ModelMetadata { get; }
|
|
}
|
|
}
|
|
}
|