Adding support for model binding specifically marked controller properties.
This commit is contained in:
parent
c082d4aa49
commit
adeb1ba194
|
|
@ -30,6 +30,11 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
public IList<ParameterDescriptor> Parameters { get; set; }
|
public IList<ParameterDescriptor> Parameters { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The set of properties which are model bound.
|
||||||
|
/// </summary>
|
||||||
|
public IList<ParameterDescriptor> BoundProperties { get; set; }
|
||||||
|
|
||||||
public IList<FilterDescriptor> FilterDescriptors { get; set; }
|
public IList<FilterDescriptor> FilterDescriptors { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
Filters = new List<IFilter>();
|
Filters = new List<IFilter>();
|
||||||
RouteConstraints = new List<IRouteConstraintProvider>();
|
RouteConstraints = new List<IRouteConstraintProvider>();
|
||||||
Properties = new Dictionary<object, object>();
|
Properties = new Dictionary<object, object>();
|
||||||
|
ControllerProperties = new List<PropertyModel>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControllerModel([NotNull] ControllerModel other)
|
public ControllerModel([NotNull] ControllerModel other)
|
||||||
|
|
@ -48,6 +49,8 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
ApiExplorer = new ApiExplorerModel(other.ApiExplorer);
|
ApiExplorer = new ApiExplorerModel(other.ApiExplorer);
|
||||||
AttributeRoutes = new List<AttributeRouteModel>(
|
AttributeRoutes = new List<AttributeRouteModel>(
|
||||||
other.AttributeRoutes.Select(a => new AttributeRouteModel(a)));
|
other.AttributeRoutes.Select(a => new AttributeRouteModel(a)));
|
||||||
|
ControllerProperties =
|
||||||
|
new List<PropertyModel>(other.ControllerProperties.Select(p => new PropertyModel(p)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<IActionConstraintMetadata> ActionConstraints { get; private set; }
|
public IList<IActionConstraintMetadata> ActionConstraints { get; private set; }
|
||||||
|
|
@ -76,6 +79,8 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
|
|
||||||
public TypeInfo ControllerType { get; private set; }
|
public TypeInfo ControllerType { get; private set; }
|
||||||
|
|
||||||
|
public IList<PropertyModel> ControllerProperties { get; }
|
||||||
|
|
||||||
public IList<IFilter> Filters { get; private set; }
|
public IList<IFilter> Filters { get; private set; }
|
||||||
|
|
||||||
public IList<IRouteConstraintProvider> RouteConstraints { get; private set; }
|
public IList<IRouteConstraintProvider> RouteConstraints { get; private set; }
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ using Microsoft.AspNet.Cors;
|
||||||
using Microsoft.AspNet.Cors.Core;
|
using Microsoft.AspNet.Cors.Core;
|
||||||
using Microsoft.AspNet.Mvc.Description;
|
using Microsoft.AspNet.Mvc.Description;
|
||||||
using Microsoft.AspNet.Mvc.Filters;
|
using Microsoft.AspNet.Mvc.Filters;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.Routing;
|
using Microsoft.AspNet.Mvc.Routing;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
using Microsoft.Framework.Logging;
|
using Microsoft.Framework.Logging;
|
||||||
|
|
@ -44,8 +45,9 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
public ControllerModel BuildControllerModel([NotNull] TypeInfo typeInfo)
|
public ControllerModel BuildControllerModel([NotNull] TypeInfo typeInfo)
|
||||||
{
|
{
|
||||||
var controllerModel = CreateControllerModel(typeInfo);
|
var controllerModel = CreateControllerModel(typeInfo);
|
||||||
|
var controllerType = typeInfo.AsType();
|
||||||
|
|
||||||
foreach (var methodInfo in typeInfo.AsType().GetMethods())
|
foreach (var methodInfo in controllerType.GetMethods())
|
||||||
{
|
{
|
||||||
var actionModels = _actionModelBuilder.BuildActionModels(typeInfo, methodInfo);
|
var actionModels = _actionModelBuilder.BuildActionModels(typeInfo, methodInfo);
|
||||||
if (actionModels != null)
|
if (actionModels != null)
|
||||||
|
|
@ -58,6 +60,17 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var propertyHelper in PropertyHelper.GetProperties(controllerType))
|
||||||
|
{
|
||||||
|
var propertyInfo = propertyHelper.Property;
|
||||||
|
var propertyModel = CreatePropertyModel(propertyInfo);
|
||||||
|
if (propertyModel != null)
|
||||||
|
{
|
||||||
|
propertyModel.Controller = controllerModel;
|
||||||
|
controllerModel.ControllerProperties.Add(propertyModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return controllerModel;
|
return controllerModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,6 +147,25 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
return controllerModel;
|
return controllerModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="PropertyModel"/> for the given <see cref="PropertyInfo"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyInfo">The <see cref="PropertyInfo"/>.</param>
|
||||||
|
/// <returns>A <see cref="PropertyModel"/> for the given <see cref="PropertyInfo"/>.</returns>
|
||||||
|
protected virtual PropertyModel CreatePropertyModel([NotNull] PropertyInfo propertyInfo)
|
||||||
|
{
|
||||||
|
// CoreCLR returns IEnumerable<Attribute> from GetCustomAttributes - the OfType<object>
|
||||||
|
// is needed to so that the result of ToArray() is object
|
||||||
|
var attributes = propertyInfo.GetCustomAttributes(inherit: true).OfType<object>().ToArray();
|
||||||
|
var propertyModel = new PropertyModel(propertyInfo, attributes);
|
||||||
|
var bindingInfo = BindingInfo.GetBindingInfo(attributes);
|
||||||
|
|
||||||
|
propertyModel.BindingInfo = bindingInfo;
|
||||||
|
propertyModel.PropertyName = propertyInfo.Name;
|
||||||
|
|
||||||
|
return propertyModel;
|
||||||
|
}
|
||||||
|
|
||||||
private static void AddRange<T>(IList<T> list, IEnumerable<T> items)
|
private static void AddRange<T>(IList<T> list, IEnumerable<T> items)
|
||||||
{
|
{
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.Framework.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A type which is used to represent a property in a <see cref="ControllerModel"/>.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("PropertyModel: Name={PropertyName}")]
|
||||||
|
public class PropertyModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="PropertyModel"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyInfo">The <see cref="PropertyInfo"/> for the underlying property.</param>
|
||||||
|
/// <param name="attributes">Any attributes which are annotated on the property.</param>
|
||||||
|
public PropertyModel([NotNull] PropertyInfo propertyInfo,
|
||||||
|
[NotNull] IReadOnlyList<object> attributes)
|
||||||
|
{
|
||||||
|
PropertyInfo = propertyInfo;
|
||||||
|
|
||||||
|
Attributes = new List<object>(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creats a new instance of <see cref="PropertyModel"/> from a given <see cref="PropertyModel"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">The <see cref="PropertyModel"/> which needs to be copied.</param>
|
||||||
|
public PropertyModel([NotNull] PropertyModel other)
|
||||||
|
{
|
||||||
|
Controller = other.Controller;
|
||||||
|
Attributes = new List<object>(other.Attributes);
|
||||||
|
BindingInfo = other.BindingInfo;
|
||||||
|
PropertyInfo = other.PropertyInfo;
|
||||||
|
PropertyName = other.PropertyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="ControllerModel"/> this <see cref="PropertyModel"/> is associated with.
|
||||||
|
/// </summary>
|
||||||
|
public ControllerModel Controller { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets any attributes which are annotated on the property.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<object> Attributes { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="BindingInfo"/> associated with this model.
|
||||||
|
/// </summary>
|
||||||
|
public BindingInfo BindingInfo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the underlying <see cref="PropertyInfo"/>.
|
||||||
|
/// </summary>
|
||||||
|
public PropertyInfo PropertyInfo { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the name of the property represented by this model.
|
||||||
|
/// </summary>
|
||||||
|
public string PropertyName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1293,9 +1293,10 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
var validationContext = new ModelValidationContext(
|
var validationContext = new ModelValidationContext(
|
||||||
modelName,
|
modelName,
|
||||||
BindingContext.ValidatorProvider,
|
bindingSource: null,
|
||||||
ModelState,
|
validatorProvider: BindingContext.ValidatorProvider,
|
||||||
modelExplorer);
|
modelState: ModelState,
|
||||||
|
modelExplorer: modelExplorer);
|
||||||
|
|
||||||
ObjectValidator.Validate(validationContext);
|
ObjectValidator.Validate(validationContext);
|
||||||
return ModelState.IsValid;
|
return ModelState.IsValid;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ using Microsoft.AspNet.Mvc.ApplicationModels;
|
||||||
using Microsoft.AspNet.Mvc.Core;
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
using Microsoft.AspNet.Mvc.Description;
|
using Microsoft.AspNet.Mvc.Description;
|
||||||
using Microsoft.AspNet.Mvc.Routing;
|
using Microsoft.AspNet.Mvc.Routing;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
|
|
@ -42,6 +43,12 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
foreach (var controller in application.Controllers)
|
foreach (var controller in application.Controllers)
|
||||||
{
|
{
|
||||||
|
// Only add properties which are explictly marked to bind.
|
||||||
|
// The attribute check is required for ModelBinder attribute.
|
||||||
|
var controllerPropertyDescriptors = controller.ControllerProperties
|
||||||
|
.Where(p => p.BindingInfo != null)
|
||||||
|
.Select(CreateParameterDescriptor)
|
||||||
|
.ToList();
|
||||||
foreach (var action in controller.Actions)
|
foreach (var action in controller.Actions)
|
||||||
{
|
{
|
||||||
// Controllers with multiple [Route] attributes (or user defined implementation of
|
// Controllers with multiple [Route] attributes (or user defined implementation of
|
||||||
|
|
@ -60,6 +67,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
AddRouteConstraints(removalConstraints, actionDescriptor, controller, action);
|
AddRouteConstraints(removalConstraints, actionDescriptor, controller, action);
|
||||||
AddProperties(actionDescriptor, action, controller, application);
|
AddProperties(actionDescriptor, action, controller, application);
|
||||||
|
|
||||||
|
actionDescriptor.BoundProperties = controllerPropertyDescriptors;
|
||||||
if (IsAttributeRoutedAction(actionDescriptor))
|
if (IsAttributeRoutedAction(actionDescriptor))
|
||||||
{
|
{
|
||||||
hasAttributeRoutes = true;
|
hasAttributeRoutes = true;
|
||||||
|
|
@ -272,13 +280,25 @@ namespace Microsoft.AspNet.Mvc
|
||||||
return actionDescriptor;
|
return actionDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ParameterDescriptor CreateParameterDescriptor(ParameterModel parameter)
|
private static ParameterDescriptor CreateParameterDescriptor(ParameterModel parameterModel)
|
||||||
{
|
{
|
||||||
var parameterDescriptor = new ParameterDescriptor()
|
var parameterDescriptor = new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
Name = parameter.ParameterName,
|
Name = parameterModel.ParameterName,
|
||||||
ParameterType = parameter.ParameterInfo.ParameterType,
|
ParameterType = parameterModel.ParameterInfo.ParameterType,
|
||||||
BindingInfo = parameter.BindingInfo
|
BindingInfo = parameterModel.BindingInfo
|
||||||
|
};
|
||||||
|
|
||||||
|
return parameterDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ParameterDescriptor CreateParameterDescriptor(PropertyModel propertyModel)
|
||||||
|
{
|
||||||
|
var parameterDescriptor = new ParameterDescriptor()
|
||||||
|
{
|
||||||
|
BindingInfo = propertyModel.BindingInfo,
|
||||||
|
Name = propertyModel.PropertyName,
|
||||||
|
ParameterType = propertyModel.PropertyInfo.PropertyType,
|
||||||
};
|
};
|
||||||
|
|
||||||
return parameterDescriptor;
|
return parameterDescriptor;
|
||||||
|
|
|
||||||
|
|
@ -80,11 +80,11 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
return actionResult;
|
return actionResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task<IDictionary<string, object>> GetActionArgumentsAsync(
|
protected override Task<IDictionary<string, object>> BindActionArgumentsAsync(
|
||||||
ActionContext context,
|
ActionContext context,
|
||||||
ActionBindingContext bindingContext)
|
ActionBindingContext bindingContext)
|
||||||
{
|
{
|
||||||
return _argumentBinder.GetActionArgumentsAsync(context, bindingContext);
|
return _argumentBinder.BindActionArgumentsAsync(context, bindingContext, Instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marking as internal for Unit Testing purposes.
|
// Marking as internal for Unit Testing purposes.
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ using Microsoft.AspNet.Mvc.Core;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
|
using Microsoft.Framework.Internal;
|
||||||
using Microsoft.Framework.OptionsModel;
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
|
@ -34,9 +35,10 @@ namespace Microsoft.AspNet.Mvc
|
||||||
_validator = validator;
|
_validator = validator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IDictionary<string, object>> GetActionArgumentsAsync(
|
public async Task<IDictionary<string, object>> BindActionArgumentsAsync(
|
||||||
ActionContext actionContext,
|
ActionContext actionContext,
|
||||||
ActionBindingContext actionBindingContext)
|
ActionBindingContext actionBindingContext,
|
||||||
|
object controller)
|
||||||
{
|
{
|
||||||
var actionDescriptor = actionContext.ActionDescriptor as ControllerActionDescriptor;
|
var actionDescriptor = actionContext.ActionDescriptor as ControllerActionDescriptor;
|
||||||
if (actionDescriptor == null)
|
if (actionDescriptor == null)
|
||||||
|
|
@ -47,31 +49,48 @@ namespace Microsoft.AspNet.Mvc
|
||||||
nameof(actionContext));
|
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);
|
var actionArguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||||
await PopulateArgumentsAsync(
|
await PopulateArgumentsAsync(
|
||||||
actionContext,
|
operationBindingContext,
|
||||||
actionBindingContext,
|
actionContext.ModelState,
|
||||||
actionArguments,
|
actionArguments,
|
||||||
actionDescriptor.Parameters);
|
actionDescriptor.Parameters);
|
||||||
return actionArguments;
|
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 => helper.Name == property.Key);
|
||||||
|
if (propertyHelper.Property == null || !propertyHelper.Property.CanWrite)
|
||||||
|
{
|
||||||
|
// nothing to do
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var setter = PropertyHelper.MakeFastPropertySetter(propertyHelper.Property);
|
||||||
|
setter(controller, property.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task PopulateArgumentsAsync(
|
private async Task PopulateArgumentsAsync(
|
||||||
ActionContext actionContext,
|
OperationBindingContext operationContext,
|
||||||
ActionBindingContext bindingContext,
|
ModelStateDictionary modelState,
|
||||||
IDictionary<string, object> arguments,
|
IDictionary<string, object> arguments,
|
||||||
IEnumerable<ParameterDescriptor> parameterMetadata)
|
IEnumerable<ParameterDescriptor> parameterMetadata)
|
||||||
{
|
{
|
||||||
var operationBindingContext = new OperationBindingContext
|
|
||||||
{
|
|
||||||
ModelBinder = bindingContext.ModelBinder,
|
|
||||||
ValidatorProvider = bindingContext.ValidatorProvider,
|
|
||||||
MetadataProvider = _modelMetadataProvider,
|
|
||||||
HttpContext = actionContext.HttpContext,
|
|
||||||
ValueProvider = bindingContext.ValueProvider,
|
|
||||||
};
|
|
||||||
|
|
||||||
var modelState = actionContext.ModelState;
|
|
||||||
modelState.MaxAllowedErrors = _options.MaxModelValidationErrors;
|
modelState.MaxAllowedErrors = _options.MaxModelValidationErrors;
|
||||||
foreach (var parameter in parameterMetadata)
|
foreach (var parameter in parameterMetadata)
|
||||||
{
|
{
|
||||||
|
|
@ -82,9 +101,9 @@ namespace Microsoft.AspNet.Mvc
|
||||||
metadata,
|
metadata,
|
||||||
parameter.BindingInfo,
|
parameter.BindingInfo,
|
||||||
modelState,
|
modelState,
|
||||||
operationBindingContext);
|
operationContext);
|
||||||
|
|
||||||
var modelBindingResult = await bindingContext.ModelBinder.BindModelAsync(modelBindingContext);
|
var modelBindingResult = await operationContext.ModelBinder.BindModelAsync(modelBindingContext);
|
||||||
if (modelBindingResult != null && modelBindingResult.IsModelSet)
|
if (modelBindingResult != null && modelBindingResult.IsModelSet)
|
||||||
{
|
{
|
||||||
var modelExplorer = new ModelExplorer(
|
var modelExplorer = new ModelExplorer(
|
||||||
|
|
@ -95,8 +114,9 @@ namespace Microsoft.AspNet.Mvc
|
||||||
arguments[parameter.Name] = modelBindingResult.Model;
|
arguments[parameter.Name] = modelBindingResult.Model;
|
||||||
var validationContext = new ModelValidationContext(
|
var validationContext = new ModelValidationContext(
|
||||||
modelBindingResult.Key,
|
modelBindingResult.Key,
|
||||||
bindingContext.ValidatorProvider,
|
modelBindingContext.BindingSource,
|
||||||
actionContext.ModelState,
|
operationContext.ValidatorProvider,
|
||||||
|
modelState,
|
||||||
modelExplorer);
|
modelExplorer);
|
||||||
_validator.Validate(validationContext);
|
_validator.Validate(validationContext);
|
||||||
}
|
}
|
||||||
|
|
@ -121,5 +141,19 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
return modelBindingContext;
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,13 +149,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
type,
|
type,
|
||||||
typeof(ActivateAttribute),
|
typeof(ActivateAttribute),
|
||||||
CreateActivateInfo);
|
CreateActivateInfo);
|
||||||
var activatorsForFromServiceProperties = PropertyActivator<ActionContext>.GetPropertiesToActivate(
|
return activatorsForActivateProperties;
|
||||||
type,
|
|
||||||
typeof(FromServicesAttribute),
|
|
||||||
CreateFromServicesInfo);
|
|
||||||
|
|
||||||
return Enumerable.Concat(activatorsForActivateProperties, activatorsForFromServiceProperties)
|
|
||||||
.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PropertyActivator<ActionContext> CreateActivateInfo(
|
private PropertyActivator<ActionContext> CreateActivateInfo(
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,24 @@ namespace Microsoft.AspNet.Mvc.Description
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context.ActionDescriptor.BoundProperties != null)
|
||||||
|
{
|
||||||
|
foreach (var actionParameter in context.ActionDescriptor.BoundProperties)
|
||||||
|
{
|
||||||
|
var visitor = new PseudoModelBindingVisitor(context, actionParameter);
|
||||||
|
var modelMetadata = context.MetadataProvider.GetMetadataForProperty(
|
||||||
|
containerType: context.ActionDescriptor.ControllerTypeInfo.AsType(),
|
||||||
|
propertyName: actionParameter.Name);
|
||||||
|
|
||||||
|
var bindingContext = ApiParameterDescriptionContext.GetContext(
|
||||||
|
modelMetadata,
|
||||||
|
actionParameter.BindingInfo,
|
||||||
|
propertyName: actionParameter.Name);
|
||||||
|
|
||||||
|
visitor.WalkParameter(bindingContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = context.Results.Count - 1; i >= 0; i--)
|
for (var i = context.Results.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
// Remove any 'hidden' parameters. These are things that can't come from user input,
|
// Remove any 'hidden' parameters. These are things that can't come from user input,
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
|
|
||||||
protected abstract Task<IActionResult> InvokeActionAsync(ActionExecutingContext actionExecutingContext);
|
protected abstract Task<IActionResult> InvokeActionAsync(ActionExecutingContext actionExecutingContext);
|
||||||
|
|
||||||
protected abstract Task<IDictionary<string, object>> GetActionArgumentsAsync(
|
protected abstract Task<IDictionary<string, object>> BindActionArgumentsAsync(
|
||||||
[NotNull] ActionContext context,
|
[NotNull] ActionContext context,
|
||||||
[NotNull] ActionBindingContext bindingContext);
|
[NotNull] ActionBindingContext bindingContext);
|
||||||
|
|
||||||
|
|
@ -434,7 +434,7 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
|
|
||||||
Instance = CreateInstance();
|
Instance = CreateInstance();
|
||||||
|
|
||||||
var arguments = await GetActionArgumentsAsync(ActionContext, ActionBindingContext);
|
var arguments = await BindActionArgumentsAsync(ActionContext, ActionBindingContext);
|
||||||
|
|
||||||
_actionExecutingContext = new ActionExecutingContext(
|
_actionExecutingContext = new ActionExecutingContext(
|
||||||
ActionContext,
|
ActionContext,
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,15 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a dictionary of representing the parameter-argument name-value pairs,
|
/// Returns a dictionary of representing the parameter-argument name-value pairs,
|
||||||
/// which can be used to invoke the action.
|
/// which can be used to invoke the action. Also binds properties explicitly marked properties on the
|
||||||
|
/// <paramref name="controller"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The action context assoicated with the current action.</param>
|
/// <param name="context">The action context assoicated with the current action.</param>
|
||||||
/// <param name="bindingContext">The <see cref="ActionBindingContext"/>.</param>
|
/// <param name="bindingContext">The <see cref="ActionBindingContext"/>.</param>
|
||||||
Task<IDictionary<string, object>> GetActionArgumentsAsync(
|
/// <param name="controller">The controller object which contains the action.</param>
|
||||||
|
Task<IDictionary<string, object>> BindActionArgumentsAsync(
|
||||||
[NotNull] ActionContext context,
|
[NotNull] ActionContext context,
|
||||||
[NotNull] ActionBindingContext bindingContext);
|
[NotNull] ActionBindingContext bindingContext,
|
||||||
|
[NotNull] object controller);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ namespace Microsoft.AspNet.Mvc.Logging
|
||||||
Name = inner.Name;
|
Name = inner.Name;
|
||||||
DisplayName = inner.DisplayName;
|
DisplayName = inner.DisplayName;
|
||||||
Parameters = inner.Parameters.Select(p => new ParameterDescriptorValues(p)).ToList();
|
Parameters = inner.Parameters.Select(p => new ParameterDescriptorValues(p)).ToList();
|
||||||
|
BoundProperties = inner.BoundProperties.Select(p => new ParameterDescriptorValues(p)).ToList();
|
||||||
FilterDescriptors = inner.FilterDescriptors.Select(f => new FilterDescriptorValues(f)).ToList();
|
FilterDescriptors = inner.FilterDescriptors.Select(f => new FilterDescriptorValues(f)).ToList();
|
||||||
RouteConstraints = inner.RouteConstraints.Select(r => new RouteDataActionConstraintValues(r)).ToList();
|
RouteConstraints = inner.RouteConstraints.Select(r => new RouteDataActionConstraintValues(r)).ToList();
|
||||||
AttributeRouteInfo = new AttributeRouteInfoValues(inner.AttributeRouteInfo);
|
AttributeRouteInfo = new AttributeRouteInfoValues(inner.AttributeRouteInfo);
|
||||||
|
|
@ -54,6 +55,12 @@ namespace Microsoft.AspNet.Mvc.Logging
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<ParameterDescriptorValues> Parameters { get; }
|
public IList<ParameterDescriptorValues> Parameters { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parameters of the action as <see cref="ParameterDescriptorValues"/>.
|
||||||
|
/// See <see cref="ActionDescriptor.BoundProperties"/>.
|
||||||
|
/// </summary>
|
||||||
|
public IList<ParameterDescriptorValues> BoundProperties { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The filters of the action as <see cref="FilterDescriptorValues"/>.
|
/// The filters of the action as <see cref="FilterDescriptorValues"/>.
|
||||||
/// See <see cref="ActionDescriptor.FilterDescriptors"/>.
|
/// See <see cref="ActionDescriptor.FilterDescriptors"/>.
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ namespace Microsoft.AspNet.Mvc.Logging
|
||||||
ApiExplorer = new ApiExplorerModelValues(inner.ApiExplorer);
|
ApiExplorer = new ApiExplorerModelValues(inner.ApiExplorer);
|
||||||
Actions = inner.Actions.Select(a => new ActionModelValues(a)).ToList();
|
Actions = inner.Actions.Select(a => new ActionModelValues(a)).ToList();
|
||||||
Attributes = inner.Attributes.Select(a => a.GetType()).ToList();
|
Attributes = inner.Attributes.Select(a => a.GetType()).ToList();
|
||||||
|
ControllerProperties = inner.ControllerProperties.Select(p => new PropertyModelValues(p)).ToList();
|
||||||
Filters = inner.Filters.Select(f => new FilterValues(f)).ToList();
|
Filters = inner.Filters.Select(f => new FilterValues(f)).ToList();
|
||||||
ActionConstraints = inner.ActionConstraints?.Select(a => new ActionConstraintValues(a))?.ToList();
|
ActionConstraints = inner.ActionConstraints?.Select(a => new ActionConstraintValues(a))?.ToList();
|
||||||
RouteConstraints = inner.RouteConstraints.Select(
|
RouteConstraints = inner.RouteConstraints.Select(
|
||||||
|
|
@ -43,6 +44,11 @@ namespace Microsoft.AspNet.Mvc.Logging
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Type ControllerType { get; }
|
public Type ControllerType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Type"/> of the controller. See <see cref="ControllerModel.ControllerType"/>.
|
||||||
|
/// </summary>
|
||||||
|
public IList<PropertyModelValues> ControllerProperties { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// See <see cref="ControllerModel.ApiExplorer"/>.
|
/// See <see cref="ControllerModel.ApiExplorer"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
// 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 Microsoft.AspNet.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Logging
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Logging representation of a <see cref="PropertyModelValues"/>. Logged as a substructure of
|
||||||
|
/// <see cref="ControllerModelValues"/>, this contains the name, type, and
|
||||||
|
/// binder metadata of the property.
|
||||||
|
/// </summary>
|
||||||
|
public class PropertyModelValues : ReflectionBasedLogValues
|
||||||
|
{
|
||||||
|
public PropertyModelValues([NotNull] PropertyModel inner)
|
||||||
|
{
|
||||||
|
PropertyName = inner.PropertyName;
|
||||||
|
PropertyType = inner.PropertyInfo.PropertyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the property. See <see cref="PropertyModel.PropertyName"/>.
|
||||||
|
/// </summary>
|
||||||
|
public string PropertyName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="Type"/> of the property.
|
||||||
|
/// </summary>
|
||||||
|
public Type PropertyType { get; }
|
||||||
|
|
||||||
|
public override string Format()
|
||||||
|
{
|
||||||
|
return LogFormatter.FormatLogValues(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -41,10 +41,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
public static BindingInfo GetBindingInfo(IEnumerable<object> attributes)
|
public static BindingInfo GetBindingInfo(IEnumerable<object> attributes)
|
||||||
{
|
{
|
||||||
var bindingInfo = new BindingInfo();
|
var bindingInfo = new BindingInfo();
|
||||||
|
var isBindingInfoPresent = false;
|
||||||
|
|
||||||
// BinderModelName
|
// BinderModelName
|
||||||
foreach (var binderModelNameAttribute in attributes.OfType<IModelNameProvider>())
|
foreach (var binderModelNameAttribute in attributes.OfType<IModelNameProvider>())
|
||||||
{
|
{
|
||||||
|
isBindingInfoPresent = true;
|
||||||
if (binderModelNameAttribute?.Name != null)
|
if (binderModelNameAttribute?.Name != null)
|
||||||
{
|
{
|
||||||
bindingInfo.BinderModelName = binderModelNameAttribute.Name;
|
bindingInfo.BinderModelName = binderModelNameAttribute.Name;
|
||||||
|
|
@ -55,6 +57,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
// BinderType
|
// BinderType
|
||||||
foreach (var binderTypeAttribute in attributes.OfType<IBinderTypeProviderMetadata>())
|
foreach (var binderTypeAttribute in attributes.OfType<IBinderTypeProviderMetadata>())
|
||||||
{
|
{
|
||||||
|
isBindingInfoPresent = true;
|
||||||
if (binderTypeAttribute.BinderType != null)
|
if (binderTypeAttribute.BinderType != null)
|
||||||
{
|
{
|
||||||
bindingInfo.BinderType = binderTypeAttribute.BinderType;
|
bindingInfo.BinderType = binderTypeAttribute.BinderType;
|
||||||
|
|
@ -65,6 +68,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
// BindingSource
|
// BindingSource
|
||||||
foreach (var bindingSourceAttribute in attributes.OfType<IBindingSourceMetadata>())
|
foreach (var bindingSourceAttribute in attributes.OfType<IBindingSourceMetadata>())
|
||||||
{
|
{
|
||||||
|
isBindingInfoPresent = true;
|
||||||
if (bindingSourceAttribute.BindingSource != null)
|
if (bindingSourceAttribute.BindingSource != null)
|
||||||
{
|
{
|
||||||
bindingInfo.BindingSource = bindingSourceAttribute.BindingSource;
|
bindingInfo.BindingSource = bindingSourceAttribute.BindingSource;
|
||||||
|
|
@ -76,11 +80,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var predicateProviders = attributes.OfType<IPropertyBindingPredicateProvider>().ToArray();
|
var predicateProviders = attributes.OfType<IPropertyBindingPredicateProvider>().ToArray();
|
||||||
if (predicateProviders.Length > 0)
|
if (predicateProviders.Length > 0)
|
||||||
{
|
{
|
||||||
|
isBindingInfoPresent = true;
|
||||||
bindingInfo.PropertyBindingPredicateProvider = new CompositePredicateProvider(
|
bindingInfo.PropertyBindingPredicateProvider = new CompositePredicateProvider(
|
||||||
predicateProviders);
|
predicateProviders);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bindingInfo;
|
return isBindingInfoPresent ? bindingInfo : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CompositePredicateProvider : IPropertyBindingPredicateProvider
|
private class CompositePredicateProvider : IPropertyBindingPredicateProvider
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ModelAttributes
|
public static class ModelAttributes
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the attributes for the given <paramref name="parameter"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameter">A <see cref="ParameterInfo"/> for which attributes need to be resolved.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>An <see cref="IEnumerable{object}"/> containing the attributes on the
|
||||||
|
/// <paramref name="parameter"/> before the attributes on the <paramref name="parameter"/> type.</returns>
|
||||||
|
public static IEnumerable<object> GetAttributesForParameter(ParameterInfo parameter)
|
||||||
|
{
|
||||||
|
// Return the parameter attributes first.
|
||||||
|
var parameterAttributes = parameter.GetCustomAttributes();
|
||||||
|
var typeAttributes = parameter.ParameterType.GetTypeInfo().GetCustomAttributes();
|
||||||
|
|
||||||
|
return parameterAttributes.Concat(typeAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the attributes for the given <paramref name="property"/>.
|
/// Gets the attributes for the given <paramref name="property"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -64,18 +64,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
/// <returns>A new instance of <see cref="ModelBindingContext"/>.</returns>
|
/// <returns>A new instance of <see cref="ModelBindingContext"/>.</returns>
|
||||||
public static ModelBindingContext GetModelBindingContext(
|
public static ModelBindingContext GetModelBindingContext(
|
||||||
[NotNull] ModelMetadata metadata,
|
[NotNull] ModelMetadata metadata,
|
||||||
[NotNull] BindingInfo bindingInfo,
|
BindingInfo bindingInfo,
|
||||||
string modelName)
|
string modelName)
|
||||||
{
|
{
|
||||||
var binderModelName = bindingInfo.BinderModelName ?? metadata.BinderModelName;
|
var binderModelName = bindingInfo?.BinderModelName ?? metadata.BinderModelName;
|
||||||
var propertyPredicateProvider =
|
var propertyPredicateProvider =
|
||||||
bindingInfo.PropertyBindingPredicateProvider ?? metadata.PropertyBindingPredicateProvider;
|
bindingInfo?.PropertyBindingPredicateProvider ?? metadata.PropertyBindingPredicateProvider;
|
||||||
return new ModelBindingContext()
|
return new ModelBindingContext()
|
||||||
{
|
{
|
||||||
ModelMetadata = metadata,
|
ModelMetadata = metadata,
|
||||||
BindingSource = bindingInfo.BindingSource ?? metadata.BindingSource,
|
BindingSource = bindingInfo?.BindingSource ?? metadata.BindingSource,
|
||||||
PropertyFilter = propertyPredicateProvider?.PropertyFilter,
|
PropertyFilter = propertyPredicateProvider?.PropertyFilter,
|
||||||
BinderType = bindingInfo.BinderType ?? metadata.BinderType,
|
BinderType = bindingInfo?.BinderType ?? metadata.BinderType,
|
||||||
BinderModelName = binderModelName,
|
BinderModelName = binderModelName,
|
||||||
ModelName = binderModelName ?? metadata.PropertyName ?? modelName,
|
ModelName = binderModelName ?? metadata.PropertyName ?? modelName,
|
||||||
FallbackToEmptyPrefix = binderModelName == null,
|
FallbackToEmptyPrefix = binderModelName == null,
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Validate([NotNull] ModelValidationContext modelValidationContext)
|
public void Validate([NotNull] ModelValidationContext modelValidationContext)
|
||||||
{
|
{
|
||||||
var modelExplorer = modelValidationContext.ModelExplorer;
|
|
||||||
var validationContext = new ValidationContext()
|
var validationContext = new ValidationContext()
|
||||||
{
|
{
|
||||||
ModelValidationContext = modelValidationContext,
|
ModelValidationContext = modelValidationContext,
|
||||||
|
|
@ -40,23 +39,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
|
||||||
ValidateNonVisitedNodeAndChildren(
|
ValidateNonVisitedNodeAndChildren(
|
||||||
modelValidationContext.RootPrefix,
|
modelValidationContext.RootPrefix,
|
||||||
modelExplorer,
|
|
||||||
validationContext,
|
validationContext,
|
||||||
validators: null);
|
validators: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ValidateNonVisitedNodeAndChildren(
|
private bool ValidateNonVisitedNodeAndChildren(
|
||||||
string modelKey,
|
string modelKey,
|
||||||
ModelExplorer modelExplorer,
|
|
||||||
ValidationContext validationContext,
|
ValidationContext validationContext,
|
||||||
IList<IModelValidator> validators)
|
IList<IModelValidator> validators)
|
||||||
{
|
{
|
||||||
|
var modelValidationContext = validationContext.ModelValidationContext;
|
||||||
|
var modelExplorer = modelValidationContext.ModelExplorer;
|
||||||
|
|
||||||
// Recursion guard to avoid stack overflows
|
// Recursion guard to avoid stack overflows
|
||||||
RuntimeHelpers.EnsureSufficientExecutionStack();
|
RuntimeHelpers.EnsureSufficientExecutionStack();
|
||||||
|
|
||||||
var modelState = validationContext.ModelValidationContext.ModelState;
|
var modelState = modelValidationContext.ModelState;
|
||||||
|
|
||||||
var bindingSource = modelExplorer.Metadata.BindingSource;
|
var bindingSource = modelValidationContext.BindingSource;
|
||||||
if (bindingSource != null && !bindingSource.IsFromRequest)
|
if (bindingSource != null && !bindingSource.IsFromRequest)
|
||||||
{
|
{
|
||||||
// Short circuit if the metadata represents something that was not bound using request data.
|
// Short circuit if the metadata represents something that was not bound using request data.
|
||||||
|
|
@ -64,7 +64,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
var validationState = modelState.GetFieldValidationState(modelKey);
|
var validationState = modelState.GetFieldValidationState(modelKey);
|
||||||
if (validationState == ModelValidationState.Unvalidated)
|
if (validationState == ModelValidationState.Unvalidated)
|
||||||
{
|
{
|
||||||
validationContext.ModelValidationContext.ModelState.MarkFieldSkipped(modelKey);
|
modelValidationContext.ModelState.MarkFieldSkipped(modelKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For validation purposes this model is valid.
|
// For validation purposes this model is valid.
|
||||||
|
|
@ -83,7 +83,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
// The validators are not null in the case of validating an array. Since the validators are
|
// The validators are not null in the case of validating an array. Since the validators are
|
||||||
// the same for all the elements of the array, we do not do GetValidators for each element,
|
// the same for all the elements of the array, we do not do GetValidators for each element,
|
||||||
// instead we just pass them over. See ValidateElements function.
|
// instead we just pass them over. See ValidateElements function.
|
||||||
var validatorProvider = validationContext.ModelValidationContext.ValidatorProvider;
|
var validatorProvider = modelValidationContext.ValidatorProvider;
|
||||||
var validatorProviderContext = new ModelValidatorProviderContext(modelExplorer.Metadata);
|
var validatorProviderContext = new ModelValidatorProviderContext(modelExplorer.Metadata);
|
||||||
validatorProvider.GetValidators(validatorProviderContext);
|
validatorProvider.GetValidators(validatorProviderContext);
|
||||||
|
|
||||||
|
|
@ -160,7 +160,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ValidateProperties(string currentModelKey, ModelExplorer modelExplorer, ValidationContext validationContext)
|
private bool ValidateProperties(
|
||||||
|
string currentModelKey,
|
||||||
|
ModelExplorer modelExplorer,
|
||||||
|
ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
var isValid = true;
|
var isValid = true;
|
||||||
|
|
||||||
|
|
@ -168,13 +171,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
var propertyExplorer = modelExplorer.GetExplorerForProperty(property.PropertyName);
|
var propertyExplorer = modelExplorer.GetExplorerForProperty(property.PropertyName);
|
||||||
var propertyMetadata = propertyExplorer.Metadata;
|
var propertyMetadata = propertyExplorer.Metadata;
|
||||||
|
var propertyValidationContext = new ValidationContext()
|
||||||
|
{
|
||||||
|
ModelValidationContext = ModelValidationContext.GetChildValidationContext(
|
||||||
|
validationContext.ModelValidationContext,
|
||||||
|
propertyExplorer),
|
||||||
|
Visited = validationContext.Visited
|
||||||
|
};
|
||||||
|
|
||||||
var propertyBindingName = propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName;
|
var propertyBindingName = propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName;
|
||||||
var childKey = ModelBindingHelper.CreatePropertyModelName(currentModelKey, propertyBindingName);
|
var childKey = ModelBindingHelper.CreatePropertyModelName(currentModelKey, propertyBindingName);
|
||||||
if (!ValidateNonVisitedNodeAndChildren(
|
if (!ValidateNonVisitedNodeAndChildren(
|
||||||
childKey,
|
childKey,
|
||||||
propertyExplorer,
|
propertyValidationContext,
|
||||||
validationContext,
|
|
||||||
validators: null))
|
validators: null))
|
||||||
{
|
{
|
||||||
isValid = false;
|
isValid = false;
|
||||||
|
|
@ -209,7 +218,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
var elementExplorer = new ModelExplorer(_modelMetadataProvider, elementMetadata, element);
|
var elementExplorer = new ModelExplorer(_modelMetadataProvider, elementMetadata, element);
|
||||||
var elementKey = ModelBindingHelper.CreateIndexModelName(currentKey, index);
|
var elementKey = ModelBindingHelper.CreateIndexModelName(currentKey, index);
|
||||||
if (!ValidateNonVisitedNodeAndChildren(elementKey, elementExplorer, validationContext, validators))
|
var elementValidationContext = new ValidationContext()
|
||||||
|
{
|
||||||
|
ModelValidationContext = ModelValidationContext.GetChildValidationContext(
|
||||||
|
validationContext.ModelValidationContext,
|
||||||
|
elementExplorer),
|
||||||
|
Visited = validationContext.Visited
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!ValidateNonVisitedNodeAndChildren(elementKey, elementValidationContext, validators))
|
||||||
{
|
{
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
|
@ -245,9 +262,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
// In a large array (tens of thousands or more) scenario it's very significant.
|
// In a large array (tens of thousands or more) scenario it's very significant.
|
||||||
if (validators == null || validators.Count > 0)
|
if (validators == null || validators.Count > 0)
|
||||||
{
|
{
|
||||||
var modelValidationContext =
|
var modelValidationContext = ModelValidationContext.GetChildValidationContext(
|
||||||
new ModelValidationContext(validationContext.ModelValidationContext, modelExplorer);
|
validationContext.ModelValidationContext,
|
||||||
|
modelExplorer);
|
||||||
|
|
||||||
var modelValidationState = modelState.GetValidationState(modelKey);
|
var modelValidationState = modelState.GetValidationState(modelKey);
|
||||||
|
|
||||||
// If either the model or its properties are unvalidated, validate them now.
|
// If either the model or its properties are unvalidated, validate them now.
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
[NotNull] ModelBindingContext bindingContext,
|
[NotNull] ModelBindingContext bindingContext,
|
||||||
[NotNull] ModelExplorer modelExplorer)
|
[NotNull] ModelExplorer modelExplorer)
|
||||||
: this(bindingContext.ModelName,
|
: this(bindingContext.ModelName,
|
||||||
|
bindingContext.BindingSource,
|
||||||
bindingContext.OperationBindingContext.ValidatorProvider,
|
bindingContext.OperationBindingContext.ValidatorProvider,
|
||||||
bindingContext.ModelState,
|
bindingContext.ModelState,
|
||||||
modelExplorer)
|
modelExplorer)
|
||||||
|
|
@ -19,6 +20,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
|
||||||
public ModelValidationContext(
|
public ModelValidationContext(
|
||||||
string rootPrefix,
|
string rootPrefix,
|
||||||
|
BindingSource bindingSource,
|
||||||
[NotNull] IModelValidatorProvider validatorProvider,
|
[NotNull] IModelValidatorProvider validatorProvider,
|
||||||
[NotNull] ModelStateDictionary modelState,
|
[NotNull] ModelStateDictionary modelState,
|
||||||
[NotNull] ModelExplorer modelExplorer)
|
[NotNull] ModelExplorer modelExplorer)
|
||||||
|
|
@ -27,25 +29,37 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
RootPrefix = rootPrefix;
|
RootPrefix = rootPrefix;
|
||||||
ValidatorProvider = validatorProvider;
|
ValidatorProvider = validatorProvider;
|
||||||
ModelExplorer = modelExplorer;
|
ModelExplorer = modelExplorer;
|
||||||
|
BindingSource = bindingSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModelValidationContext(
|
/// <summary>
|
||||||
|
/// Constructs a new instance of the <see cref="ModelValidationContext"/> class using the
|
||||||
|
/// <paramref name="parentContext" /> and <paramref name="modelExplorer"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parentContext">Existing <see cref="ModelValidationContext"/>.</param>
|
||||||
|
/// <param name="modelExplorer"><see cref="ModelExplorer"/> associated with the new
|
||||||
|
/// <see cref="ModelValidationContext"/>.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static ModelValidationContext GetChildValidationContext(
|
||||||
[NotNull] ModelValidationContext parentContext,
|
[NotNull] ModelValidationContext parentContext,
|
||||||
[NotNull] ModelExplorer modelExplorer)
|
[NotNull] ModelExplorer modelExplorer)
|
||||||
{
|
{
|
||||||
ModelExplorer = modelExplorer;
|
return new ModelValidationContext(
|
||||||
ModelState = parentContext.ModelState;
|
parentContext.RootPrefix,
|
||||||
RootPrefix = parentContext.RootPrefix;
|
modelExplorer.Metadata.BindingSource,
|
||||||
ValidatorProvider = parentContext.ValidatorProvider;
|
parentContext.ValidatorProvider,
|
||||||
|
parentContext.ModelState,
|
||||||
|
modelExplorer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ModelExplorer ModelExplorer { get; }
|
public ModelExplorer ModelExplorer { get; }
|
||||||
|
|
||||||
public ModelStateDictionary ModelState { get; }
|
public ModelStateDictionary ModelState { get; }
|
||||||
|
|
||||||
public string RootPrefix { get; set; }
|
public string RootPrefix { get; set; }
|
||||||
|
|
||||||
|
public BindingSource BindingSource { get; set; }
|
||||||
|
|
||||||
public IModelValidatorProvider ValidatorProvider { get; }
|
public IModelValidatorProvider ValidatorProvider { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -424,9 +424,10 @@ namespace System.Web.Http
|
||||||
|
|
||||||
var modelValidationContext = new ModelValidationContext(
|
var modelValidationContext = new ModelValidationContext(
|
||||||
keyPrefix,
|
keyPrefix,
|
||||||
BindingContext.ValidatorProvider,
|
bindingSource: null,
|
||||||
ModelState,
|
validatorProvider: BindingContext.ValidatorProvider,
|
||||||
modelExplorer);
|
modelState: ModelState,
|
||||||
|
modelExplorer: modelExplorer);
|
||||||
|
|
||||||
ObjectValidator.Validate(modelValidationContext);
|
ObjectValidator.Validate(modelValidationContext);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
// Some IBindingSourceMetadata attributes like ModelBinder attribute return null
|
// Some IBindingSourceMetadata attributes like ModelBinder attribute return null
|
||||||
// as their binding source. Special case to ensure we do not ignore them.
|
// as their binding source. Special case to ensure we do not ignore them.
|
||||||
if (parameter.BindingInfo.BindingSource != null ||
|
if (parameter.BindingInfo?.BindingSource != null ||
|
||||||
parameter.Attributes.OfType<IBindingSourceMetadata>().Any())
|
parameter.Attributes.OfType<IBindingSourceMetadata>().Any())
|
||||||
{
|
{
|
||||||
// This has a binding behavior configured, just leave it alone.
|
// This has a binding behavior configured, just leave it alone.
|
||||||
|
|
@ -30,12 +30,13 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
else if (ValueProviderResult.CanConvertFromString(parameter.ParameterInfo.ParameterType))
|
else if (ValueProviderResult.CanConvertFromString(parameter.ParameterInfo.ParameterType))
|
||||||
{
|
{
|
||||||
// Simple types are by-default from the URI.
|
// Simple types are by-default from the URI.
|
||||||
|
parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo();
|
||||||
parameter.BindingInfo.BindingSource = uriBindingSource;
|
parameter.BindingInfo.BindingSource = uriBindingSource;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Complex types are by-default from the body.
|
// Complex types are by-default from the body.
|
||||||
|
parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo();
|
||||||
parameter.BindingInfo.BindingSource = BindingSource.Body;
|
parameter.BindingInfo.BindingSource = BindingSource.Body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
foreach (var parameter in candidate.Action.Parameters)
|
foreach (var parameter in candidate.Action.Parameters)
|
||||||
{
|
{
|
||||||
// We only consider parameters that are marked as bound from the URL.
|
// We only consider parameters that are marked as bound from the URL.
|
||||||
var source = parameter.BindingInfo.BindingSource;
|
var source = parameter.BindingInfo?.BindingSource;
|
||||||
if (source == null)
|
if (source == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -115,7 +115,7 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var prefix = parameter.BindingInfo.BinderModelName ?? parameter.Name;
|
var prefix = parameter.BindingInfo?.BinderModelName ?? parameter.Name;
|
||||||
|
|
||||||
parameters.Add(new OverloadedParameter()
|
parameters.Add(new OverloadedParameter()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,8 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
controller.Filters.Add(new AuthorizeFilter(new AuthorizationPolicyBuilder().Build()));
|
controller.Filters.Add(new AuthorizeFilter(new AuthorizationPolicyBuilder().Build()));
|
||||||
controller.RouteConstraints.Add(new AreaAttribute("Admin"));
|
controller.RouteConstraints.Add(new AreaAttribute("Admin"));
|
||||||
controller.Properties.Add(new KeyValuePair<object, object>("test key", "test value"));
|
controller.Properties.Add(new KeyValuePair<object, object>("test key", "test value"));
|
||||||
|
controller.ControllerProperties.Add(
|
||||||
|
new PropertyModel(typeof(TestController).GetProperty("TestProperty"), new List<object>()));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var controller2 = new ControllerModel(controller);
|
var controller2 = new ControllerModel(controller);
|
||||||
|
|
@ -67,7 +69,8 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
{
|
{
|
||||||
if (property.Name.Equals("Actions") ||
|
if (property.Name.Equals("Actions") ||
|
||||||
property.Name.Equals("AttributeRoutes") ||
|
property.Name.Equals("AttributeRoutes") ||
|
||||||
property.Name.Equals("ApiExplorer"))
|
property.Name.Equals("ApiExplorer") ||
|
||||||
|
property.Name.Equals("ControllerProperties"))
|
||||||
{
|
{
|
||||||
// This test excludes other ApplicationModel objects on purpose because we deep copy them.
|
// This test excludes other ApplicationModel objects on purpose because we deep copy them.
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -110,6 +113,8 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
|
|
||||||
private class TestController
|
private class TestController
|
||||||
{
|
{
|
||||||
|
public string TestProperty { get; set; }
|
||||||
|
|
||||||
public void Edit()
|
public void Edit()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Authorization;
|
using Microsoft.AspNet.Authorization;
|
||||||
using Microsoft.AspNet.Cors.Core;
|
using Microsoft.AspNet.Cors.Core;
|
||||||
using Microsoft.AspNet.Mvc.Filters;
|
using Microsoft.AspNet.Mvc.Filters;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
using Microsoft.Framework.OptionsModel;
|
using Microsoft.Framework.OptionsModel;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -71,6 +72,27 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
Assert.Single(model.Filters, f => f is CorsAuthorizationFilterFactory);
|
Assert.Single(model.Filters, f => f is CorsAuthorizationFilterFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BuildControllerModel_AddsControllerProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
|
||||||
|
NullLoggerFactory.Instance,
|
||||||
|
null);
|
||||||
|
var typeInfo = typeof(ModelBinderController).GetTypeInfo();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var model = builder.BuildControllerModel(typeInfo);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(2, model.ControllerProperties.Count);
|
||||||
|
Assert.Equal("Bound", model.ControllerProperties[0].PropertyName);
|
||||||
|
Assert.Equal(BindingSource.Query, model.ControllerProperties[0].BindingInfo.BindingSource);
|
||||||
|
Assert.NotNull(model.ControllerProperties[0].Controller);
|
||||||
|
var attribute = Assert.Single(model.ControllerProperties[0].Attributes);
|
||||||
|
Assert.IsType<FromQueryAttribute>(attribute);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void BuildControllerModel_DisableCorsAttributeAddsDisableCorsAuthorizationFilter()
|
public void BuildControllerModel_DisableCorsAttributeAddsDisableCorsAuthorizationFilter()
|
||||||
{
|
{
|
||||||
|
|
@ -168,6 +190,14 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ModelBinderController
|
||||||
|
{
|
||||||
|
[FromQuery]
|
||||||
|
public string Bound { get; set; }
|
||||||
|
|
||||||
|
public string Unbound { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class SomeFiltersController : IAsyncActionFilter, IResultFilter
|
public class SomeFiltersController : IAsyncActionFilter, IResultFilter
|
||||||
{
|
{
|
||||||
public Task OnActionExecutionAsync(
|
public Task OnActionExecutionAsync(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
// 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.Reflection;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
|
{
|
||||||
|
public class PropertyModelTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CopyConstructor_CopiesAllProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var propertyModel = new PropertyModel(typeof(TestController).GetProperty("Property"),
|
||||||
|
new List<object>() { new FromBodyAttribute() });
|
||||||
|
|
||||||
|
propertyModel.Controller = new ControllerModel(typeof(TestController).GetTypeInfo(), new List<object>());
|
||||||
|
propertyModel.BindingInfo = BindingInfo.GetBindingInfo(propertyModel.Attributes);
|
||||||
|
propertyModel.PropertyName = "Property";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var propertyModel2 = new PropertyModel(propertyModel);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
foreach (var property in typeof(PropertyModel).GetProperties())
|
||||||
|
{
|
||||||
|
var value1 = property.GetValue(propertyModel);
|
||||||
|
var value2 = property.GetValue(propertyModel2);
|
||||||
|
|
||||||
|
if (typeof(IEnumerable<object>).IsAssignableFrom(property.PropertyType))
|
||||||
|
{
|
||||||
|
Assert.Equal<object>((IEnumerable<object>)value1, (IEnumerable<object>)value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotEmpty((IEnumerable<object>)value1);
|
||||||
|
}
|
||||||
|
else if (property.PropertyType.IsValueType ||
|
||||||
|
Nullable.GetUnderlyingType(property.PropertyType) != null)
|
||||||
|
{
|
||||||
|
Assert.Equal(value1, value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotEqual(value1, Activator.CreateInstance(property.PropertyType));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.Same(value1, value2);
|
||||||
|
|
||||||
|
// Ensure non-default value
|
||||||
|
Assert.NotNull(value1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestController
|
||||||
|
{
|
||||||
|
public string Property { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,12 +5,50 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNet.Mvc.ApplicationModels;
|
using Microsoft.AspNet.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
public class ControllerActionDescriptorBuilderTest
|
public class ControllerActionDescriptorBuilderTest
|
||||||
{
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Build_WithControllerPropertiesSet_AddsPropertiesWithBinderMetadataSet()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var applicationModel = new ApplicationModel();
|
||||||
|
var controller = new ControllerModel(typeof(TestController).GetTypeInfo(),
|
||||||
|
new List<object>() { });
|
||||||
|
controller.ControllerProperties.Add(
|
||||||
|
new PropertyModel(
|
||||||
|
controller.ControllerType.GetProperty("BoundProperty"),
|
||||||
|
new List<object>() { })
|
||||||
|
{
|
||||||
|
BindingInfo = BindingInfo.GetBindingInfo(new object[] { new FromQueryAttribute() }),
|
||||||
|
PropertyName = "BoundProperty"
|
||||||
|
});
|
||||||
|
|
||||||
|
controller.ControllerProperties.Add(
|
||||||
|
new PropertyModel(controller.ControllerType.GetProperty("UnboundProperty"), new List<object>() { }));
|
||||||
|
|
||||||
|
controller.Application = applicationModel;
|
||||||
|
applicationModel.Controllers.Add(controller);
|
||||||
|
|
||||||
|
var methodInfo = typeof(TestController).GetMethod("SomeAction");
|
||||||
|
var actionModel = new ActionModel(methodInfo, new List<object>() { });
|
||||||
|
actionModel.Controller = controller;
|
||||||
|
controller.Actions.Add(actionModel);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptors = ControllerActionDescriptorBuilder.Build(applicationModel);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var property = Assert.Single(descriptors.Single().BoundProperties);
|
||||||
|
Assert.Equal("BoundProperty", property.Name);
|
||||||
|
Assert.Equal(typeof(string), property.ParameterType);
|
||||||
|
Assert.Equal(BindingSource.Query, property.BindingInfo.BindingSource);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Build_WithPropertiesSet_FromApplicationModel()
|
public void Build_WithPropertiesSet_FromApplicationModel()
|
||||||
{
|
{
|
||||||
|
|
@ -88,6 +126,11 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
private class TestController
|
private class TestController
|
||||||
{
|
{
|
||||||
|
[FromQuery]
|
||||||
|
public string BoundProperty { get; set; }
|
||||||
|
|
||||||
|
public string UnboundProperty { get; set; }
|
||||||
|
|
||||||
public void SomeAction() { }
|
public void SomeAction() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
var id = Assert.Single(main.Parameters);
|
var id = Assert.Single(main.Parameters);
|
||||||
|
|
||||||
Assert.Equal("id", id.Name);
|
Assert.Equal("id", id.Name);
|
||||||
Assert.Null(id.BindingInfo.BindingSource);
|
Assert.Null(id.BindingInfo?.BindingSource);
|
||||||
Assert.Equal(typeof(int), id.ParameterType);
|
Assert.Equal(typeof(int), id.ParameterType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,7 +130,7 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
var id = Assert.Single(main.Parameters, p => p.Name == "id");
|
var id = Assert.Single(main.Parameters, p => p.Name == "id");
|
||||||
|
|
||||||
Assert.Equal("id", id.Name);
|
Assert.Equal("id", id.Name);
|
||||||
Assert.Null(id.BindingInfo.BindingSource);
|
Assert.Null(id.BindingInfo?.BindingSource);
|
||||||
Assert.Equal(typeof(int), id.ParameterType);
|
Assert.Equal(typeof(int), id.ParameterType);
|
||||||
|
|
||||||
var entity = Assert.Single(main.Parameters, p => p.Name == "entity");
|
var entity = Assert.Single(main.Parameters, p => p.Name == "entity");
|
||||||
|
|
@ -155,19 +155,19 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
var id = Assert.Single(main.Parameters, p => p.Name == "id");
|
var id = Assert.Single(main.Parameters, p => p.Name == "id");
|
||||||
|
|
||||||
Assert.Equal("id", id.Name);
|
Assert.Equal("id", id.Name);
|
||||||
Assert.Null(id.BindingInfo.BindingSource);
|
Assert.Null(id.BindingInfo?.BindingSource);
|
||||||
Assert.Equal(typeof(int), id.ParameterType);
|
Assert.Equal(typeof(int), id.ParameterType);
|
||||||
|
|
||||||
var upperCaseId = Assert.Single(main.Parameters, p => p.Name == "ID");
|
var upperCaseId = Assert.Single(main.Parameters, p => p.Name == "ID");
|
||||||
|
|
||||||
Assert.Equal("ID", upperCaseId.Name);
|
Assert.Equal("ID", upperCaseId.Name);
|
||||||
Assert.Null(upperCaseId.BindingInfo.BindingSource);
|
Assert.Null(upperCaseId.BindingInfo?.BindingSource);
|
||||||
Assert.Equal(typeof(int), upperCaseId.ParameterType);
|
Assert.Equal(typeof(int), upperCaseId.ParameterType);
|
||||||
|
|
||||||
var pascalCaseId = Assert.Single(main.Parameters, p => p.Name == "Id");
|
var pascalCaseId = Assert.Single(main.Parameters, p => p.Name == "Id");
|
||||||
|
|
||||||
Assert.Equal("Id", pascalCaseId.Name);
|
Assert.Equal("Id", pascalCaseId.Name);
|
||||||
Assert.Null(id.BindingInfo.BindingSource);
|
Assert.Null(id.BindingInfo?.BindingSource);
|
||||||
Assert.Equal(typeof(int), pascalCaseId.ParameterType);
|
Assert.Equal(typeof(int), pascalCaseId.ParameterType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -209,7 +209,7 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
var entity = Assert.Single(notFromBody.Parameters);
|
var entity = Assert.Single(notFromBody.Parameters);
|
||||||
|
|
||||||
Assert.Equal("entity", entity.Name);
|
Assert.Equal("entity", entity.Name);
|
||||||
Assert.Null(entity.BindingInfo.BindingSource);
|
Assert.Null(entity.BindingInfo?.BindingSource);
|
||||||
Assert.Equal(typeof(TestActionParameter), entity.ParameterType);
|
Assert.Equal(typeof(TestActionParameter), entity.ParameterType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2063,6 +2063,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
// Arrange
|
// Arrange
|
||||||
var actionDescriptor = new ControllerActionDescriptor
|
var actionDescriptor = new ControllerActionDescriptor
|
||||||
{
|
{
|
||||||
|
ControllerTypeInfo = typeof(TestController).GetTypeInfo(),
|
||||||
|
BoundProperties = new List<ParameterDescriptor>(),
|
||||||
MethodInfo = typeof(TestController).GetTypeInfo()
|
MethodInfo = typeof(TestController).GetTypeInfo()
|
||||||
.DeclaredMethods
|
.DeclaredMethods
|
||||||
.First(m => m.Name.Equals("ActionMethodWithDefaultValues", StringComparison.Ordinal)),
|
.First(m => m.Name.Equals("ActionMethodWithDefaultValues", StringComparison.Ordinal)),
|
||||||
|
|
|
||||||
|
|
@ -143,58 +143,6 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
Assert.Same(bindingContext, controller.BindingContext);
|
Assert.Same(bindingContext, controller.BindingContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void CreateController_PopulatesServicesFromServiceContainer()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var actionDescriptor = new ControllerActionDescriptor
|
|
||||||
{
|
|
||||||
ControllerTypeInfo = typeof(ControllerWithActivateAndFromServices).GetTypeInfo()
|
|
||||||
};
|
|
||||||
var services = GetServices();
|
|
||||||
var urlHelper = services.GetRequiredService<IUrlHelper>();
|
|
||||||
|
|
||||||
var httpContext = new DefaultHttpContext
|
|
||||||
{
|
|
||||||
RequestServices = services
|
|
||||||
};
|
|
||||||
var context = new ActionContext(httpContext, new RouteData(), actionDescriptor);
|
|
||||||
var factory = new DefaultControllerFactory(new DefaultControllerActivator(new DefaultTypeActivatorCache()));
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = factory.CreateController(context);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var controller = Assert.IsType<ControllerWithActivateAndFromServices>(result);
|
|
||||||
Assert.Same(urlHelper, controller.Helper);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void CreateController_PopulatesUserServicesFromServiceContainer()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var actionDescriptor = new ControllerActionDescriptor
|
|
||||||
{
|
|
||||||
ControllerTypeInfo = typeof(ControllerWithActivateAndFromServices).GetTypeInfo()
|
|
||||||
};
|
|
||||||
var services = GetServices();
|
|
||||||
var testService = services.GetService<TestService>();
|
|
||||||
|
|
||||||
var httpContext = new DefaultHttpContext
|
|
||||||
{
|
|
||||||
RequestServices = services
|
|
||||||
};
|
|
||||||
var context = new ActionContext(httpContext, new RouteData(), actionDescriptor);
|
|
||||||
var factory = new DefaultControllerFactory(new DefaultControllerActivator(new DefaultTypeActivatorCache()));
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = factory.CreateController(context);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var controller = Assert.IsType<ControllerWithActivateAndFromServices>(result);
|
|
||||||
Assert.Same(testService, controller.TestService);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CreateController_IgnoresPropertiesThatAreNotDecoratedWithActivateAttribute()
|
public void CreateController_IgnoresPropertiesThatAreNotDecoratedWithActivateAttribute()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -901,6 +901,41 @@ namespace Microsoft.AspNet.Mvc.Description
|
||||||
Assert.Equal(typeof(int), id.Type);
|
Assert.Equal(typeof(int), id.Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetApiDescription_WithControllerProperties_Merges_ParameterDescription()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var action = CreateActionDescriptor("FromQueryName", typeof(TestController));
|
||||||
|
var parameterDescriptor = action.Parameters.Single();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptions = GetApiDescriptions(action);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var description = Assert.Single(descriptions);
|
||||||
|
Assert.Equal(5, description.ParameterDescriptions.Count);
|
||||||
|
|
||||||
|
var name = Assert.Single(description.ParameterDescriptions, p => p.Name == "name");
|
||||||
|
Assert.Same(BindingSource.Query, name.Source);
|
||||||
|
Assert.Equal(typeof(string), name.Type);
|
||||||
|
|
||||||
|
var id = Assert.Single(description.ParameterDescriptions, p => p.Name == "Id");
|
||||||
|
Assert.Same(BindingSource.Path, id.Source);
|
||||||
|
Assert.Equal(typeof(int), id.Type);
|
||||||
|
|
||||||
|
var product = Assert.Single(description.ParameterDescriptions, p => p.Name == "Product");
|
||||||
|
Assert.Same(BindingSource.Body, product.Source);
|
||||||
|
Assert.Equal(typeof(Product), product.Type);
|
||||||
|
|
||||||
|
var userId = Assert.Single(description.ParameterDescriptions, p => p.Name == "UserId");
|
||||||
|
Assert.Same(BindingSource.Header, userId.Source);
|
||||||
|
Assert.Equal(typeof(string), userId.Type);
|
||||||
|
|
||||||
|
var comments = Assert.Single(description.ParameterDescriptions, p => p.Name == "Comments");
|
||||||
|
Assert.Same(BindingSource.ModelBinding, comments.Source);
|
||||||
|
Assert.Equal(typeof(string), comments.Type);
|
||||||
|
}
|
||||||
|
|
||||||
private IReadOnlyList<ApiDescription> GetApiDescriptions(ActionDescriptor action)
|
private IReadOnlyList<ApiDescription> GetApiDescriptions(ActionDescriptor action)
|
||||||
{
|
{
|
||||||
return GetApiDescriptions(action, CreateFormatters());
|
return GetApiDescriptions(action, CreateFormatters());
|
||||||
|
|
@ -950,17 +985,42 @@ namespace Microsoft.AspNet.Mvc.Description
|
||||||
return formatters;
|
return formatters;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ControllerActionDescriptor CreateActionDescriptor(string methodName = null)
|
private ControllerActionDescriptor CreateActionDescriptor(string methodName = null, Type controllerType = null)
|
||||||
{
|
{
|
||||||
var action = new ControllerActionDescriptor();
|
var action = new ControllerActionDescriptor();
|
||||||
action.SetProperty(new ApiDescriptionActionData());
|
action.SetProperty(new ApiDescriptionActionData());
|
||||||
|
|
||||||
action.MethodInfo = GetType().GetMethod(
|
if (controllerType != null)
|
||||||
methodName ?? "ReturnsObject",
|
{
|
||||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
action.MethodInfo = controllerType.GetMethod(
|
||||||
|
methodName ?? "ReturnsObject",
|
||||||
|
BindingFlags.Instance | BindingFlags.Public);
|
||||||
|
|
||||||
|
action.ControllerTypeInfo = controllerType.GetTypeInfo();
|
||||||
|
action.BoundProperties = new List<ParameterDescriptor>();
|
||||||
|
|
||||||
|
foreach (var property in action.ControllerTypeInfo.GetProperties())
|
||||||
|
{
|
||||||
|
var bindingInfo = BindingInfo.GetBindingInfo(property.GetCustomAttributes().OfType<object>());
|
||||||
|
if (bindingInfo != null)
|
||||||
|
{
|
||||||
|
action.BoundProperties.Add(new ParameterDescriptor()
|
||||||
|
{
|
||||||
|
BindingInfo = bindingInfo,
|
||||||
|
Name = property.Name,
|
||||||
|
ParameterType = property.PropertyType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
action.MethodInfo = GetType().GetMethod(
|
||||||
|
methodName ?? "ReturnsObject",
|
||||||
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
}
|
||||||
|
|
||||||
action.Parameters = new List<ParameterDescriptor>();
|
action.Parameters = new List<ParameterDescriptor>();
|
||||||
|
|
||||||
foreach (var parameter in action.MethodInfo.GetParameters())
|
foreach (var parameter in action.MethodInfo.GetParameters())
|
||||||
{
|
{
|
||||||
action.Parameters.Add(new ParameterDescriptor()
|
action.Parameters.Add(new ParameterDescriptor()
|
||||||
|
|
@ -1118,6 +1178,27 @@ namespace Microsoft.AspNet.Mvc.Description
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestController
|
||||||
|
{
|
||||||
|
[FromRoute]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[FromBody]
|
||||||
|
public Product Product { get; set; }
|
||||||
|
|
||||||
|
[FromHeader]
|
||||||
|
public string UserId { get; set; }
|
||||||
|
|
||||||
|
[ModelBinder]
|
||||||
|
public string Comments { get; set; }
|
||||||
|
|
||||||
|
public string NotBound { get; set; }
|
||||||
|
|
||||||
|
public void FromQueryName([FromQuery] string name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class Product
|
private class Product
|
||||||
{
|
{
|
||||||
public int ProductId { get; set; }
|
public int ProductId { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http.Core;
|
using Microsoft.AspNet.Http.Core;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
@ -54,48 +55,33 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
public async Task GetActionArgumentsAsync_DoesNotAddActionArguments_IfBinderReturnsFalse()
|
public async Task GetActionArgumentsAsync_DoesNotAddActionArguments_IfBinderReturnsFalse()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Func<object, int> method = foo => 1;
|
var actionDescriptor = GetActionDescriptor();
|
||||||
var actionDescriptor = new ControllerActionDescriptor
|
actionDescriptor.Parameters.Add(
|
||||||
{
|
new ParameterDescriptor
|
||||||
MethodInfo = method.Method,
|
|
||||||
Parameters = new List<ParameterDescriptor>
|
|
||||||
{
|
{
|
||||||
new ParameterDescriptor
|
Name = "foo",
|
||||||
{
|
ParameterType = typeof(object),
|
||||||
Name = "foo",
|
|
||||||
ParameterType = typeof(object),
|
|
||||||
BindingInfo = new BindingInfo(),
|
BindingInfo = new BindingInfo(),
|
||||||
}
|
});
|
||||||
}
|
|
||||||
};
|
var actionContext = GetActionContext(actionDescriptor);
|
||||||
|
|
||||||
var binder = new Mock<IModelBinder>();
|
var binder = new Mock<IModelBinder>();
|
||||||
binder
|
binder
|
||||||
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
.Returns(Task.FromResult<ModelBindingResult>(result: null));
|
.Returns(Task.FromResult<ModelBindingResult>(result: null));
|
||||||
|
|
||||||
var actionContext = new ActionContext(
|
|
||||||
new DefaultHttpContext(),
|
|
||||||
new RouteData(),
|
|
||||||
actionDescriptor);
|
|
||||||
|
|
||||||
var actionBindingContext = new ActionBindingContext()
|
var actionBindingContext = new ActionBindingContext()
|
||||||
{
|
{
|
||||||
ModelBinder = binder.Object,
|
ModelBinder = binder.Object,
|
||||||
};
|
};
|
||||||
|
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var inputFormattersProvider = new Mock<IInputFormattersProvider>();
|
|
||||||
inputFormattersProvider
|
var argumentBinder = GetArgumentBinder();
|
||||||
.SetupGet(o => o.InputFormatters)
|
|
||||||
.Returns(new List<IInputFormatter>());
|
|
||||||
var invoker = new DefaultControllerActionArgumentBinder(
|
|
||||||
modelMetadataProvider,
|
|
||||||
new DefaultObjectValidator(Mock.Of<IValidationExcludeFiltersProvider>(), modelMetadataProvider),
|
|
||||||
new MockMvcOptionsAccessor());
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await invoker.GetActionArgumentsAsync(actionContext, actionBindingContext);
|
var result = await argumentBinder
|
||||||
|
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(result);
|
Assert.Empty(result);
|
||||||
|
|
@ -105,20 +91,14 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
public async Task GetActionArgumentsAsync_DoesNotAddActionArguments_IfBinderDoesNotSetModel()
|
public async Task GetActionArgumentsAsync_DoesNotAddActionArguments_IfBinderDoesNotSetModel()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Func<object, int> method = foo => 1;
|
var actionDescriptor = GetActionDescriptor();
|
||||||
var actionDescriptor = new ControllerActionDescriptor
|
actionDescriptor.Parameters.Add(
|
||||||
{
|
new ParameterDescriptor
|
||||||
MethodInfo = method.Method,
|
|
||||||
Parameters = new List<ParameterDescriptor>
|
|
||||||
{
|
{
|
||||||
new ParameterDescriptor
|
Name = "foo",
|
||||||
{
|
ParameterType = typeof(object),
|
||||||
Name = "foo",
|
|
||||||
ParameterType = typeof(object),
|
|
||||||
BindingInfo = new BindingInfo(),
|
BindingInfo = new BindingInfo(),
|
||||||
}
|
});
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var binder = new Mock<IModelBinder>();
|
var binder = new Mock<IModelBinder>();
|
||||||
binder
|
binder
|
||||||
|
|
@ -135,19 +115,12 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
ModelBinder = binder.Object,
|
ModelBinder = binder.Object,
|
||||||
};
|
};
|
||||||
|
|
||||||
var inputFormattersProvider = new Mock<IInputFormattersProvider>();
|
var argumentBinder = GetArgumentBinder();
|
||||||
inputFormattersProvider
|
|
||||||
.SetupGet(o => o.InputFormatters)
|
|
||||||
.Returns(new List<IInputFormatter>());
|
|
||||||
|
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var invoker = new DefaultControllerActionArgumentBinder(
|
|
||||||
modelMetadataProvider,
|
|
||||||
new DefaultObjectValidator(Mock.Of<IValidationExcludeFiltersProvider>(), modelMetadataProvider),
|
|
||||||
new MockMvcOptionsAccessor());
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await invoker.GetActionArgumentsAsync(actionContext, actionBindingContext);
|
var result = await argumentBinder
|
||||||
|
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(result);
|
Assert.Empty(result);
|
||||||
|
|
@ -158,19 +131,14 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Func<object, int> method = foo => 1;
|
Func<object, int> method = foo => 1;
|
||||||
var actionDescriptor = new ControllerActionDescriptor
|
var actionDescriptor = GetActionDescriptor();
|
||||||
{
|
actionDescriptor.Parameters.Add(
|
||||||
MethodInfo = method.Method,
|
new ParameterDescriptor
|
||||||
Parameters = new List<ParameterDescriptor>
|
|
||||||
{
|
{
|
||||||
new ParameterDescriptor
|
Name = "foo",
|
||||||
{
|
ParameterType = typeof(string),
|
||||||
Name = "foo",
|
|
||||||
ParameterType = typeof(string),
|
|
||||||
BindingInfo = new BindingInfo(),
|
BindingInfo = new BindingInfo(),
|
||||||
}
|
});
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
var value = "Hello world";
|
var value = "Hello world";
|
||||||
var metadataProvider = new EmptyModelMetadataProvider();
|
var metadataProvider = new EmptyModelMetadataProvider();
|
||||||
|
|
@ -194,16 +162,11 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
ModelBinder = binder.Object,
|
ModelBinder = binder.Object,
|
||||||
};
|
};
|
||||||
|
|
||||||
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
var argumentBinder = GetArgumentBinder();
|
||||||
mockValidatorProvider.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()));
|
|
||||||
|
|
||||||
var invoker = new DefaultControllerActionArgumentBinder(
|
|
||||||
metadataProvider,
|
|
||||||
mockValidatorProvider.Object,
|
|
||||||
new MockMvcOptionsAccessor());
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await invoker.GetActionArgumentsAsync(actionContext, actionBindingContext);
|
var result = await argumentBinder
|
||||||
|
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(1, result.Count);
|
Assert.Equal(1, result.Count);
|
||||||
|
|
@ -214,50 +177,26 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
public async Task GetActionArgumentsAsync_CallsValidator_IfModelBinderSucceeds()
|
public async Task GetActionArgumentsAsync_CallsValidator_IfModelBinderSucceeds()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Func<object, int> method = foo => 1;
|
var actionDescriptor = GetActionDescriptor();
|
||||||
var actionDescriptor = new ControllerActionDescriptor
|
actionDescriptor.Parameters.Add(
|
||||||
{
|
new ParameterDescriptor
|
||||||
MethodInfo = method.Method,
|
|
||||||
Parameters = new List<ParameterDescriptor>
|
|
||||||
{
|
{
|
||||||
new ParameterDescriptor
|
Name = "foo",
|
||||||
{
|
ParameterType = typeof(object),
|
||||||
Name = "foo",
|
});
|
||||||
ParameterType = typeof(object),
|
|
||||||
BindingInfo = new BindingInfo(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var actionContext = new ActionContext(
|
var actionContext = GetActionContext(actionDescriptor);
|
||||||
new DefaultHttpContext(),
|
var actionBindingContext = GetActionBindingContext();
|
||||||
new RouteData(),
|
|
||||||
actionDescriptor);
|
|
||||||
|
|
||||||
var binder = new Mock<IModelBinder>();
|
|
||||||
binder
|
|
||||||
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
|
||||||
.Returns(Task.FromResult(result: new ModelBindingResult(
|
|
||||||
model: null,
|
|
||||||
key: string.Empty,
|
|
||||||
isModelSet: true)));
|
|
||||||
|
|
||||||
var actionBindingContext = new ActionBindingContext()
|
|
||||||
{
|
|
||||||
ModelBinder = binder.Object,
|
|
||||||
};
|
|
||||||
|
|
||||||
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||||
mockValidatorProvider
|
mockValidatorProvider
|
||||||
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()))
|
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var invoker = new DefaultControllerActionArgumentBinder(
|
var argumentBinder = GetArgumentBinder(mockValidatorProvider.Object);
|
||||||
TestModelMetadataProvider.CreateDefaultProvider(),
|
|
||||||
mockValidatorProvider.Object,
|
|
||||||
new MockMvcOptionsAccessor());
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await invoker.GetActionArgumentsAsync(actionContext, actionBindingContext);
|
var result = await argumentBinder
|
||||||
|
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
mockValidatorProvider.Verify(
|
mockValidatorProvider.Verify(
|
||||||
|
|
@ -269,19 +208,14 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Func<object, int> method = foo => 1;
|
Func<object, int> method = foo => 1;
|
||||||
var actionDescriptor = new ControllerActionDescriptor
|
var actionDescriptor = GetActionDescriptor();
|
||||||
{
|
actionDescriptor.Parameters.Add(
|
||||||
MethodInfo = method.Method,
|
new ParameterDescriptor
|
||||||
Parameters = new List<ParameterDescriptor>
|
|
||||||
{
|
{
|
||||||
new ParameterDescriptor
|
Name = "foo",
|
||||||
{
|
ParameterType = typeof(object),
|
||||||
Name = "foo",
|
|
||||||
ParameterType = typeof(object),
|
|
||||||
BindingInfo = new BindingInfo(),
|
BindingInfo = new BindingInfo(),
|
||||||
}
|
});
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var actionContext = new ActionContext(
|
var actionContext = new ActionContext(
|
||||||
new DefaultHttpContext(),
|
new DefaultHttpContext(),
|
||||||
|
|
@ -301,13 +235,11 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||||
mockValidatorProvider.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()))
|
mockValidatorProvider.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var invoker = new DefaultControllerActionArgumentBinder(
|
var argumentBinder = GetArgumentBinder(mockValidatorProvider.Object);
|
||||||
TestModelMetadataProvider.CreateDefaultProvider(),
|
|
||||||
mockValidatorProvider.Object,
|
|
||||||
new MockMvcOptionsAccessor());
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await invoker.GetActionArgumentsAsync(actionContext, actionBindingContext);
|
var result = await argumentBinder
|
||||||
|
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
mockValidatorProvider.Verify(o => o.Validate(It.IsAny<ModelValidationContext>()), Times.Never());
|
mockValidatorProvider.Verify(o => o.Validate(It.IsAny<ModelValidationContext>()), Times.Never());
|
||||||
|
|
@ -318,55 +250,179 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Func<object, int> method = foo => 1;
|
Func<object, int> method = foo => 1;
|
||||||
var actionDescriptor = new ControllerActionDescriptor
|
var actionDescriptor = GetActionDescriptor();
|
||||||
{
|
actionDescriptor.Parameters.Add(
|
||||||
MethodInfo = method.Method,
|
new ParameterDescriptor
|
||||||
Parameters = new List<ParameterDescriptor>
|
|
||||||
{
|
{
|
||||||
new ParameterDescriptor
|
Name = "foo",
|
||||||
{
|
ParameterType = typeof(object),
|
||||||
Name = "foo",
|
|
||||||
ParameterType = typeof(object),
|
|
||||||
BindingInfo = new BindingInfo(),
|
BindingInfo = new BindingInfo(),
|
||||||
}
|
});
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var binder = new Mock<IModelBinder>();
|
|
||||||
binder
|
|
||||||
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
|
||||||
.Returns(Task.FromResult(
|
|
||||||
result: new ModelBindingResult(model: "Hello", key: string.Empty, isModelSet: true)));
|
|
||||||
|
|
||||||
var actionContext = new ActionContext(
|
var actionContext = new ActionContext(
|
||||||
new DefaultHttpContext(),
|
new DefaultHttpContext(),
|
||||||
new RouteData(),
|
new RouteData(),
|
||||||
actionDescriptor);
|
actionDescriptor);
|
||||||
|
|
||||||
|
var actionBindingContext = GetActionBindingContext();
|
||||||
|
|
||||||
|
var argumentBinder = GetArgumentBinder();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await argumentBinder
|
||||||
|
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(5, actionContext.ModelState.MaxAllowedErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetActionArgumentsAsync_CallsValidator_ForControllerProperties_IfModelBinderSucceeds()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var actionDescriptor = GetActionDescriptor();
|
||||||
|
actionDescriptor.BoundProperties.Add(
|
||||||
|
new ParameterDescriptor
|
||||||
|
{
|
||||||
|
Name = "ValueBinderMarkedProperty",
|
||||||
|
ParameterType = typeof(string),
|
||||||
|
});
|
||||||
|
|
||||||
|
var actionContext = GetActionContext(actionDescriptor);
|
||||||
|
var actionBindingContext = GetActionBindingContext();
|
||||||
|
|
||||||
|
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||||
|
mockValidatorProvider
|
||||||
|
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()))
|
||||||
|
.Verifiable();
|
||||||
|
var argumentBinder = GetArgumentBinder(mockValidatorProvider.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await argumentBinder
|
||||||
|
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
mockValidatorProvider.Verify(
|
||||||
|
o => o.Validate(It.IsAny<ModelValidationContext>()), Times.Once());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetActionArgumentsAsync_DoesNotCallValidator_ForControllerProperties_IfModelBinderFails()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
Func<object, int> method = foo => 1;
|
||||||
|
var actionDescriptor = GetActionDescriptor();
|
||||||
|
actionDescriptor.BoundProperties.Add(
|
||||||
|
new ParameterDescriptor
|
||||||
|
{
|
||||||
|
Name = "ValueBinderMarkedProperty",
|
||||||
|
ParameterType = typeof(string),
|
||||||
|
});
|
||||||
|
|
||||||
|
var actionContext = new ActionContext(
|
||||||
|
new DefaultHttpContext(),
|
||||||
|
new RouteData(),
|
||||||
|
actionDescriptor);
|
||||||
|
|
||||||
|
var binder = new Mock<IModelBinder>();
|
||||||
|
binder
|
||||||
|
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
|
.Returns(Task.FromResult<ModelBindingResult>(null));
|
||||||
|
|
||||||
var actionBindingContext = new ActionBindingContext()
|
var actionBindingContext = new ActionBindingContext()
|
||||||
{
|
{
|
||||||
ModelBinder = binder.Object,
|
ModelBinder = binder.Object,
|
||||||
};
|
};
|
||||||
|
|
||||||
var inputFormattersProvider = new Mock<IInputFormattersProvider>();
|
|
||||||
inputFormattersProvider
|
|
||||||
.SetupGet(o => o.InputFormatters)
|
|
||||||
.Returns(new List<IInputFormatter>());
|
|
||||||
|
|
||||||
var options = new MockMvcOptionsAccessor();
|
|
||||||
options.Options.MaxModelValidationErrors = 5;
|
|
||||||
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||||
mockValidatorProvider.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()));
|
mockValidatorProvider.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()))
|
||||||
var invoker = new DefaultControllerActionArgumentBinder(
|
.Verifiable();
|
||||||
TestModelMetadataProvider.CreateDefaultProvider(),
|
var argumentBinder = GetArgumentBinder(mockValidatorProvider.Object);
|
||||||
mockValidatorProvider.Object,
|
|
||||||
options);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await invoker.GetActionArgumentsAsync(actionContext, actionBindingContext);
|
var result = await argumentBinder
|
||||||
|
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(5, actionContext.ModelState.MaxAllowedErrors);
|
mockValidatorProvider.Verify(o => o.Validate(It.IsAny<ModelValidationContext>()), Times.Never());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetActionArgumentsAsync_SetsControllerProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var actionDescriptor = GetActionDescriptor();
|
||||||
|
actionDescriptor.BoundProperties.Add(
|
||||||
|
new ParameterDescriptor
|
||||||
|
{
|
||||||
|
Name = "ValueBinderMarkedProperty",
|
||||||
|
BindingInfo = new BindingInfo(),
|
||||||
|
ParameterType = typeof(string)
|
||||||
|
});
|
||||||
|
|
||||||
|
var actionContext = GetActionContext(actionDescriptor);
|
||||||
|
var actionBindingContext = GetActionBindingContext();
|
||||||
|
var argumentBinder = GetArgumentBinder();
|
||||||
|
var controller = new TestController();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await argumentBinder.BindActionArgumentsAsync(actionContext, actionBindingContext, controller);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("Hello", controller.ValueBinderMarkedProperty);
|
||||||
|
Assert.Null(controller.UnmarkedProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ActionContext GetActionContext(ActionDescriptor descriptor = null)
|
||||||
|
{
|
||||||
|
return new ActionContext(
|
||||||
|
new DefaultHttpContext(),
|
||||||
|
new RouteData(),
|
||||||
|
descriptor ?? GetActionDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ActionDescriptor GetActionDescriptor()
|
||||||
|
{
|
||||||
|
Func<object, int> method = foo => 1;
|
||||||
|
return new ControllerActionDescriptor
|
||||||
|
{
|
||||||
|
MethodInfo = method.Method,
|
||||||
|
ControllerTypeInfo = typeof(TestController).GetTypeInfo(),
|
||||||
|
BoundProperties = new List<ParameterDescriptor>(),
|
||||||
|
Parameters = new List<ParameterDescriptor>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ActionBindingContext GetActionBindingContext()
|
||||||
|
{
|
||||||
|
var binder = new Mock<IModelBinder>();
|
||||||
|
binder
|
||||||
|
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
|
.Returns(Task.FromResult(
|
||||||
|
result: new ModelBindingResult(model: "Hello", key: string.Empty, isModelSet: true)));
|
||||||
|
return new ActionBindingContext()
|
||||||
|
{
|
||||||
|
ModelBinder = binder.Object,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DefaultControllerActionArgumentBinder GetArgumentBinder(IObjectModelValidator validator = null)
|
||||||
|
{
|
||||||
|
var options = new MockMvcOptionsAccessor();
|
||||||
|
options.Options.MaxModelValidationErrors = 5;
|
||||||
|
|
||||||
|
if (validator == null)
|
||||||
|
{
|
||||||
|
var mockValidator = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||||
|
mockValidator.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()));
|
||||||
|
validator = mockValidator.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DefaultControllerActionArgumentBinder(
|
||||||
|
TestModelMetadataProvider.CreateDefaultProvider(),
|
||||||
|
validator,
|
||||||
|
options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestController
|
private class TestController
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,22 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
private readonly Action<IApplicationBuilder> _app = new ModelBindingWebSite.Startup().Configure;
|
private readonly Action<IApplicationBuilder> _app = new ModelBindingWebSite.Startup().Configure;
|
||||||
private readonly Action<IServiceCollection> _configureServices = new ModelBindingWebSite.Startup().ConfigureServices;
|
private readonly Action<IServiceCollection> _configureServices = new ModelBindingWebSite.Startup().ConfigureServices;
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DoNotValidate_ParametersOrControllerProperties_IfSourceNotFromRequest()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/Validation/DoNotValidateParameter");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var stringValue = await response.Content.ReadAsStringAsync();
|
||||||
|
var isModelStateValid = JsonConvert.DeserializeObject<bool>(stringValue);
|
||||||
|
Assert.True(isModelStateValid);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TypeBasedExclusion_ForBodyAndNonBodyBoundModels()
|
public async Task TypeBasedExclusion_ForBodyAndNonBodyBoundModels()
|
||||||
{
|
{
|
||||||
|
|
@ -181,6 +197,74 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
exception.ExceptionMessage);
|
exception.ExceptionMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ControllerPropertyAndAnActionWithoutFromBody_InvokesWithoutErrors()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/FromBodyControllerProperty/GetSiteUser");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ControllerPropertyAndAnActionParameterWithFromBody_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/FromBodyControllerProperty/AddUser");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var exception = response.GetServerException();
|
||||||
|
Assert.Equal(typeof(InvalidOperationException).FullName, exception.ExceptionType);
|
||||||
|
Assert.Equal(
|
||||||
|
"More than one parameter and/or property is bound to the HTTP request's content.",
|
||||||
|
exception.ExceptionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ControllerPropertyAndAModelPropertyWithFromBody_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/FromBodyControllerProperty/AddUser");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var exception = response.GetServerException();
|
||||||
|
Assert.Equal(typeof(InvalidOperationException).FullName, exception.ExceptionType);
|
||||||
|
Assert.Equal(
|
||||||
|
"More than one parameter and/or property is bound to the HTTP request's content.",
|
||||||
|
exception.ExceptionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MultipleControllerPropertiesMarkedWithFromBody_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.GetAsync("http://localhost/MultiplePropertiesFromBody/GetUser");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var exception = response.GetServerException();
|
||||||
|
Assert.Equal(typeof(InvalidOperationException).FullName, exception.ExceptionType);
|
||||||
|
Assert.Equal(
|
||||||
|
"More than one parameter and/or property is bound to the HTTP request's content.",
|
||||||
|
exception.ExceptionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task MultipleParameterAndPropertiesMarkedWithFromBody_Throws()
|
public async Task MultipleParameterAndPropertiesMarkedWithFromBody_Throws()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -279,7 +279,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
|
||||||
private static ModelValidationContext CreateValidationContext(ModelExplorer modelExplorer)
|
private static ModelValidationContext CreateValidationContext(ModelExplorer modelExplorer)
|
||||||
{
|
{
|
||||||
return new ModelValidationContext(null, null, null, modelExplorer);
|
return new ModelValidationContext(
|
||||||
|
rootPrefix: null,
|
||||||
|
bindingSource: null,
|
||||||
|
modelState: null,
|
||||||
|
validatorProvider: null,
|
||||||
|
modelExplorer: modelExplorer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DerivedRequiredAttribute : RequiredAttribute
|
private class DerivedRequiredAttribute : RequiredAttribute
|
||||||
|
|
|
||||||
|
|
@ -509,6 +509,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
ModelValidationContext = new ModelValidationContext(
|
ModelValidationContext = new ModelValidationContext(
|
||||||
key,
|
key,
|
||||||
|
null,
|
||||||
TestModelValidatorProvider.CreateDefaultProvider(),
|
TestModelValidatorProvider.CreateDefaultProvider(),
|
||||||
modelStateDictionary,
|
modelStateDictionary,
|
||||||
modelExplorer),
|
modelExplorer),
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ namespace ApplicationModelWebSite
|
||||||
{
|
{
|
||||||
public void Apply(ParameterModel model)
|
public void Apply(ParameterModel model)
|
||||||
{
|
{
|
||||||
|
model.BindingInfo = model.BindingInfo ?? new BindingInfo();
|
||||||
model.BindingInfo.BindingSource = BindingSource.Custom;
|
model.BindingInfo.BindingSource = BindingSource.Custom;
|
||||||
model.BindingInfo.BinderModelName = "CoolMetadata";
|
model.BindingInfo.BinderModelName = "CoolMetadata";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace FormatterWebSite
|
||||||
var bodyParameter = context.ActionDescriptor
|
var bodyParameter = context.ActionDescriptor
|
||||||
.Parameters
|
.Parameters
|
||||||
.FirstOrDefault(parameter => IsBodyBindingSource(
|
.FirstOrDefault(parameter => IsBodyBindingSource(
|
||||||
parameter.BindingInfo.BindingSource));
|
parameter.BindingInfo?.BindingSource));
|
||||||
if (bodyParameter != null)
|
if (bodyParameter != null)
|
||||||
{
|
{
|
||||||
var parameterBindingErrors = context.ModelState[bodyParameter.Name].Errors;
|
var parameterBindingErrors = context.ModelState[bodyParameter.Name].Errors;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
// 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 Microsoft.AspNet.Mvc;
|
||||||
|
using ModelBindingWebSite.Models;
|
||||||
|
|
||||||
|
namespace ModelBindingWebSite.Controllers
|
||||||
|
{
|
||||||
|
public class FromBodyControllerProperty : Controller
|
||||||
|
{
|
||||||
|
[FromBody]
|
||||||
|
public User SiteUser { get; set; }
|
||||||
|
|
||||||
|
public User GetSiteUser(int id)
|
||||||
|
{
|
||||||
|
return SiteUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will throw as Customer reads body.
|
||||||
|
public Customer GetCustomer(Customer customer)
|
||||||
|
{
|
||||||
|
return customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will throw as a controller property and a parameter name are being read from body.
|
||||||
|
public void AddUser([FromBody] User user)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// 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 Microsoft.AspNet.Mvc;
|
||||||
|
using ModelBindingWebSite.Models;
|
||||||
|
|
||||||
|
namespace ModelBindingWebSite.Controllers
|
||||||
|
{
|
||||||
|
public class MultiplePropertiesFromBodyController : Controller
|
||||||
|
{
|
||||||
|
[FromBody]
|
||||||
|
public User SiteUser { get; set; }
|
||||||
|
|
||||||
|
[FromBody]
|
||||||
|
public Country Country { get; set; }
|
||||||
|
|
||||||
|
public User GetUser()
|
||||||
|
{
|
||||||
|
return SiteUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,9 @@ namespace ModelBindingWebSite.Controllers
|
||||||
[Route("Validation/[Action]")]
|
[Route("Validation/[Action]")]
|
||||||
public class ValidationController : Controller
|
public class ValidationController : Controller
|
||||||
{
|
{
|
||||||
|
[FromServices]
|
||||||
|
public ITestService ControllerService { get; set; }
|
||||||
|
|
||||||
public bool SkipValidation(Resident resident)
|
public bool SkipValidation(Resident resident)
|
||||||
{
|
{
|
||||||
return ModelState.IsValid;
|
return ModelState.IsValid;
|
||||||
|
|
@ -21,6 +24,11 @@ namespace ModelBindingWebSite.Controllers
|
||||||
return ModelState.IsValid;
|
return ModelState.IsValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool DoNotValidateParameter([FromServices] ITestService service)
|
||||||
|
{
|
||||||
|
return ModelState.IsValid;
|
||||||
|
}
|
||||||
|
|
||||||
public IActionResult CreateRectangle([FromBody] Rectangle rectangle)
|
public IActionResult CreateRectangle([FromBody] Rectangle rectangle)
|
||||||
{
|
{
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace ModelBindingWebSite
|
namespace ModelBindingWebSite
|
||||||
{
|
{
|
||||||
public interface ITestService
|
public interface ITestService
|
||||||
{
|
{
|
||||||
|
[Required]
|
||||||
|
string NeverBound { get; set; }
|
||||||
|
|
||||||
bool Test();
|
bool Test();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace ModelBindingWebSite
|
namespace ModelBindingWebSite
|
||||||
{
|
{
|
||||||
public class TestService : ITestService
|
public class TestService : ITestService
|
||||||
{
|
{
|
||||||
|
[Required]
|
||||||
|
public string NeverBound { get; set; }
|
||||||
|
|
||||||
public bool Test()
|
public bool Test()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue