[Fixefor #860]- Adding Support for [FromFormData] [FromQuery] and [FromRoute],
also updates FromBody to use the new pattern for model binding.
This commit is contained in:
parent
8cf74afc75
commit
8f933b2ac3
|
|
@ -0,0 +1,85 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a default implementation of <see cref="IControllerActionArgumentBinder"/>.
|
||||||
|
/// Uses ModelBinding to populate action parameters.
|
||||||
|
/// </summary>
|
||||||
|
public class DefaultControllerActionArgumentBinder : IControllerActionArgumentBinder
|
||||||
|
{
|
||||||
|
private readonly IBodyModelValidator _modelValidator;
|
||||||
|
private readonly IActionBindingContextProvider _bindingContextProvider;
|
||||||
|
|
||||||
|
public DefaultControllerActionArgumentBinder(IActionBindingContextProvider bindingContextProvider,
|
||||||
|
IBodyModelValidator modelValidator)
|
||||||
|
{
|
||||||
|
_bindingContextProvider = bindingContextProvider;
|
||||||
|
_modelValidator = modelValidator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IDictionary<string, object>> GetActionArgumentsAsync(ActionContext actionContext)
|
||||||
|
{
|
||||||
|
var actionBindingContext = await _bindingContextProvider.GetActionBindingContextAsync(actionContext);
|
||||||
|
var metadataProvider = actionBindingContext.MetadataProvider;
|
||||||
|
var parameters = actionContext.ActionDescriptor.Parameters;
|
||||||
|
var actionDescriptor = actionContext.ActionDescriptor as ControllerActionDescriptor;
|
||||||
|
if (actionDescriptor == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(
|
||||||
|
Resources.FormatActionDescriptorMustBeBasedOnControllerAction(
|
||||||
|
typeof(ControllerActionDescriptor)),
|
||||||
|
nameof(actionContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
var actionMethodInfo = actionDescriptor.MethodInfo;
|
||||||
|
var parameterMetadatas = metadataProvider.GetMetadataForParameters(actionMethodInfo);
|
||||||
|
|
||||||
|
var actionArguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||||
|
await PopulateActionArgumentsAsync(parameterMetadatas, actionBindingContext, actionArguments);
|
||||||
|
return actionArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PopulateActionArgumentsAsync(IEnumerable<ModelMetadata> modelMetadatas,
|
||||||
|
ActionBindingContext actionBindingContext,
|
||||||
|
IDictionary<string, object> invocationInfo)
|
||||||
|
{
|
||||||
|
var bodyBoundParameterCount = modelMetadatas.Count(
|
||||||
|
modelMetadata => modelMetadata.Marker is IBodyBinderMarker);
|
||||||
|
if (bodyBoundParameterCount > 1)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(Resources.MultipleBodyParametersAreNotAllowed);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var modelMetadata in modelMetadatas)
|
||||||
|
{
|
||||||
|
var parameterType = modelMetadata.ModelType;
|
||||||
|
var modelBindingContext = new ModelBindingContext
|
||||||
|
{
|
||||||
|
ModelName = modelMetadata.PropertyName,
|
||||||
|
ModelMetadata = modelMetadata,
|
||||||
|
ModelState = actionBindingContext.ActionContext.ModelState,
|
||||||
|
ModelBinder = actionBindingContext.ModelBinder,
|
||||||
|
ValidatorProvider = actionBindingContext.ValidatorProvider,
|
||||||
|
MetadataProvider = actionBindingContext.MetadataProvider,
|
||||||
|
HttpContext = actionBindingContext.ActionContext.HttpContext,
|
||||||
|
FallbackToEmptyPrefix = true,
|
||||||
|
ValueProvider = actionBindingContext.ValueProvider,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (await actionBindingContext.ModelBinder.BindModelAsync(modelBindingContext))
|
||||||
|
{
|
||||||
|
invocationInfo[modelMetadata.PropertyName] = modelBindingContext.Model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// 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;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Mvc.Core;
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
|
|
@ -15,19 +16,20 @@ namespace Microsoft.AspNet.Mvc
|
||||||
private readonly ControllerActionDescriptor _descriptor;
|
private readonly ControllerActionDescriptor _descriptor;
|
||||||
private readonly IControllerFactory _controllerFactory;
|
private readonly IControllerFactory _controllerFactory;
|
||||||
private readonly IInputFormattersProvider _inputFormattersProvider;
|
private readonly IInputFormattersProvider _inputFormattersProvider;
|
||||||
|
private readonly IControllerActionArgumentBinder _actionInvocationProvider;
|
||||||
|
|
||||||
public ControllerActionInvoker([NotNull] ActionContext actionContext,
|
public ControllerActionInvoker([NotNull] ActionContext actionContext,
|
||||||
[NotNull] IActionBindingContextProvider bindingContextProvider,
|
|
||||||
[NotNull] INestedProviderManager<FilterProviderContext> filterProvider,
|
[NotNull] INestedProviderManager<FilterProviderContext> filterProvider,
|
||||||
[NotNull] IControllerFactory controllerFactory,
|
[NotNull] IControllerFactory controllerFactory,
|
||||||
[NotNull] ControllerActionDescriptor descriptor,
|
[NotNull] ControllerActionDescriptor descriptor,
|
||||||
[NotNull] IInputFormattersProvider inputFormattersProvider,
|
[NotNull] IInputFormattersProvider inputFormattersProvider,
|
||||||
[NotNull] IBodyModelValidator modelValidator)
|
[NotNull] IControllerActionArgumentBinder controllerActionArgumentBinder)
|
||||||
: base(actionContext, bindingContextProvider, filterProvider, modelValidator)
|
: base(actionContext, filterProvider)
|
||||||
{
|
{
|
||||||
_descriptor = descriptor;
|
_descriptor = descriptor;
|
||||||
_controllerFactory = controllerFactory;
|
_controllerFactory = controllerFactory;
|
||||||
_inputFormattersProvider = inputFormattersProvider;
|
_inputFormattersProvider = inputFormattersProvider;
|
||||||
|
_actionInvocationProvider = controllerActionArgumentBinder;
|
||||||
if (descriptor.MethodInfo == null)
|
if (descriptor.MethodInfo == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
|
|
@ -67,6 +69,11 @@ namespace Microsoft.AspNet.Mvc
|
||||||
return actionResult;
|
return actionResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Task<IDictionary<string, object>> GetActionArgumentsAsync(ActionContext context)
|
||||||
|
{
|
||||||
|
return _actionInvocationProvider.GetActionArgumentsAsync(context);
|
||||||
|
}
|
||||||
|
|
||||||
// Marking as internal for Unit Testing purposes.
|
// Marking as internal for Unit Testing purposes.
|
||||||
internal static IActionResult CreateActionResult([NotNull] Type declaredReturnType, object actionReturnValue)
|
internal static IActionResult CreateActionResult([NotNull] Type declaredReturnType, object actionReturnValue)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -10,22 +10,19 @@ namespace Microsoft.AspNet.Mvc
|
||||||
public class ControllerActionInvokerProvider : IActionInvokerProvider
|
public class ControllerActionInvokerProvider : IActionInvokerProvider
|
||||||
{
|
{
|
||||||
private readonly IControllerFactory _controllerFactory;
|
private readonly IControllerFactory _controllerFactory;
|
||||||
private readonly IActionBindingContextProvider _bindingProvider;
|
|
||||||
private readonly IInputFormattersProvider _inputFormattersProvider;
|
private readonly IInputFormattersProvider _inputFormattersProvider;
|
||||||
private readonly INestedProviderManager<FilterProviderContext> _filterProvider;
|
private readonly INestedProviderManager<FilterProviderContext> _filterProvider;
|
||||||
private readonly IBodyModelValidator _modelValidator;
|
private readonly IControllerActionArgumentBinder _actionInvocationInfoProvider;
|
||||||
|
|
||||||
public ControllerActionInvokerProvider(IControllerFactory controllerFactory,
|
public ControllerActionInvokerProvider(IControllerFactory controllerFactory,
|
||||||
IActionBindingContextProvider bindingProvider,
|
|
||||||
IInputFormattersProvider inputFormattersProvider,
|
IInputFormattersProvider inputFormattersProvider,
|
||||||
INestedProviderManager<FilterProviderContext> filterProvider,
|
INestedProviderManager<FilterProviderContext> filterProvider,
|
||||||
IBodyModelValidator modelValidator)
|
IControllerActionArgumentBinder actionInvocationInfoProvider)
|
||||||
{
|
{
|
||||||
_controllerFactory = controllerFactory;
|
_controllerFactory = controllerFactory;
|
||||||
_bindingProvider = bindingProvider;
|
|
||||||
_inputFormattersProvider = inputFormattersProvider;
|
_inputFormattersProvider = inputFormattersProvider;
|
||||||
_filterProvider = filterProvider;
|
_filterProvider = filterProvider;
|
||||||
_modelValidator = modelValidator;
|
_actionInvocationInfoProvider = actionInvocationInfoProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Order
|
public int Order
|
||||||
|
|
@ -41,12 +38,11 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
context.Result = new ControllerActionInvoker(
|
context.Result = new ControllerActionInvoker(
|
||||||
context.ActionContext,
|
context.ActionContext,
|
||||||
_bindingProvider,
|
|
||||||
_filterProvider,
|
_filterProvider,
|
||||||
_controllerFactory,
|
_controllerFactory,
|
||||||
actionDescriptor,
|
actionDescriptor,
|
||||||
_inputFormattersProvider,
|
_inputFormattersProvider,
|
||||||
_modelValidator);
|
_actionInvocationInfoProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
callNext();
|
callNext();
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
if (actionDescriptor == null)
|
if (actionDescriptor == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
Resources.FormatDefaultControllerFactory_ActionDescriptorMustBeReflected(
|
Resources.FormatActionDescriptorMustBeBasedOnControllerAction(
|
||||||
typeof(ControllerActionDescriptor)),
|
typeof(ControllerActionDescriptor)),
|
||||||
"actionContext");
|
"actionContext");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
public abstract class FilterActionInvoker : IActionInvoker
|
public abstract class FilterActionInvoker : IActionInvoker
|
||||||
{
|
{
|
||||||
private readonly IActionBindingContextProvider _bindingProvider;
|
|
||||||
private readonly INestedProviderManager<FilterProviderContext> _filterProvider;
|
private readonly INestedProviderManager<FilterProviderContext> _filterProvider;
|
||||||
private readonly IBodyModelValidator _modelValidator;
|
|
||||||
|
|
||||||
private IFilter[] _filters;
|
private IFilter[] _filters;
|
||||||
private FilterCursor _cursor;
|
private FilterCursor _cursor;
|
||||||
|
|
@ -34,20 +32,18 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
public FilterActionInvoker(
|
public FilterActionInvoker(
|
||||||
[NotNull] ActionContext actionContext,
|
[NotNull] ActionContext actionContext,
|
||||||
[NotNull] IActionBindingContextProvider bindingContextProvider,
|
[NotNull] INestedProviderManager<FilterProviderContext> filterProvider)
|
||||||
[NotNull] INestedProviderManager<FilterProviderContext> filterProvider,
|
|
||||||
[NotNull] IBodyModelValidator modelValidator)
|
|
||||||
{
|
{
|
||||||
ActionContext = actionContext;
|
ActionContext = actionContext;
|
||||||
_bindingProvider = bindingContextProvider;
|
|
||||||
_filterProvider = filterProvider;
|
_filterProvider = filterProvider;
|
||||||
_modelValidator = modelValidator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ActionContext ActionContext { get; private set; }
|
protected ActionContext ActionContext { get; private set; }
|
||||||
|
|
||||||
protected abstract Task<IActionResult> InvokeActionAsync(ActionExecutingContext actionExecutingContext);
|
protected abstract Task<IActionResult> InvokeActionAsync(ActionExecutingContext actionExecutingContext);
|
||||||
|
|
||||||
|
protected abstract Task<IDictionary<string, object>> GetActionArgumentsAsync([NotNull] ActionContext context);
|
||||||
|
|
||||||
public virtual async Task InvokeAsync()
|
public virtual async Task InvokeAsync()
|
||||||
{
|
{
|
||||||
_filters = GetFilters();
|
_filters = GetFilters();
|
||||||
|
|
@ -220,80 +216,11 @@ namespace Microsoft.AspNet.Mvc
|
||||||
private async Task InvokeActionMethodWithFilters()
|
private async Task InvokeActionMethodWithFilters()
|
||||||
{
|
{
|
||||||
_cursor.SetStage(FilterStage.ActionFilters);
|
_cursor.SetStage(FilterStage.ActionFilters);
|
||||||
|
var arguments = await GetActionArgumentsAsync(ActionContext);
|
||||||
var arguments = await GetActionArguments(ActionContext.ModelState);
|
|
||||||
_actionExecutingContext = new ActionExecutingContext(ActionContext, _filters, arguments);
|
_actionExecutingContext = new ActionExecutingContext(ActionContext, _filters, arguments);
|
||||||
|
|
||||||
await InvokeActionMethodFilter();
|
await InvokeActionMethodFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<IDictionary<string, object>> GetActionArguments(ModelStateDictionary modelState)
|
|
||||||
{
|
|
||||||
var actionBindingContext = await _bindingProvider.GetActionBindingContextAsync(ActionContext);
|
|
||||||
var parameters = ActionContext.ActionDescriptor.Parameters;
|
|
||||||
var metadataProvider = actionBindingContext.MetadataProvider;
|
|
||||||
var parameterValues = new Dictionary<string, object>(parameters.Count, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
for (var i = 0; i < parameters.Count; i++)
|
|
||||||
{
|
|
||||||
var parameter = parameters[i];
|
|
||||||
var parameterType = parameter.BodyParameterInfo != null ?
|
|
||||||
parameter.BodyParameterInfo.ParameterType : parameter.ParameterBindingInfo.ParameterType;
|
|
||||||
|
|
||||||
if (parameter.BodyParameterInfo != null)
|
|
||||||
{
|
|
||||||
var formatterContext = new InputFormatterContext(actionBindingContext.ActionContext,
|
|
||||||
parameterType);
|
|
||||||
var inputFormatter = actionBindingContext.InputFormatterSelector.SelectFormatter(
|
|
||||||
formatterContext);
|
|
||||||
if (inputFormatter == null)
|
|
||||||
{
|
|
||||||
var request = ActionContext.HttpContext.Request;
|
|
||||||
var unsupportedContentType = Resources.FormatUnsupportedContentType(request.ContentType);
|
|
||||||
ActionContext.ModelState.AddModelError(parameter.Name, unsupportedContentType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
parameterValues[parameter.Name] = await inputFormatter.ReadAsync(formatterContext);
|
|
||||||
var modelMetadata =
|
|
||||||
metadataProvider.GetMetadataForType(modelAccessor: null, modelType: parameterType);
|
|
||||||
modelMetadata.Model = parameterValues[parameter.Name];
|
|
||||||
|
|
||||||
// Validate the generated object
|
|
||||||
var validationContext = new ModelValidationContext(metadataProvider,
|
|
||||||
actionBindingContext.ValidatorProvider,
|
|
||||||
modelState,
|
|
||||||
modelMetadata,
|
|
||||||
containerMetadata: null);
|
|
||||||
_modelValidator.Validate(validationContext, parameter.Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var modelMetadata =
|
|
||||||
metadataProvider.GetMetadataForType(modelAccessor: null, modelType: parameterType);
|
|
||||||
var modelBindingContext = new ModelBindingContext
|
|
||||||
{
|
|
||||||
ModelName = parameter.Name,
|
|
||||||
ModelState = modelState,
|
|
||||||
ModelMetadata = modelMetadata,
|
|
||||||
ModelBinder = actionBindingContext.ModelBinder,
|
|
||||||
ValueProvider = actionBindingContext.ValueProvider,
|
|
||||||
ValidatorProvider = actionBindingContext.ValidatorProvider,
|
|
||||||
MetadataProvider = metadataProvider,
|
|
||||||
HttpContext = actionBindingContext.ActionContext.HttpContext,
|
|
||||||
FallbackToEmptyPrefix = true
|
|
||||||
};
|
|
||||||
if (await actionBindingContext.ModelBinder.BindModelAsync(modelBindingContext))
|
|
||||||
{
|
|
||||||
parameterValues[parameter.Name] = modelBindingContext.Model;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parameterValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<ActionExecutedContext> InvokeActionMethodFilter()
|
private async Task<ActionExecutedContext> InvokeActionMethodFilter()
|
||||||
{
|
{
|
||||||
Contract.Assert(_actionExecutingContext != null);
|
Contract.Assert(_actionExecutingContext != null);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
// 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.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a dictionary of action arguments.
|
||||||
|
/// </summary>
|
||||||
|
public interface IControllerActionArgumentBinder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a dictionary of representing the parameter-argument name-value pairs,
|
||||||
|
/// which can be used to invoke the action.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The action context assoicated with the current action.</param>
|
||||||
|
Task<IDictionary<string, object>> GetActionArgumentsAsync([NotNull] ActionContext context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a model binder which understands <see cref="IBodyBinderMarker"/> and uses
|
||||||
|
/// InputFomatters to bind the model to request's body.
|
||||||
|
/// </summary>
|
||||||
|
public class BodyModelBinder : MarkerAwareBinder<IBodyBinderMarker>
|
||||||
|
{
|
||||||
|
private readonly ActionContext _actionContext;
|
||||||
|
private readonly IInputFormatterSelector _formatterSelector;
|
||||||
|
private readonly IBodyModelValidator _bodyModelValidator;
|
||||||
|
|
||||||
|
public BodyModelBinder([NotNull] IContextAccessor<ActionContext> context,
|
||||||
|
[NotNull] IInputFormatterSelector selector,
|
||||||
|
[NotNull] IBodyModelValidator bodyModelValidator)
|
||||||
|
{
|
||||||
|
_actionContext = context.Value;
|
||||||
|
_formatterSelector = selector;
|
||||||
|
_bodyModelValidator = bodyModelValidator;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<bool> BindAsync(ModelBindingContext bindingContext, IBodyBinderMarker marker)
|
||||||
|
{
|
||||||
|
var formatterContext = new InputFormatterContext(_actionContext, bindingContext.ModelType);
|
||||||
|
var formatter = _formatterSelector.SelectFormatter(formatterContext);
|
||||||
|
|
||||||
|
if (formatter == null)
|
||||||
|
{
|
||||||
|
var unsupportedContentType = Resources.FormatUnsupportedContentType(
|
||||||
|
bindingContext.HttpContext.Request.ContentType);
|
||||||
|
bindingContext.ModelState.AddModelError(bindingContext.ModelName, unsupportedContentType);
|
||||||
|
|
||||||
|
// Should always return true so that the model binding process ends here.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bindingContext.Model = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
|
// Validate the deserialized object
|
||||||
|
var validationContext = new ModelValidationContext(bindingContext, bindingContext.ModelMetadata);
|
||||||
|
_bodyModelValidator.Validate(validationContext, bindingContext.ModelName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// 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;
|
using System;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
|
|
@ -11,6 +12,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
public bool IsOptional { get; set; }
|
public bool IsOptional { get; set; }
|
||||||
|
|
||||||
|
public Type ParameterType { get; set; }
|
||||||
|
|
||||||
public ParameterBindingInfo ParameterBindingInfo { get; set; }
|
public ParameterBindingInfo ParameterBindingInfo { get; set; }
|
||||||
|
|
||||||
public BodyParameterInfo BodyParameterInfo { get; set; }
|
public BodyParameterInfo BodyParameterInfo { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,22 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
private static readonly ResourceManager _resourceManager
|
private static readonly ResourceManager _resourceManager
|
||||||
= new ResourceManager("Microsoft.AspNet.Mvc.Core.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
= new ResourceManager("Microsoft.AspNet.Mvc.Core.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// More than one parameter is bound to the HTTP request's content.
|
||||||
|
/// </summary>
|
||||||
|
internal static string MultipleBodyParametersAreNotAllowed
|
||||||
|
{
|
||||||
|
get { return GetString("MultipleBodyParametersAreNotAllowed"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// More than one parameter is bound to the HTTP request's content.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatMultipleBodyParametersAreNotAllowed()
|
||||||
|
{
|
||||||
|
return GetString("MultipleBodyParametersAreNotAllowed");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The provided anti-forgery token failed a custom data check.
|
/// The provided anti-forgery token failed a custom data check.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -413,17 +429,17 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The action descriptor must be of type '{0}'.
|
/// The action descriptor must be of type '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string DefaultControllerFactory_ActionDescriptorMustBeReflected
|
internal static string ActionDescriptorMustBeBasedOnControllerAction
|
||||||
{
|
{
|
||||||
get { return GetString("DefaultControllerFactory_ActionDescriptorMustBeReflected"); }
|
get { return GetString("ActionDescriptorMustBeBasedOnControllerAction"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The action descriptor must be of type '{0}'.
|
/// The action descriptor must be of type '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatDefaultControllerFactory_ActionDescriptorMustBeReflected(object p0)
|
internal static string FormatActionDescriptorMustBeBasedOnControllerAction(object p0)
|
||||||
{
|
{
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("DefaultControllerFactory_ActionDescriptorMustBeReflected"), p0);
|
return string.Format(CultureInfo.CurrentCulture, GetString("ActionDescriptorMustBeBasedOnControllerAction"), p0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,9 @@
|
||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
|
<data name="MultipleBodyParametersAreNotAllowed" xml:space="preserve">
|
||||||
|
<value>More than one parameter is bound to the HTTP request's content.</value>
|
||||||
|
</data>
|
||||||
<data name="AntiForgeryToken_AdditionalDataCheckFailed" xml:space="preserve">
|
<data name="AntiForgeryToken_AdditionalDataCheckFailed" xml:space="preserve">
|
||||||
<value>The provided anti-forgery token failed a custom data check.</value>
|
<value>The provided anti-forgery token failed a custom data check.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -192,7 +195,7 @@
|
||||||
<data name="ActionInvokerFactory_CouldNotCreateInvoker" xml:space="preserve">
|
<data name="ActionInvokerFactory_CouldNotCreateInvoker" xml:space="preserve">
|
||||||
<value>An action invoker could not be created for action '{0}'.</value>
|
<value>An action invoker could not be created for action '{0}'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DefaultControllerFactory_ActionDescriptorMustBeReflected" xml:space="preserve">
|
<data name="ActionDescriptorMustBeBasedOnControllerAction" xml:space="preserve">
|
||||||
<value>The action descriptor must be of type '{0}'.</value>
|
<value>The action descriptor must be of type '{0}'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ArgumentCannotBeNullOrEmpty" xml:space="preserve">
|
<data name="ArgumentCannotBeNullOrEmpty" xml:space="preserve">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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.ModelBinding;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This attribute is used on action parameters to indicate
|
||||||
|
/// they are bound from the body of the incoming request.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||||
|
public class FromBodyAttribute : Attribute, IBodyBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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.ModelBinding;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This attribute is used on action parameters to indicate that
|
||||||
|
/// they will be bound using form data of the incoming request.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||||
|
public class FromFormAttribute : Attribute, IFormDataMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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.ModelBinding;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This attribute is used on action parameters to indicate that
|
||||||
|
/// they will be bound using query data of the incoming request.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||||
|
public class FromQueryAttribute : Attribute, IQueryBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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.ModelBinding;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This attribute is used on action parameters to indicate that
|
||||||
|
/// they will be bound using route data of the incoming request.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||||
|
public class FromRouteAttribute : Attribute, IRouteDataMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,12 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
[AttributeUsage(AttributeTargets.Parameter)]
|
/// <summary>
|
||||||
public sealed class FromBodyAttribute : Attribute
|
/// Represents a marker used to identify a particular binder applies to a model.
|
||||||
|
/// </summary>
|
||||||
|
public interface IBinderMarker
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a marker used to identify a binder which can bind request body to a model.
|
||||||
|
/// </summary>
|
||||||
|
public interface IBodyBinderMarker : IBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a marker used to identify a binder which can bind form data to a model.
|
||||||
|
/// </summary>
|
||||||
|
public interface IFormDataMarker : IValueBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a marker used to identify a binder which can bind query data to a model.
|
||||||
|
/// </summary>
|
||||||
|
public interface IQueryBinderMarker : IValueBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a marker used to identify a binder which can bind route data to a model.
|
||||||
|
/// </summary>
|
||||||
|
public interface IRouteDataMarker : IValueBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a binder marker which identifies a binder which is based on a value provider.
|
||||||
|
/// </summary>
|
||||||
|
public interface IValueBinderMarker : IBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
// 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.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
@ -132,7 +134,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
ValidatorProvider = oldBindingContext.ValidatorProvider,
|
ValidatorProvider = oldBindingContext.ValidatorProvider,
|
||||||
MetadataProvider = oldBindingContext.MetadataProvider,
|
MetadataProvider = oldBindingContext.MetadataProvider,
|
||||||
ModelBinder = oldBindingContext.ModelBinder,
|
ModelBinder = oldBindingContext.ModelBinder,
|
||||||
HttpContext = oldBindingContext.HttpContext
|
HttpContext = oldBindingContext.HttpContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
// validation is expensive to create, so copy it over if we can
|
// validation is expensive to create, so copy it over if we can
|
||||||
|
|
@ -141,6 +143,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
newBindingContext.ValidationNode = oldBindingContext.ValidationNode;
|
newBindingContext.ValidationNode = oldBindingContext.ValidationNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// look at the value providers and see if they need to be restricted.
|
||||||
|
var marker = oldBindingContext.ModelMetadata.Marker as IValueBinderMarker;
|
||||||
|
if (marker != null)
|
||||||
|
{
|
||||||
|
var valueProvider = oldBindingContext.ValueProvider as IMarkerAwareValueProvider;
|
||||||
|
if (valueProvider != null)
|
||||||
|
{
|
||||||
|
newBindingContext.ValueProvider = valueProvider.Filter(marker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return newBindingContext;
|
return newBindingContext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="IModelBinder"/> which is aware of <see cref="IBinderMarker"/>.
|
||||||
|
/// </summary>
|
||||||
|
public interface IMarkerAwareBinder : IModelBinder
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
// 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.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an <see cref="IMarkerAwareBinder"/> which can select itself based on the
|
||||||
|
/// <typeparamref name="TBinderMarker"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TBinderMarker">Represents a type implementing <see cref="IBinderMarker"/></typeparam>
|
||||||
|
public abstract class MarkerAwareBinder<TBinderMarker> : IMarkerAwareBinder
|
||||||
|
where TBinderMarker : IBinderMarker
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Async function which does the actual binding to bind to a particular model.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bindingContext">The binding context which has the object to be bound.</param>
|
||||||
|
/// <param name="marker">The <see cref="IBinderMarker"/> associated with the current binder.</param>
|
||||||
|
/// <returns>A Task with a bool implying the success or failure of the operation.</returns>
|
||||||
|
protected abstract Task<bool> BindAsync(ModelBindingContext bindingContext, TBinderMarker marker);
|
||||||
|
|
||||||
|
public Task<bool> BindModelAsync(ModelBindingContext context)
|
||||||
|
{
|
||||||
|
if (context.ModelMetadata.Marker is TBinderMarker)
|
||||||
|
{
|
||||||
|
var marker = (TBinderMarker)context.ModelMetadata.Marker;
|
||||||
|
return BindAsync(context, marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -46,6 +46,36 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
return CreateMetadataFromPrototype(prototype, modelAccessor);
|
return CreateMetadataFromPrototype(prototype, modelAccessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ModelMetadata> GetMetadataForParameters([NotNull] MethodInfo methodInfo)
|
||||||
|
{
|
||||||
|
var parameters = methodInfo.GetParameters();
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
// Name can be null if the methodinfo represents an open delegate.
|
||||||
|
if (!string.IsNullOrEmpty(parameter.Name))
|
||||||
|
{
|
||||||
|
yield return GetMetadataForParameterCore(
|
||||||
|
modelAccessor: null, parameterName: parameter.Name, parameter: parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelMetadata GetMetadataForParameter(
|
||||||
|
Func<object> modelAccessor,
|
||||||
|
[NotNull] MethodInfo methodInfo,
|
||||||
|
[NotNull] string parameterName)
|
||||||
|
{
|
||||||
|
var parameter = methodInfo.GetParameters().FirstOrDefault(
|
||||||
|
param => StringComparer.Ordinal.Equals(param.Name, parameterName));
|
||||||
|
if (parameter == null)
|
||||||
|
{
|
||||||
|
var message = Resources.FormatCommon_ParameterNotFound(parameterName);
|
||||||
|
throw new ArgumentException(message, nameof(parameterName));
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetMetadataForParameterCore(modelAccessor, parameterName, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
// Override for creating the prototype metadata (without the accessor)
|
// Override for creating the prototype metadata (without the accessor)
|
||||||
protected abstract TModelMetadata CreateMetadataPrototype(IEnumerable<Attribute> attributes,
|
protected abstract TModelMetadata CreateMetadataPrototype(IEnumerable<Attribute> attributes,
|
||||||
Type containerType,
|
Type containerType,
|
||||||
|
|
@ -56,6 +86,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
protected abstract TModelMetadata CreateMetadataFromPrototype(TModelMetadata prototype,
|
protected abstract TModelMetadata CreateMetadataFromPrototype(TModelMetadata prototype,
|
||||||
Func<object> modelAccessor);
|
Func<object> modelAccessor);
|
||||||
|
|
||||||
|
private ModelMetadata GetMetadataForParameterCore(Func<object> modelAccessor,
|
||||||
|
string parameterName,
|
||||||
|
ParameterInfo parameter)
|
||||||
|
{
|
||||||
|
var parameterInfo =
|
||||||
|
CreateParameterInfo(parameter.ParameterType,
|
||||||
|
parameter.GetCustomAttributes(),
|
||||||
|
parameterName);
|
||||||
|
var typePrototype = GetTypeInformation(parameter.ParameterType).Prototype;
|
||||||
|
return CreateMetadataFromPrototype(parameterInfo.Prototype, modelAccessor);
|
||||||
|
}
|
||||||
|
|
||||||
private IEnumerable<ModelMetadata> GetMetadataForPropertiesCore(object container, Type containerType)
|
private IEnumerable<ModelMetadata> GetMetadataForPropertiesCore(object container, Type containerType)
|
||||||
{
|
{
|
||||||
var typeInfo = GetTypeInformation(containerType);
|
var typeInfo = GetTypeInformation(containerType);
|
||||||
|
|
@ -78,6 +120,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
metadata.IsReadOnly = true;
|
metadata.IsReadOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,6 +181,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ParameterInformation CreateParameterInfo(Type parameterType, IEnumerable<Attribute> attributes, string parameterName)
|
||||||
|
{
|
||||||
|
var metadataProtoType = CreateMetadataPrototype(attributes: attributes,
|
||||||
|
containerType: null,
|
||||||
|
modelType: parameterType,
|
||||||
|
propertyName: parameterName);
|
||||||
|
|
||||||
|
return new ParameterInformation
|
||||||
|
{
|
||||||
|
Prototype = metadataProtoType
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class ParameterInformation
|
||||||
|
{
|
||||||
|
public TModelMetadata Prototype { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class TypeInformation
|
private sealed class TypeInformation
|
||||||
{
|
{
|
||||||
public TModelMetadata Prototype { get; set; }
|
public TModelMetadata Prototype { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
@ -32,6 +33,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
propertyName,
|
propertyName,
|
||||||
new CachedDataAnnotationsMetadataAttributes(attributes))
|
new CachedDataAnnotationsMetadataAttributes(attributes))
|
||||||
{
|
{
|
||||||
|
Marker = attributes.OfType<IBinderMarker>().FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool ComputeConvertEmptyStringToNull()
|
protected override bool ComputeConvertEmptyStringToNull()
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
CacheKey = prototype.CacheKey;
|
CacheKey = prototype.CacheKey;
|
||||||
PrototypeCache = prototype.PrototypeCache;
|
PrototypeCache = prototype.PrototypeCache;
|
||||||
|
Marker = prototype.Marker;
|
||||||
_isComplexType = prototype.IsComplexType;
|
_isComplexType = prototype.IsComplexType;
|
||||||
_isComplexTypeComputed = true;
|
_isComplexTypeComputed = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,20 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
public interface IModelMetadataProvider
|
public interface IModelMetadataProvider
|
||||||
{
|
{
|
||||||
IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType);
|
IEnumerable<ModelMetadata> GetMetadataForProperties(object container, [NotNull] Type containerType);
|
||||||
|
|
||||||
ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName);
|
ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, [NotNull] Type containerType, [NotNull] string propertyName);
|
||||||
|
|
||||||
ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType);
|
ModelMetadata GetMetadataForType(Func<object> modelAccessor, [NotNull] Type modelType);
|
||||||
|
|
||||||
|
IEnumerable<ModelMetadata> GetMetadataForParameters([NotNull] MethodInfo methodInfo);
|
||||||
|
|
||||||
|
ModelMetadata GetMetadataForParameter(Func<object> modelAccessor, [NotNull] MethodInfo methodInfo, [NotNull] string parameterName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
_isRequired = !modelType.AllowsNullValue();
|
_isRequired = !modelType.AllowsNullValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a binder marker for this model.
|
||||||
|
/// </summary>
|
||||||
|
public IBinderMarker Marker { get; set; }
|
||||||
|
|
||||||
public Type ContainerType
|
public Type ContainerType
|
||||||
{
|
{
|
||||||
get { return _containerType; }
|
get { return _containerType; }
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,22 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("Common_PropertyNotFound"), p0, p1);
|
return string.Format(CultureInfo.CurrentCulture, GetString("Common_PropertyNotFound"), p0, p1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parameter '{0}' could not be found.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Common_ParameterNotFound
|
||||||
|
{
|
||||||
|
get { return GetString("Common_ParameterNotFound"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parameter '{0}' could not be found.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatCommon_ParameterNotFound(object p0)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("Common_ParameterNotFound"), p0);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The type '{0}' must have a public constructor which accepts a single parameter of type '{1}'.
|
/// The type '{0}' must have a public constructor which accepts a single parameter of type '{1}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,9 @@
|
||||||
<data name="Common_PropertyNotFound" xml:space="preserve">
|
<data name="Common_PropertyNotFound" xml:space="preserve">
|
||||||
<value>The property {0}.{1} could not be found.</value>
|
<value>The property {0}.{1} could not be found.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Common_ParameterNotFound" xml:space="preserve">
|
||||||
|
<value>The parameter '{0}' could not be found.</value>
|
||||||
|
</data>
|
||||||
<data name="DataAnnotationsModelValidatorProvider_ConstructorRequirements" xml:space="preserve">
|
<data name="DataAnnotationsModelValidatorProvider_ConstructorRequirements" xml:space="preserve">
|
||||||
<value>The type '{0}' must have a public constructor which accepts a single parameter of type '{1}'.</value>
|
<value>The type '{0}' must have a public constructor which accepts a single parameter of type '{1}'.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
public class CompositeValueProvider : Collection<IValueProvider>, IEnumerableValueProvider
|
public class CompositeValueProvider : Collection<IValueProvider>, IEnumerableValueProvider, IMarkerAwareValueProvider
|
||||||
{
|
{
|
||||||
public CompositeValueProvider()
|
public CompositeValueProvider()
|
||||||
: base()
|
: base()
|
||||||
|
|
@ -79,5 +79,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
base.SetItem(index, item);
|
base.SetItem(index, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IValueProvider Filter(IValueBinderMarker valueBinderMarker)
|
||||||
|
{
|
||||||
|
var filteredValueProviders = new List<IValueProvider>();
|
||||||
|
foreach (var valueProvider in this.OfType<IMarkerAwareValueProvider>())
|
||||||
|
{
|
||||||
|
var result = valueProvider.Filter(valueBinderMarker);
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
filteredValueProviders.Add(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CompositeValueProvider(filteredValueProviders);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
public class DictionaryBasedValueProvider : IValueProvider
|
public class DictionaryBasedValueProvider<TBinderMarker> : MakerAwareValueProvider<TBinderMarker>
|
||||||
|
where TBinderMarker : IValueBinderMarker
|
||||||
{
|
{
|
||||||
private readonly IDictionary<string, object> _values;
|
private readonly IDictionary<string, object> _values;
|
||||||
|
|
||||||
|
|
@ -16,12 +17,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
_values = values;
|
_values = values;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> ContainsPrefixAsync(string key)
|
public override Task<bool> ContainsPrefixAsync(string key)
|
||||||
{
|
{
|
||||||
return Task.FromResult(_values.ContainsKey(key));
|
return Task.FromResult(_values.ContainsKey(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<ValueProviderResult> GetValueAsync([NotNull] string key)
|
public override Task<ValueProviderResult> GetValueAsync([NotNull] string key)
|
||||||
{
|
{
|
||||||
object value;
|
object value;
|
||||||
ValueProviderResult result;
|
ValueProviderResult result;
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
if (IsSupportedContentType(request))
|
if (IsSupportedContentType(request))
|
||||||
{
|
{
|
||||||
var culture = GetCultureInfo(request);
|
var culture = GetCultureInfo(request);
|
||||||
return new ReadableStringCollectionValueProvider(() => request.GetFormAsync(), culture);
|
return new ReadableStringCollectionValueProvider<IFormDataMarker>(() => request.GetFormAsync(), culture);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A value provider which is aware of <see cref="IValueBinderMarker"/>.
|
||||||
|
/// </summary>
|
||||||
|
public interface IMarkerAwareValueProvider : IValueProvider
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Filters the value provider based on <paramref name="valueBinderMarker"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="valueBinderMarker">The <see cref="IValueBinderMarker"/> associated with a model.</param>
|
||||||
|
/// <returns>The filtered value provider.</returns>
|
||||||
|
IValueProvider Filter([NotNull] IValueBinderMarker valueBinderMarker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// 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.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="IMarkerAwareValueProvider"/> value provider which can filter
|
||||||
|
/// based on <see cref="IValueBinderMarker"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TBinderMarker">Represents a type implementing <see cref="IValueBinderMarker"/></typeparam>
|
||||||
|
public abstract class MakerAwareValueProvider<TBinderMarker> : IMarkerAwareValueProvider
|
||||||
|
where TBinderMarker : IValueBinderMarker
|
||||||
|
{
|
||||||
|
public abstract Task<bool> ContainsPrefixAsync(string prefix);
|
||||||
|
|
||||||
|
public abstract Task<ValueProviderResult> GetValueAsync(string key);
|
||||||
|
|
||||||
|
public virtual IValueProvider Filter(IValueBinderMarker valueBinderMarker)
|
||||||
|
{
|
||||||
|
if (valueBinderMarker is TBinderMarker)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,12 +18,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
if (!storage.TryGetValue(_cacheKey, out value))
|
if (!storage.TryGetValue(_cacheKey, out value))
|
||||||
{
|
{
|
||||||
var queryCollection = context.HttpContext.Request.Query;
|
var queryCollection = context.HttpContext.Request.Query;
|
||||||
provider = new ReadableStringCollectionValueProvider(queryCollection, CultureInfo.InvariantCulture);
|
provider = new ReadableStringCollectionValueProvider<IQueryBinderMarker>(queryCollection,
|
||||||
|
CultureInfo.InvariantCulture);
|
||||||
storage[_cacheKey] = provider;
|
storage[_cacheKey] = provider;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
provider = (ReadableStringCollectionValueProvider)value;
|
provider = (ReadableStringCollectionValueProvider<IQueryBinderMarker>)value;
|
||||||
}
|
}
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,14 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
|
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
public class ReadableStringCollectionValueProvider : IEnumerableValueProvider
|
public class ReadableStringCollectionValueProvider<TBinderMarker> : MakerAwareValueProvider<TBinderMarker>, IEnumerableValueProvider
|
||||||
|
where TBinderMarker : IValueBinderMarker
|
||||||
{
|
{
|
||||||
private readonly CultureInfo _culture;
|
private readonly CultureInfo _culture;
|
||||||
private PrefixContainer _prefixContainer;
|
private PrefixContainer _prefixContainer;
|
||||||
|
|
@ -45,7 +45,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task<bool> ContainsPrefixAsync(string prefix)
|
public override async Task<bool> ContainsPrefixAsync(string prefix)
|
||||||
{
|
{
|
||||||
var prefixContainer = await GetPrefixContainerAsync();
|
var prefixContainer = await GetPrefixContainerAsync();
|
||||||
return prefixContainer.ContainsPrefix(prefix);
|
return prefixContainer.ContainsPrefix(prefix);
|
||||||
|
|
@ -57,7 +57,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
return prefixContainer.GetKeysFromPrefix(prefix);
|
return prefixContainer.GetKeysFromPrefix(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task<ValueProviderResult> GetValueAsync([NotNull] string key)
|
public override async Task<ValueProviderResult> GetValueAsync([NotNull] string key)
|
||||||
{
|
{
|
||||||
var collection = await GetValueCollectionAsync();
|
var collection = await GetValueCollectionAsync();
|
||||||
var values = collection.GetValues(key);
|
var values = collection.GetValues(key);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
public IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context)
|
public IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context)
|
||||||
{
|
{
|
||||||
return new DictionaryBasedValueProvider(context.RouteValues);
|
return new DictionaryBasedValueProvider<IRouteDataMarker>(context.RouteValues);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
options.ViewEngines.Add(typeof(RazorViewEngine));
|
options.ViewEngines.Add(typeof(RazorViewEngine));
|
||||||
|
|
||||||
// Set up ModelBinding
|
// Set up ModelBinding
|
||||||
|
options.ModelBinders.Add(typeof(BodyModelBinder));
|
||||||
options.ModelBinders.Add(new TypeConverterModelBinder());
|
options.ModelBinders.Add(new TypeConverterModelBinder());
|
||||||
options.ModelBinders.Add(new TypeMatchModelBinder());
|
options.ModelBinders.Add(new TypeMatchModelBinder());
|
||||||
options.ModelBinders.Add(new CancellationTokenModelBinder());
|
options.ModelBinders.Add(new CancellationTokenModelBinder());
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
yield return describe.Singleton<IActionSelectorDecisionTreeProvider, ActionSelectorDecisionTreeProvider>();
|
yield return describe.Singleton<IActionSelectorDecisionTreeProvider, ActionSelectorDecisionTreeProvider>();
|
||||||
yield return describe.Scoped<IActionSelector, DefaultActionSelector>();
|
yield return describe.Scoped<IActionSelector, DefaultActionSelector>();
|
||||||
|
yield return describe.Transient<IControllerActionArgumentBinder, DefaultControllerActionArgumentBinder>();
|
||||||
|
|
||||||
yield return describe.Transient<INestedProvider<ActionDescriptorProviderContext>,
|
yield return describe.Transient<INestedProvider<ActionDescriptorProviderContext>,
|
||||||
ControllerActionDescriptorProvider>();
|
ControllerActionDescriptorProvider>();
|
||||||
|
|
@ -83,8 +84,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
yield return describe.Scoped<ICompositeModelBinder, CompositeModelBinder>();
|
yield return describe.Scoped<ICompositeModelBinder, CompositeModelBinder>();
|
||||||
yield return describe.Transient<IValueProviderFactoryProvider, DefaultValueProviderFactoryProvider>();
|
yield return describe.Transient<IValueProviderFactoryProvider, DefaultValueProviderFactoryProvider>();
|
||||||
yield return describe.Scoped<ICompositeValueProviderFactory, CompositeValueProviderFactory>();
|
yield return describe.Scoped<ICompositeValueProviderFactory, CompositeValueProviderFactory>();
|
||||||
|
yield return describe.Transient<IValueProviderFactoryProvider, DefaultValueProviderFactoryProvider>();
|
||||||
yield return describe.Transient<IOutputFormattersProvider, DefaultOutputFormattersProvider>();
|
yield return describe.Transient<IOutputFormattersProvider, DefaultOutputFormattersProvider>();
|
||||||
|
|
||||||
yield return describe.Instance<JsonOutputFormatter>(new JsonOutputFormatter());
|
yield return describe.Instance<JsonOutputFormatter>(new JsonOutputFormatter());
|
||||||
|
|
||||||
yield return describe.Transient<IModelValidatorProviderProvider, DefaultModelValidatorProviderProvider>();
|
yield return describe.Transient<IModelValidatorProviderProvider, DefaultModelValidatorProviderProvider>();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,140 @@
|
||||||
|
// 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.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNet.PipelineCore;
|
||||||
|
using Microsoft.AspNet.Routing;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
public class BodyModelBinderTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task BindModel_CallsValidationAndSelectedInputFormatterOnce()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mockValidator = new Mock<IBodyModelValidator>();
|
||||||
|
mockValidator.Setup(o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<string>()))
|
||||||
|
.Returns(true)
|
||||||
|
.Verifiable();
|
||||||
|
var mockInputFormatter = new Mock<IInputFormatter>();
|
||||||
|
mockInputFormatter.Setup(o => o.ReadAsync(It.IsAny<InputFormatterContext>()))
|
||||||
|
.Returns(Task.FromResult<object>(new Person()))
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var bindingContext = GetBindingContext(typeof(Person), inputFormatter: mockInputFormatter.Object);
|
||||||
|
bindingContext.ModelMetadata.Marker = Mock.Of<IBodyBinderMarker>();
|
||||||
|
|
||||||
|
var binder = GetBodyBinder(mockInputFormatter.Object, mockValidator.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var binderResult = await binder.BindModelAsync(bindingContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
mockValidator.Verify(v => v.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<string>()), Times.Once);
|
||||||
|
mockInputFormatter.Verify(v => v.ReadAsync(It.IsAny<InputFormatterContext>()), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BindModel_NoInputFormatterFound_SetsModelStateError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var bindingContext = GetBindingContext(typeof(Person), inputFormatter: null);
|
||||||
|
bindingContext.ModelMetadata.Marker = Mock.Of<IBodyBinderMarker>();
|
||||||
|
var binder = bindingContext.ModelBinder;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var binderResult = await binder.BindModelAsync(bindingContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
|
||||||
|
// Returns true because it understands the marker.
|
||||||
|
Assert.True(binderResult);
|
||||||
|
Assert.Null(bindingContext.Model);
|
||||||
|
Assert.True(bindingContext.ModelState.ContainsKey("someName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(true)]
|
||||||
|
[InlineData(false)]
|
||||||
|
public async Task BindModel_IsMarkerAware(bool useBodyMarker)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var bindingContext = GetBindingContext(typeof(Person), inputFormatter: null);
|
||||||
|
bindingContext.ModelMetadata.Marker = useBodyMarker ? Mock.Of<IBodyBinderMarker>() :
|
||||||
|
Mock.Of<IBinderMarker>();
|
||||||
|
var binder = bindingContext.ModelBinder;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var binderResult = await binder.BindModelAsync(bindingContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(useBodyMarker, binderResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ModelBindingContext GetBindingContext(Type modelType, IInputFormatter inputFormatter)
|
||||||
|
{
|
||||||
|
var metadataProvider = new EmptyModelMetadataProvider();
|
||||||
|
ModelBindingContext bindingContext = new ModelBindingContext
|
||||||
|
{
|
||||||
|
ModelMetadata = metadataProvider.GetMetadataForType(null, modelType),
|
||||||
|
ModelName = "someName",
|
||||||
|
ValueProvider = Mock.Of<IValueProvider>(),
|
||||||
|
ModelBinder = GetBodyBinder(inputFormatter, null),
|
||||||
|
MetadataProvider = metadataProvider,
|
||||||
|
HttpContext = new DefaultHttpContext(),
|
||||||
|
ModelState = new ModelStateDictionary()
|
||||||
|
};
|
||||||
|
|
||||||
|
return bindingContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BodyModelBinder GetBodyBinder(IInputFormatter inputFormatter, IBodyModelValidator validator)
|
||||||
|
{
|
||||||
|
var actionContext = CreateActionContext(new DefaultHttpContext());
|
||||||
|
var inputFormatterSelector = new Mock<IInputFormatterSelector>();
|
||||||
|
inputFormatterSelector.Setup(o => o.SelectFormatter(It.IsAny<InputFormatterContext>())).Returns(inputFormatter);
|
||||||
|
|
||||||
|
if (validator == null)
|
||||||
|
{
|
||||||
|
var mockValidator = new Mock<IBodyModelValidator>();
|
||||||
|
mockValidator.Setup(o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<string>()))
|
||||||
|
.Returns(true)
|
||||||
|
.Verifiable();
|
||||||
|
validator = mockValidator.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
var binder = new BodyModelBinder(actionContext,
|
||||||
|
inputFormatterSelector.Object,
|
||||||
|
validator);
|
||||||
|
return binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IContextAccessor<ActionContext> CreateActionContext(HttpContext context)
|
||||||
|
{
|
||||||
|
return CreateActionContext(context, (new Mock<IRouter>()).Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IContextAccessor<ActionContext> CreateActionContext(HttpContext context, IRouter router)
|
||||||
|
{
|
||||||
|
var routeData = new RouteData();
|
||||||
|
routeData.Values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
routeData.Routers.Add(router);
|
||||||
|
|
||||||
|
var actionContext = new ActionContext(context,
|
||||||
|
routeData,
|
||||||
|
new ActionDescriptor());
|
||||||
|
var contextAccessor = new Mock<IContextAccessor<ActionContext>>();
|
||||||
|
contextAccessor.SetupGet(c => c.Value)
|
||||||
|
.Returns(actionContext);
|
||||||
|
return contextAccessor.Object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1339,187 +1339,16 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
var invoker = new TestControllerActionInvoker(
|
var invoker = new TestControllerActionInvoker(
|
||||||
actionContext,
|
actionContext,
|
||||||
actionBindingContextProvider.Object,
|
|
||||||
filterProvider.Object,
|
filterProvider.Object,
|
||||||
controllerFactory,
|
controllerFactory,
|
||||||
actionDescriptor,
|
actionDescriptor,
|
||||||
inputFormattersProvider.Object,
|
inputFormattersProvider.Object,
|
||||||
new DefaultBodyModelValidator());
|
Mock.Of<IControllerActionArgumentBinder>());
|
||||||
|
|
||||||
return invoker;
|
return invoker;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetActionArguments_DoesNotAddActionArgumentsToModelStateDictionary_IfBinderReturnsFalse()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
Func<object, int> method = x => 1;
|
|
||||||
var actionDescriptor = new ControllerActionDescriptor
|
|
||||||
{
|
|
||||||
MethodInfo = method.Method,
|
|
||||||
Parameters = new List<ParameterDescriptor>
|
|
||||||
{
|
|
||||||
new ParameterDescriptor
|
|
||||||
{
|
|
||||||
Name = "foo",
|
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("foo", typeof(object))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var binder = new Mock<IModelBinder>();
|
|
||||||
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
|
||||||
.Returns(Task.FromResult(result: false));
|
|
||||||
var actionContext = new ActionContext(new RouteContext(Mock.Of<HttpContext>()),
|
|
||||||
actionDescriptor);
|
|
||||||
var bindingContext = new ActionBindingContext(actionContext,
|
|
||||||
Mock.Of<IModelMetadataProvider>(),
|
|
||||||
binder.Object,
|
|
||||||
Mock.Of<IValueProvider>(),
|
|
||||||
Mock.Of<IInputFormatterSelector>(),
|
|
||||||
Mock.Of<IModelValidatorProvider>());
|
|
||||||
|
|
||||||
var actionBindingContextProvider = new Mock<IActionBindingContextProvider>();
|
|
||||||
actionBindingContextProvider.Setup(p => p.GetActionBindingContextAsync(It.IsAny<ActionContext>()))
|
|
||||||
.Returns(Task.FromResult(bindingContext));
|
|
||||||
var inputFormattersProvider = new Mock<IInputFormattersProvider>();
|
|
||||||
inputFormattersProvider.SetupGet(o => o.InputFormatters)
|
|
||||||
.Returns(new List<IInputFormatter>());
|
|
||||||
var invoker = new ControllerActionInvoker(actionContext,
|
|
||||||
actionBindingContextProvider.Object,
|
|
||||||
Mock.Of<INestedProviderManager<FilterProviderContext>>(),
|
|
||||||
Mock.Of<IControllerFactory>(),
|
|
||||||
actionDescriptor,
|
|
||||||
inputFormattersProvider.Object,
|
|
||||||
new DefaultBodyModelValidator());
|
|
||||||
|
|
||||||
var modelStateDictionary = new ModelStateDictionary();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await invoker.GetActionArguments(modelStateDictionary);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Empty(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetActionArguments_AddsActionArgumentsToModelStateDictionary_IfBinderReturnsTrue()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
Func<object, int> method = x => 1;
|
|
||||||
var actionDescriptor = new ControllerActionDescriptor
|
|
||||||
{
|
|
||||||
MethodInfo = method.Method,
|
|
||||||
Parameters = new List<ParameterDescriptor>
|
|
||||||
{
|
|
||||||
new ParameterDescriptor
|
|
||||||
{
|
|
||||||
Name = "foo",
|
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("foo", typeof(object))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var value = "Hello world";
|
|
||||||
var binder = new Mock<IModelBinder>();
|
|
||||||
var metadataProvider = new EmptyModelMetadataProvider();
|
|
||||||
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
|
||||||
.Callback((ModelBindingContext context) =>
|
|
||||||
{
|
|
||||||
context.ModelMetadata = metadataProvider.GetMetadataForType(modelAccessor: null,
|
|
||||||
modelType: typeof(string));
|
|
||||||
context.Model = value;
|
|
||||||
})
|
|
||||||
.Returns(Task.FromResult(result: true));
|
|
||||||
var actionContext = new ActionContext(new RouteContext(Mock.Of<HttpContext>()),
|
|
||||||
actionDescriptor);
|
|
||||||
var bindingContext = new ActionBindingContext(actionContext,
|
|
||||||
Mock.Of<IModelMetadataProvider>(),
|
|
||||||
binder.Object,
|
|
||||||
Mock.Of<IValueProvider>(),
|
|
||||||
Mock.Of<IInputFormatterSelector>(),
|
|
||||||
Mock.Of<IModelValidatorProvider>());
|
|
||||||
|
|
||||||
var actionBindingContextProvider = new Mock<IActionBindingContextProvider>();
|
|
||||||
actionBindingContextProvider.Setup(p => p.GetActionBindingContextAsync(It.IsAny<ActionContext>()))
|
|
||||||
.Returns(Task.FromResult(bindingContext));
|
|
||||||
var inputFormattersProvider = new Mock<IInputFormattersProvider>();
|
|
||||||
inputFormattersProvider.SetupGet(o => o.InputFormatters)
|
|
||||||
.Returns(new List<IInputFormatter>());
|
|
||||||
var invoker = new ControllerActionInvoker(actionContext,
|
|
||||||
actionBindingContextProvider.Object,
|
|
||||||
Mock.Of<INestedProviderManager<FilterProviderContext>>(),
|
|
||||||
Mock.Of<IControllerFactory>(),
|
|
||||||
actionDescriptor,
|
|
||||||
inputFormattersProvider.Object,
|
|
||||||
new DefaultBodyModelValidator());
|
|
||||||
|
|
||||||
var modelStateDictionary = new ModelStateDictionary();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await invoker.GetActionArguments(modelStateDictionary);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(1, result.Count);
|
|
||||||
Assert.Equal(value, result["foo"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetActionArguments_NoInputFormatterFound_SetsModelStateError()
|
|
||||||
{
|
|
||||||
var actionDescriptor = new ControllerActionDescriptor
|
|
||||||
{
|
|
||||||
MethodInfo = typeof(TestController).GetTypeInfo().GetMethod("ActionMethodWithDefaultValues"),
|
|
||||||
Parameters = new List<ParameterDescriptor>
|
|
||||||
{
|
|
||||||
new ParameterDescriptor
|
|
||||||
{
|
|
||||||
Name = "bodyParam",
|
|
||||||
BodyParameterInfo = new BodyParameterInfo(typeof(Person))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
FilterDescriptors = new List<FilterDescriptor>()
|
|
||||||
};
|
|
||||||
|
|
||||||
var context = new DefaultHttpContext();
|
|
||||||
var routeContext = new RouteContext(context);
|
|
||||||
var actionContext = new ActionContext(routeContext,
|
|
||||||
actionDescriptor);
|
|
||||||
var bindingContext = new ActionBindingContext(actionContext,
|
|
||||||
Mock.Of<IModelMetadataProvider>(),
|
|
||||||
Mock.Of<IModelBinder>(),
|
|
||||||
Mock.Of<IValueProvider>(),
|
|
||||||
Mock.Of<IInputFormatterSelector>(),
|
|
||||||
Mock.Of<IModelValidatorProvider>());
|
|
||||||
|
|
||||||
var actionBindingContextProvider = new Mock<IActionBindingContextProvider>();
|
|
||||||
actionBindingContextProvider.Setup(p => p.GetActionBindingContextAsync(It.IsAny<ActionContext>()))
|
|
||||||
.Returns(Task.FromResult(bindingContext));
|
|
||||||
var controllerFactory = new Mock<IControllerFactory>();
|
|
||||||
controllerFactory.Setup(c => c.CreateController(It.IsAny<ActionContext>()))
|
|
||||||
.Returns(new TestController());
|
|
||||||
var inputFormattersProvider = new Mock<IInputFormattersProvider>();
|
|
||||||
inputFormattersProvider.SetupGet(o => o.InputFormatters)
|
|
||||||
.Returns(new List<IInputFormatter>());
|
|
||||||
var invoker = new ControllerActionInvoker(actionContext,
|
|
||||||
actionBindingContextProvider.Object,
|
|
||||||
Mock.Of<INestedProviderManager<FilterProviderContext>>(),
|
|
||||||
controllerFactory.Object,
|
|
||||||
actionDescriptor,
|
|
||||||
inputFormattersProvider.Object,
|
|
||||||
new DefaultBodyModelValidator());
|
|
||||||
|
|
||||||
|
|
||||||
var modelStateDictionary = new ModelStateDictionary();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await invoker.GetActionArguments(modelStateDictionary);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Empty(result);
|
|
||||||
Assert.DoesNotContain("bodyParam", result.Keys);
|
|
||||||
Assert.False(actionContext.ModelState.IsValid);
|
|
||||||
Assert.Equal("Unsupported content type '" + context.Request.ContentType + "'.",
|
|
||||||
actionContext.ModelState["bodyParam"].Errors[0].ErrorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Invoke_UsesDefaultValuesIfNotBound()
|
public async Task Invoke_UsesDefaultValuesIfNotBound()
|
||||||
|
|
@ -1568,12 +1397,13 @@ namespace Microsoft.AspNet.Mvc
|
||||||
inputFormattersProvider.SetupGet(o => o.InputFormatters)
|
inputFormattersProvider.SetupGet(o => o.InputFormatters)
|
||||||
.Returns(new List<IInputFormatter>());
|
.Returns(new List<IInputFormatter>());
|
||||||
var invoker = new ControllerActionInvoker(actionContext,
|
var invoker = new ControllerActionInvoker(actionContext,
|
||||||
actionBindingContextProvider.Object,
|
|
||||||
Mock.Of<INestedProviderManager<FilterProviderContext>>(),
|
Mock.Of<INestedProviderManager<FilterProviderContext>>(),
|
||||||
controllerFactory.Object,
|
controllerFactory.Object,
|
||||||
actionDescriptor,
|
actionDescriptor,
|
||||||
inputFormattersProvider.Object,
|
inputFormattersProvider.Object,
|
||||||
new DefaultBodyModelValidator());
|
new DefaultControllerActionArgumentBinder(
|
||||||
|
actionBindingContextProvider.Object,
|
||||||
|
new DefaultBodyModelValidator()));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await invoker.InvokeAsync();
|
await invoker.InvokeAsync();
|
||||||
|
|
@ -1629,19 +1459,17 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
public TestControllerActionInvoker(
|
public TestControllerActionInvoker(
|
||||||
ActionContext actionContext,
|
ActionContext actionContext,
|
||||||
IActionBindingContextProvider bindingContextProvider,
|
|
||||||
INestedProviderManager<FilterProviderContext> filterProvider,
|
INestedProviderManager<FilterProviderContext> filterProvider,
|
||||||
Mock<IControllerFactory> controllerFactoryMock,
|
Mock<IControllerFactory> controllerFactoryMock,
|
||||||
ControllerActionDescriptor descriptor,
|
ControllerActionDescriptor descriptor,
|
||||||
IInputFormattersProvider inputFormattersProvider,
|
IInputFormattersProvider inputFormattersProvider,
|
||||||
IBodyModelValidator bodyModelValidator) :
|
IControllerActionArgumentBinder controllerActionArgumentBinder) :
|
||||||
base(actionContext,
|
base(actionContext,
|
||||||
bindingContextProvider,
|
|
||||||
filterProvider,
|
filterProvider,
|
||||||
controllerFactoryMock.Object,
|
controllerFactoryMock.Object,
|
||||||
descriptor,
|
descriptor,
|
||||||
inputFormattersProvider,
|
inputFormattersProvider,
|
||||||
bodyModelValidator)
|
controllerActionArgumentBinder)
|
||||||
{
|
{
|
||||||
_factoryMock = controllerFactoryMock;
|
_factoryMock = controllerFactoryMock;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,112 +19,6 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
public class InputObjectBindingTests
|
public class InputObjectBindingTests
|
||||||
{
|
{
|
||||||
[Fact]
|
|
||||||
public async Task GetArguments_UsingInputFormatter_DeserializesWithoutErrors_WhenValidationAttributesAreAbsent()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var sampleName = "SampleName";
|
|
||||||
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
|
|
||||||
"<Person><Name>" + sampleName + "</Name></Person>";
|
|
||||||
var modelStateDictionary = new ModelStateDictionary();
|
|
||||||
var invoker = GetControllerActionInvoker(
|
|
||||||
input, typeof(Person), new XmlSerializerInputFormatter(), "application/xml");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await invoker.GetActionArguments(modelStateDictionary);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.True(modelStateDictionary.IsValid);
|
|
||||||
Assert.Equal(0, modelStateDictionary.ErrorCount);
|
|
||||||
var model = result["foo"] as Person;
|
|
||||||
Assert.Equal(sampleName, model.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetArguments_UsingInputFormatter_DeserializesWithValidationError()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var sampleName = "SampleName";
|
|
||||||
var sampleUserName = "No5";
|
|
||||||
var input = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
|
|
||||||
"<User><Name>" + sampleName + "</Name><UserName>" + sampleUserName + "</UserName></User>";
|
|
||||||
var modelStateDictionary = new ModelStateDictionary();
|
|
||||||
var invoker = GetControllerActionInvoker(input, typeof(User), new XmlSerializerInputFormatter(), "application/xml");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await invoker.GetActionArguments(modelStateDictionary);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.False(modelStateDictionary.IsValid);
|
|
||||||
Assert.Equal(1, modelStateDictionary.ErrorCount);
|
|
||||||
Assert.Equal(
|
|
||||||
ValidationAttributeUtil.GetMinLengthErrorMessage(5, "UserName"),
|
|
||||||
Assert.Single(Assert.Single(modelStateDictionary.Values).Errors).ErrorMessage);
|
|
||||||
var model = result["foo"] as User;
|
|
||||||
Assert.Equal(sampleName, model.Name);
|
|
||||||
Assert.Equal(sampleUserName, model.UserName);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetArguments_UsingInputFormatter_DeserializesArrays()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var sampleFirstUser = "FirstUser";
|
|
||||||
var sampleFirstUserName = "fuser";
|
|
||||||
var sampleSecondUser = "SecondUser";
|
|
||||||
var sampleSecondUserName = "suser";
|
|
||||||
var input = "{'Users': [{Name : '" + sampleFirstUser + "', UserName: '" + sampleFirstUserName +
|
|
||||||
"'}, {Name: '" + sampleSecondUser + "', UserName: '" + sampleSecondUserName + "'}]}";
|
|
||||||
var modelStateDictionary = new ModelStateDictionary();
|
|
||||||
var invoker = GetControllerActionInvoker(input, typeof(Customers), new JsonInputFormatter(), "application/xml");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await invoker.GetActionArguments(modelStateDictionary);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.True(modelStateDictionary.IsValid);
|
|
||||||
Assert.Equal(0, modelStateDictionary.ErrorCount);
|
|
||||||
var model = result["foo"] as Customers;
|
|
||||||
Assert.Equal(2, model.Users.Count);
|
|
||||||
Assert.Equal(sampleFirstUser, model.Users[0].Name);
|
|
||||||
Assert.Equal(sampleFirstUserName, model.Users[0].UserName);
|
|
||||||
Assert.Equal(sampleSecondUser, model.Users[1].Name);
|
|
||||||
Assert.Equal(sampleSecondUserName, model.Users[1].UserName);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task GetArguments_UsingInputFormatter_DeserializesArrays_WithErrors()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var sampleFirstUser = "FirstUser";
|
|
||||||
var sampleFirstUserName = "fusr";
|
|
||||||
var sampleSecondUser = "SecondUser";
|
|
||||||
var sampleSecondUserName = "susr";
|
|
||||||
var input = "{'Users': [{Name : '" + sampleFirstUser + "', UserName: '" + sampleFirstUserName +
|
|
||||||
"'}, {Name: '" + sampleSecondUser + "', UserName: '" + sampleSecondUserName + "'}]}";
|
|
||||||
var modelStateDictionary = new ModelStateDictionary();
|
|
||||||
var invoker = GetControllerActionInvoker(input, typeof(Customers), new JsonInputFormatter(), "application/xml");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await invoker.GetActionArguments(modelStateDictionary);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.False(modelStateDictionary.IsValid);
|
|
||||||
Assert.Equal(2, modelStateDictionary.ErrorCount);
|
|
||||||
var model = result["foo"] as Customers;
|
|
||||||
Assert.Equal(
|
|
||||||
ValidationAttributeUtil.GetMinLengthErrorMessage(5, "UserName"),
|
|
||||||
modelStateDictionary["foo.Users[0].UserName"].Errors[0].ErrorMessage);
|
|
||||||
Assert.Equal(
|
|
||||||
ValidationAttributeUtil.GetMinLengthErrorMessage(5, "UserName"),
|
|
||||||
modelStateDictionary["foo.Users[1].UserName"].Errors[0].ErrorMessage);
|
|
||||||
Assert.Equal(2, model.Users.Count);
|
|
||||||
Assert.Equal(sampleFirstUser, model.Users[0].Name);
|
|
||||||
Assert.Equal(sampleFirstUserName, model.Users[0].UserName);
|
|
||||||
Assert.Equal(sampleSecondUser, model.Users[1].Name);
|
|
||||||
Assert.Equal(sampleSecondUserName, model.Users[1].UserName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ControllerActionInvoker GetControllerActionInvoker(
|
private static ControllerActionInvoker GetControllerActionInvoker(
|
||||||
string input, Type parameterType, IInputFormatter selectedFormatter, string contentType)
|
string input, Type parameterType, IInputFormatter selectedFormatter, string contentType)
|
||||||
{
|
{
|
||||||
|
|
@ -174,12 +68,11 @@ namespace Microsoft.AspNet.Mvc
|
||||||
inputFormattersProvider.SetupGet(o => o.InputFormatters)
|
inputFormattersProvider.SetupGet(o => o.InputFormatters)
|
||||||
.Returns(new List<IInputFormatter>());
|
.Returns(new List<IInputFormatter>());
|
||||||
return new ControllerActionInvoker(actionContext,
|
return new ControllerActionInvoker(actionContext,
|
||||||
actionBindingContextProvider.Object,
|
|
||||||
Mock.Of<INestedProviderManager<FilterProviderContext>>(),
|
Mock.Of<INestedProviderManager<FilterProviderContext>>(),
|
||||||
Mock.Of<IControllerFactory>(),
|
Mock.Of<IControllerFactory>(),
|
||||||
actionDescriptor,
|
actionDescriptor,
|
||||||
inputFormattersProvider.Object,
|
inputFormattersProvider.Object,
|
||||||
new DefaultBodyModelValidator());
|
Mock.Of<IControllerActionArgumentBinder>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ActionContext GetActionContext(byte[] contentBytes,
|
private static ActionContext GetActionContext(byte[] contentBytes,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,196 @@
|
||||||
|
// 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.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNet.Routing;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
|
{
|
||||||
|
public class ControllerActionArgumentBinderTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task Parameters_WithMultipleFromBody_Throw()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var actionDescriptor = new ControllerActionDescriptor
|
||||||
|
{
|
||||||
|
MethodInfo = typeof(TestController).GetMethod("ActionWithTwoBodyParam"),
|
||||||
|
Parameters = new List<ParameterDescriptor>
|
||||||
|
{
|
||||||
|
new ParameterDescriptor
|
||||||
|
{
|
||||||
|
Name = "bodyParam",
|
||||||
|
ParameterType = typeof(Person),
|
||||||
|
},
|
||||||
|
new ParameterDescriptor
|
||||||
|
{
|
||||||
|
Name = "bodyParam1",
|
||||||
|
ParameterType = typeof(Person),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var binder = new Mock<IModelBinder>();
|
||||||
|
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||||
|
var actionContext = new ActionContext(new RouteContext(Mock.Of<HttpContext>()),
|
||||||
|
actionDescriptor);
|
||||||
|
actionContext.Controller = Mock.Of<object>();
|
||||||
|
var bindingContext = new ActionBindingContext(actionContext,
|
||||||
|
metadataProvider,
|
||||||
|
Mock.Of<IModelBinder>(),
|
||||||
|
Mock.Of<IValueProvider>(),
|
||||||
|
Mock.Of<IInputFormatterSelector>(),
|
||||||
|
Mock.Of<IModelValidatorProvider>());
|
||||||
|
|
||||||
|
var actionBindingContextProvider = new Mock<IActionBindingContextProvider>();
|
||||||
|
actionBindingContextProvider.Setup(p => p.GetActionBindingContextAsync(It.IsAny<ActionContext>()))
|
||||||
|
.Returns(Task.FromResult(bindingContext));
|
||||||
|
|
||||||
|
var invoker = new DefaultControllerActionArgumentBinder(
|
||||||
|
actionBindingContextProvider.Object, Mock.Of<IBodyModelValidator>());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||||
|
() => invoker.GetActionArgumentsAsync(actionContext));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("More than one parameter is bound to the HTTP request's content.",
|
||||||
|
ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetActionArgumentsAsync_DoesNotAddActionArguments_IfBinderReturnsFalse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
Func<object, int> method = foo => 1;
|
||||||
|
var actionDescriptor = new ControllerActionDescriptor
|
||||||
|
{
|
||||||
|
MethodInfo = method.Method,
|
||||||
|
Parameters = new List<ParameterDescriptor>
|
||||||
|
{
|
||||||
|
new ParameterDescriptor
|
||||||
|
{
|
||||||
|
Name = "foo",
|
||||||
|
ParameterBindingInfo = new ParameterBindingInfo("foo", typeof(object))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var binder = new Mock<IModelBinder>();
|
||||||
|
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
|
.Returns(Task.FromResult(result: false));
|
||||||
|
var actionContext = new ActionContext(new RouteContext(Mock.Of<HttpContext>()),
|
||||||
|
actionDescriptor);
|
||||||
|
actionContext.Controller = Mock.Of<object>();
|
||||||
|
var bindingContext = new ActionBindingContext(actionContext,
|
||||||
|
Mock.Of<IModelMetadataProvider>(),
|
||||||
|
binder.Object,
|
||||||
|
Mock.Of<IValueProvider>(),
|
||||||
|
Mock.Of<IInputFormatterSelector>(),
|
||||||
|
Mock.Of<IModelValidatorProvider>());
|
||||||
|
var inputFormattersProvider = new Mock<IInputFormattersProvider>();
|
||||||
|
inputFormattersProvider.SetupGet(o => o.InputFormatters)
|
||||||
|
.Returns(new List<IInputFormatter>());
|
||||||
|
var actionBindingContextProvider = new Mock<IActionBindingContextProvider>();
|
||||||
|
actionBindingContextProvider.Setup(p => p.GetActionBindingContextAsync(It.IsAny<ActionContext>()))
|
||||||
|
.Returns(Task.FromResult(bindingContext));
|
||||||
|
|
||||||
|
var invoker = new DefaultControllerActionArgumentBinder(
|
||||||
|
actionBindingContextProvider.Object, Mock.Of<IBodyModelValidator>());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await invoker.GetActionArgumentsAsync(actionContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetActionArgumentsAsync_AddsActionArguments_IfBinderReturnsTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
Func<object, int> method = foo => 1;
|
||||||
|
var actionDescriptor = new ControllerActionDescriptor
|
||||||
|
{
|
||||||
|
MethodInfo = method.Method,
|
||||||
|
Parameters = new List<ParameterDescriptor>
|
||||||
|
{
|
||||||
|
new ParameterDescriptor
|
||||||
|
{
|
||||||
|
Name = "foo",
|
||||||
|
ParameterType = typeof(string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var value = "Hello world";
|
||||||
|
var binder = new Mock<IModelBinder>();
|
||||||
|
var metadataProvider = new EmptyModelMetadataProvider();
|
||||||
|
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
|
.Callback((ModelBindingContext context) =>
|
||||||
|
{
|
||||||
|
context.ModelMetadata = metadataProvider.GetMetadataForType(modelAccessor: null,
|
||||||
|
modelType: typeof(string));
|
||||||
|
context.Model = value;
|
||||||
|
})
|
||||||
|
.Returns(Task.FromResult(result: true));
|
||||||
|
var actionContext = new ActionContext(new RouteContext(Mock.Of<HttpContext>()),
|
||||||
|
actionDescriptor);
|
||||||
|
actionContext.Controller = Mock.Of<object>();
|
||||||
|
var bindingContext = new ActionBindingContext(actionContext,
|
||||||
|
metadataProvider,
|
||||||
|
binder.Object,
|
||||||
|
Mock.Of<IValueProvider>(),
|
||||||
|
Mock.Of<IInputFormatterSelector>(),
|
||||||
|
Mock.Of<IModelValidatorProvider>());
|
||||||
|
|
||||||
|
var actionBindingContextProvider = new Mock<IActionBindingContextProvider>();
|
||||||
|
actionBindingContextProvider.Setup(p => p.GetActionBindingContextAsync(It.IsAny<ActionContext>()))
|
||||||
|
.Returns(Task.FromResult(bindingContext));
|
||||||
|
|
||||||
|
var invoker = new DefaultControllerActionArgumentBinder(
|
||||||
|
actionBindingContextProvider.Object, Mock.Of<IBodyModelValidator>());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await invoker.GetActionArgumentsAsync(actionContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(1, result.Count);
|
||||||
|
Assert.Equal(value, result["foo"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestController
|
||||||
|
{
|
||||||
|
public string UnmarkedProperty { get; set; }
|
||||||
|
|
||||||
|
[NonValueBinderMarker]
|
||||||
|
public string NonValueBinderMarkedProperty { get; set; }
|
||||||
|
|
||||||
|
[ValueBinderMarker]
|
||||||
|
public string ValueBinderMarkedProperty { get; set; }
|
||||||
|
|
||||||
|
public Person ActionWithBodyParam([FromBody] Person bodyParam)
|
||||||
|
{
|
||||||
|
return bodyParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Person ActionWithTwoBodyParam([FromBody] Person bodyParam, [FromBody] Person bodyParam1)
|
||||||
|
{
|
||||||
|
return bodyParam;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class NonValueBinderMarkerAttribute : Attribute, IBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ValueBinderMarkerAttribute : Attribute, IValueBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -67,7 +67,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
{
|
{
|
||||||
{ "", null }
|
{ "", null }
|
||||||
};
|
};
|
||||||
var valueProvider = new DictionaryBasedValueProvider(values);
|
var valueProvider = new DictionaryBasedValueProvider<TestValueBinderMarker>(values);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await ModelBindingHelper.TryUpdateModelAsync(
|
var result = await ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
|
@ -105,7 +105,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
{ "", null },
|
{ "", null },
|
||||||
{ "MyProperty", "MyPropertyValue" }
|
{ "MyProperty", "MyPropertyValue" }
|
||||||
};
|
};
|
||||||
var valueProvider = new DictionaryBasedValueProvider(values);
|
var valueProvider = new DictionaryBasedValueProvider<TestValueBinderMarker>(values);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await ModelBindingHelper.TryUpdateModelAsync(
|
var result = await ModelBindingHelper.TryUpdateModelAsync(
|
||||||
|
|
@ -136,6 +136,10 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
[Required]
|
[Required]
|
||||||
public string MyProperty { get; set; }
|
public string MyProperty { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestValueBinderMarker : IValueBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.TestHost;
|
using Microsoft.AspNet.TestHost;
|
||||||
|
using ModelBindingWebSite;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -18,6 +20,93 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
private readonly IServiceProvider _services = TestHelper.CreateServices("ModelBindingWebSite");
|
private readonly IServiceProvider _services = TestHelper.CreateServices("ModelBindingWebSite");
|
||||||
private readonly Action<IApplicationBuilder> _app = new ModelBindingWebSite.Startup().Configure;
|
private readonly Action<IApplicationBuilder> _app = new ModelBindingWebSite.Startup().Configure;
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("RestrictValueProvidersUsingFromRoute", "valueFromRoute")]
|
||||||
|
[InlineData("RestrictValueProvidersUsingFromQuery", "valueFromQuery")]
|
||||||
|
[InlineData("RestrictValueProvidersUsingFromForm", "valueFromForm")]
|
||||||
|
public async Task CompositeModelBinder_Restricts_ValueProviders(string actionName, string expectedValue)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Provide all three values, it should bind based on the attribute on the action method.
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post,
|
||||||
|
string.Format("http://localhost/CompositeTest/{0}/valueFromRoute?param=valueFromQuery", actionName));
|
||||||
|
var nameValueCollection = new List<KeyValuePair<string, string>>
|
||||||
|
{
|
||||||
|
new KeyValuePair<string,string>("param", "valueFromForm"),
|
||||||
|
};
|
||||||
|
|
||||||
|
request.Content = new FormUrlEncodedContent(nameValueCollection);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal(expectedValue, await response.Content.ReadAsStringAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MultipleParametersMarkedWithFromBody_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||||
|
client.GetAsync("http://localhost/MultipleParametersFromBody/MultipleParametersFromBodyThrows"));
|
||||||
|
|
||||||
|
Assert.Equal("More than one parameter is bound to the HTTP request's content.",
|
||||||
|
ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ParametersWithNoMarkersUseTheAvailableValueProviders()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await
|
||||||
|
client.GetAsync("http://localhost/WithMarker" +
|
||||||
|
"/ParametersWithNoMarkersUseTheAvailableValueProviders" +
|
||||||
|
"?Name=somename&Age=12");
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var emp = JsonConvert.DeserializeObject<Employee>(
|
||||||
|
await response.Content.ReadAsStringAsync());
|
||||||
|
Assert.Null(emp.Department);
|
||||||
|
Assert.Equal("somename", emp.Name);
|
||||||
|
Assert.Equal(12, emp.Age);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ParametersAreAlwaysCreated()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await
|
||||||
|
client.GetAsync("http://localhost/WithoutMarker" +
|
||||||
|
"/GetPersonParameter" +
|
||||||
|
"?Name=somename&Age=12");
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var person = JsonConvert.DeserializeObject<Person>(
|
||||||
|
await response.Content.ReadAsStringAsync());
|
||||||
|
Assert.NotNull(person);
|
||||||
|
Assert.Equal("somename", person.Name);
|
||||||
|
Assert.Equal(12, person.Age);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ModelBindCancellationTokenParameteres()
|
public async Task ModelBindCancellationTokenParameteres()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -68,5 +68,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
|
|
||||||
return Task.FromResult(result);
|
return Task.FromResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsValidFor(Type valueProviderMarkerType)
|
||||||
|
{
|
||||||
|
// since this is not aware for any marker type, it should return false.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#if ASPNET50
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
|
{
|
||||||
|
public class CompositeValueProviderTests
|
||||||
|
{
|
||||||
|
public static IEnumerable<object[]> RegisteredAsMarkerClasses
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
yield return new object[] { new TestValueBinderMarker() };
|
||||||
|
yield return new object[] { new DerivedValueBinder() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RegisteredAsMarkerClasses))]
|
||||||
|
public void FilterReturnsItself_ForAnyClassRegisteredAsGenericParam(IValueBinderMarker binderMarker)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
var unrelatedMarker = new UnrelatedValueBinderMarker();
|
||||||
|
var valueProvider1 = GetMockValueProvider(binderMarker);
|
||||||
|
var valueProvider2 = GetMockValueProvider(unrelatedMarker);
|
||||||
|
var provider = new CompositeValueProvider(new List<IValueProvider>() { valueProvider1.Object, valueProvider2.Object });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = provider.Filter(binderMarker);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var valueProvider = Assert.IsType<CompositeValueProvider>(result);
|
||||||
|
var filteredProvider = Assert.Single(valueProvider);
|
||||||
|
|
||||||
|
// should not be unrelated marker.
|
||||||
|
Assert.Same(valueProvider1.Object, filteredProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mock<IMarkerAwareValueProvider> GetMockValueProvider(IValueBinderMarker marker)
|
||||||
|
{
|
||||||
|
var valueProvider = new Mock<IMarkerAwareValueProvider>();
|
||||||
|
valueProvider.Setup(o => o.Filter(marker))
|
||||||
|
.Returns(valueProvider.Object);
|
||||||
|
return valueProvider;
|
||||||
|
}
|
||||||
|
private class TestValueBinderMarker : IValueBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DerivedValueBinder : TestValueBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UnrelatedValueBinderMarker : IValueBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
{ "test-key", "value" }
|
{ "test-key", "value" }
|
||||||
};
|
};
|
||||||
var provider = new DictionaryBasedValueProvider(values);
|
var provider = new DictionaryBasedValueProvider<TestValueBinderMarker>(values);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await provider.GetValueAsync("not-test-key");
|
var result = await provider.GetValueAsync("not-test-key");
|
||||||
|
|
@ -35,7 +35,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
{ "test-key", "test-value" }
|
{ "test-key", "test-value" }
|
||||||
};
|
};
|
||||||
var provider = new DictionaryBasedValueProvider(values);
|
var provider = new DictionaryBasedValueProvider<TestValueBinderMarker>(values);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await provider.GetValueAsync("test-key");
|
var result = await provider.GetValueAsync("test-key");
|
||||||
|
|
@ -52,7 +52,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
{ "test-key", null }
|
{ "test-key", null }
|
||||||
};
|
};
|
||||||
var provider = new DictionaryBasedValueProvider(values);
|
var provider = new DictionaryBasedValueProvider<TestValueBinderMarker>(values);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await provider.GetValueAsync("test-key");
|
var result = await provider.GetValueAsync("test-key");
|
||||||
|
|
@ -71,7 +71,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
{ "test-key", "test-value" }
|
{ "test-key", "test-value" }
|
||||||
};
|
};
|
||||||
var provider = new DictionaryBasedValueProvider(values);
|
var provider = new DictionaryBasedValueProvider<TestValueBinderMarker>(values);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await provider.ContainsPrefixAsync("not-test-key");
|
var result = await provider.ContainsPrefixAsync("not-test-key");
|
||||||
|
|
@ -88,7 +88,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
{ "test-key", "test-value" }
|
{ "test-key", "test-value" }
|
||||||
};
|
};
|
||||||
var provider = new DictionaryBasedValueProvider(values);
|
var provider = new DictionaryBasedValueProvider<TestValueBinderMarker>(values);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await provider.ContainsPrefixAsync("test-key");
|
var result = await provider.ContainsPrefixAsync("test-key");
|
||||||
|
|
@ -96,5 +96,38 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result);
|
Assert.True(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> RegisteredAsMarkerClasses
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
yield return new object[] { new TestValueBinderMarker() };
|
||||||
|
yield return new object[] { new DerivedValueBinder() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RegisteredAsMarkerClasses))]
|
||||||
|
public void FilterReturnsItself_ForAnyClassRegisteredAsGenericParam(IValueBinderMarker binderMarker)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
var provider = new DictionaryBasedValueProvider<TestValueBinderMarker>(values);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = provider.Filter(binderMarker);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.IsType<DictionaryBasedValueProvider<TestValueBinderMarker>>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestValueBinderMarker : IValueBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DerivedValueBinder :TestValueBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
var result = factory.GetValueProvider(context);
|
var result = factory.GetValueProvider(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var valueProvider = Assert.IsType<ReadableStringCollectionValueProvider>(result);
|
var valueProvider = Assert.IsType<ReadableStringCollectionValueProvider<IFormDataMarker>>(result);
|
||||||
Assert.Equal(CultureInfo.CurrentCulture, valueProvider.Culture);
|
Assert.Equal(CultureInfo.CurrentCulture, valueProvider.Culture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
var result = _factory.GetValueProvider(factoryContext);
|
var result = _factory.GetValueProvider(factoryContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var valueProvider = Assert.IsType<ReadableStringCollectionValueProvider>(result);
|
var valueProvider = Assert.IsType<ReadableStringCollectionValueProvider<IQueryBinderMarker>>(result);
|
||||||
Assert.Equal(CultureInfo.InvariantCulture, valueProvider.Culture);
|
Assert.Equal(CultureInfo.InvariantCulture, valueProvider.Culture);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// 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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
@ -27,7 +28,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var backingStore = new ReadableStringCollection(new Dictionary<string, string[]>());
|
var backingStore = new ReadableStringCollection(new Dictionary<string, string[]>());
|
||||||
var valueProvider = new ReadableStringCollectionValueProvider(backingStore, null);
|
var valueProvider = new ReadableStringCollectionValueProvider<TestValueBinderMarker>(backingStore, null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await valueProvider.ContainsPrefixAsync("");
|
var result = await valueProvider.ContainsPrefixAsync("");
|
||||||
|
|
@ -40,7 +41,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForEmptyPrefix()
|
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForEmptyPrefix()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, null);
|
var valueProvider = new ReadableStringCollectionValueProvider<TestValueBinderMarker>(_backingStore, null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await valueProvider.ContainsPrefixAsync("");
|
var result = await valueProvider.ContainsPrefixAsync("");
|
||||||
|
|
@ -53,7 +54,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForKnownPrefixes()
|
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsTrueForKnownPrefixes()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, null);
|
var valueProvider = new ReadableStringCollectionValueProvider<TestValueBinderMarker>(_backingStore, null);
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
Assert.True(await valueProvider.ContainsPrefixAsync("foo"));
|
Assert.True(await valueProvider.ContainsPrefixAsync("foo"));
|
||||||
|
|
@ -65,7 +66,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsFalseForUnknownPrefix()
|
public async Task ContainsPrefixAsync_WithNonEmptyCollection_ReturnsFalseForUnknownPrefix()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, null);
|
var valueProvider = new ReadableStringCollectionValueProvider<TestValueBinderMarker>(_backingStore, null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await valueProvider.ContainsPrefixAsync("biff");
|
var result = await valueProvider.ContainsPrefixAsync("biff");
|
||||||
|
|
@ -85,7 +86,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
{ "null_value", "null_value" },
|
{ "null_value", "null_value" },
|
||||||
{ "prefix", "prefix" }
|
{ "prefix", "prefix" }
|
||||||
};
|
};
|
||||||
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, culture: null);
|
var valueProvider = new ReadableStringCollectionValueProvider<TestValueBinderMarker>(_backingStore, culture: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await valueProvider.GetKeysFromPrefixAsync("");
|
var result = await valueProvider.GetKeysFromPrefixAsync("");
|
||||||
|
|
@ -98,7 +99,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
public async Task GetKeysFromPrefixAsync_UnknownPrefix_ReturnsEmptyDictionary()
|
public async Task GetKeysFromPrefixAsync_UnknownPrefix_ReturnsEmptyDictionary()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, null);
|
var valueProvider = new ReadableStringCollectionValueProvider<TestValueBinderMarker>(_backingStore, null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await valueProvider.GetKeysFromPrefixAsync("abc");
|
var result = await valueProvider.GetKeysFromPrefixAsync("abc");
|
||||||
|
|
@ -111,7 +112,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
public async Task GetKeysFromPrefixAsync_KnownPrefix_ReturnsMatchingItems()
|
public async Task GetKeysFromPrefixAsync_KnownPrefix_ReturnsMatchingItems()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, null);
|
var valueProvider = new ReadableStringCollectionValueProvider<TestValueBinderMarker>(_backingStore, null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await valueProvider.GetKeysFromPrefixAsync("bar");
|
var result = await valueProvider.GetKeysFromPrefixAsync("bar");
|
||||||
|
|
@ -127,7 +128,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var culture = new CultureInfo("fr-FR");
|
var culture = new CultureInfo("fr-FR");
|
||||||
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, culture);
|
var valueProvider = new ReadableStringCollectionValueProvider<TestValueBinderMarker>(_backingStore, culture);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var vpResult = await valueProvider.GetValueAsync("bar.baz");
|
var vpResult = await valueProvider.GetValueAsync("bar.baz");
|
||||||
|
|
@ -144,7 +145,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var culture = new CultureInfo("fr-FR");
|
var culture = new CultureInfo("fr-FR");
|
||||||
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, culture);
|
var valueProvider = new ReadableStringCollectionValueProvider<TestValueBinderMarker>(_backingStore, culture);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var vpResult = await valueProvider.GetValueAsync("foo");
|
var vpResult = await valueProvider.GetValueAsync("foo");
|
||||||
|
|
@ -187,7 +188,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
{ "key", new string[] { null, null, "value" } }
|
{ "key", new string[] { null, null, "value" } }
|
||||||
});
|
});
|
||||||
var culture = new CultureInfo("fr-FR");
|
var culture = new CultureInfo("fr-FR");
|
||||||
var valueProvider = new ReadableStringCollectionValueProvider(backingStore, culture);
|
var valueProvider = new ReadableStringCollectionValueProvider<TestValueBinderMarker>(backingStore, culture);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var vpResult = await valueProvider.GetValueAsync("key");
|
var vpResult = await valueProvider.GetValueAsync("key");
|
||||||
|
|
@ -201,7 +202,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
public async Task GetValueAsync_ReturnsNullIfKeyNotFound()
|
public async Task GetValueAsync_ReturnsNullIfKeyNotFound()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var valueProvider = new ReadableStringCollectionValueProvider(_backingStore, null);
|
var valueProvider = new ReadableStringCollectionValueProvider<TestValueBinderMarker>(_backingStore, null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var vpResult = await valueProvider.GetValueAsync("bar");
|
var vpResult = await valueProvider.GetValueAsync("bar");
|
||||||
|
|
@ -209,5 +210,37 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Null(vpResult);
|
Assert.Null(vpResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> RegisteredAsMarkerClasses
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
yield return new object[] { new TestValueBinderMarker() };
|
||||||
|
yield return new object[] { new DerivedValueBinder() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RegisteredAsMarkerClasses))]
|
||||||
|
public void FilterReturnsItself_ForAnyClassRegisteredAsGenericParam(IValueBinderMarker binderMarker)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var valueProvider = new ReadableStringCollectionValueProvider<TestValueBinderMarker>(_backingStore, null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = valueProvider.Filter(binderMarker);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
Assert.IsType<ReadableStringCollectionValueProvider<TestValueBinderMarker>>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestValueBinderMarker : IValueBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DerivedValueBinder : TestValueBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,14 +35,15 @@ namespace Microsoft.AspNet.Mvc
|
||||||
setup.Configure(mvcOptions);
|
setup.Configure(mvcOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(7, mvcOptions.ModelBinders.Count);
|
Assert.Equal(8, mvcOptions.ModelBinders.Count);
|
||||||
Assert.Equal(typeof(TypeConverterModelBinder), mvcOptions.ModelBinders[0].OptionType);
|
Assert.Equal(typeof(BodyModelBinder), mvcOptions.ModelBinders[0].OptionType);
|
||||||
Assert.Equal(typeof(TypeMatchModelBinder), mvcOptions.ModelBinders[1].OptionType);
|
Assert.Equal(typeof(TypeConverterModelBinder), mvcOptions.ModelBinders[1].OptionType);
|
||||||
Assert.Equal(typeof(CancellationTokenModelBinder), mvcOptions.ModelBinders[2].OptionType);
|
Assert.Equal(typeof(TypeMatchModelBinder), mvcOptions.ModelBinders[2].OptionType);
|
||||||
Assert.Equal(typeof(ByteArrayModelBinder), mvcOptions.ModelBinders[3].OptionType);
|
Assert.Equal(typeof(CancellationTokenModelBinder), mvcOptions.ModelBinders[3].OptionType);
|
||||||
Assert.Equal(typeof(GenericModelBinder), mvcOptions.ModelBinders[4].OptionType);
|
Assert.Equal(typeof(ByteArrayModelBinder), mvcOptions.ModelBinders[4].OptionType);
|
||||||
Assert.Equal(typeof(MutableObjectModelBinder), mvcOptions.ModelBinders[5].OptionType);
|
Assert.Equal(typeof(GenericModelBinder), mvcOptions.ModelBinders[5].OptionType);
|
||||||
Assert.Equal(typeof(ComplexModelDtoModelBinder), mvcOptions.ModelBinders[6].OptionType);
|
Assert.Equal(typeof(MutableObjectModelBinder), mvcOptions.ModelBinders[6].OptionType);
|
||||||
|
Assert.Equal(typeof(ComplexModelDtoModelBinder), mvcOptions.ModelBinders[7].OptionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace ModelBindingWebSite.Controllers
|
||||||
|
{
|
||||||
|
[Route("/CompositeTest/[action]/{param}")]
|
||||||
|
public class CompositeTestController : Controller
|
||||||
|
{
|
||||||
|
public string RestrictValueProvidersUsingFromQuery([FromQuery] string param)
|
||||||
|
{
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RestrictValueProvidersUsingFromRoute([FromRoute] string param)
|
||||||
|
{
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RestrictValueProvidersUsingFromForm([FromForm] string param)
|
||||||
|
{
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace ModelBindingWebSite.Controllers
|
||||||
|
{
|
||||||
|
public class MultipleParametersFromBodyController : Controller
|
||||||
|
{
|
||||||
|
public void MultipleParametersFromBodyThrows([FromBody] int i, [FromBody] string emp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace ModelBindingWebSite.Controllers
|
||||||
|
{
|
||||||
|
public class WithMarkerController : Controller
|
||||||
|
{
|
||||||
|
public EmployeeWithMarker BindWithTypeMarker(EmployeeWithMarker emp)
|
||||||
|
{
|
||||||
|
return emp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DerivedEmployee TypeMarkerAtDerivedTypeWinsOverTheBaseType(DerivedEmployee emp)
|
||||||
|
{
|
||||||
|
return emp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ParameterMarkerOverridesTypeMarker([FromBody] Employee emp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Employee ParametersWithNoMarkersUseTheAvailableValueProviders([FromQuery] Employee emp)
|
||||||
|
{
|
||||||
|
return emp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace ModelBindingWebSite.Controllers
|
||||||
|
{
|
||||||
|
public class WithoutMarkerController : Controller
|
||||||
|
{
|
||||||
|
public Person Person { get; set; }
|
||||||
|
|
||||||
|
public Person GetPersonProperty()
|
||||||
|
{
|
||||||
|
return Person;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Person GetPersonParameter(Person p)
|
||||||
|
{
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SimpleTypes(int id, string name, bool isValid, DateTime lastUpdateTime)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace ModelBindingWebSite
|
||||||
|
{
|
||||||
|
public class Employee : Person
|
||||||
|
{
|
||||||
|
public string Department { get; set; }
|
||||||
|
|
||||||
|
public string Location { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace ModelBindingWebSite
|
||||||
|
{
|
||||||
|
public class EmployeeWithMarker : Employee
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DerivedEmployee : EmployeeWithMarker
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace ModelBindingWebSite
|
||||||
|
{
|
||||||
|
public class ExternalType
|
||||||
|
{
|
||||||
|
public string Department { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace ModelBindingWebSite
|
||||||
|
{
|
||||||
|
public class Person
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public int Age { get; set; }
|
||||||
|
|
||||||
|
public Person Parent { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -34,6 +34,11 @@ namespace ValueProvidersSite
|
||||||
var result = new ValueProviderResult(value, value, CultureInfo.CurrentCulture);
|
var result = new ValueProviderResult(value, value, CultureInfo.CurrentCulture);
|
||||||
return Task.FromResult(result);
|
return Task.FromResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsValidFor(Type valueProviderMarkerType)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue