diff --git a/src/Microsoft.AspNet.Mvc.Core/ControllerActionArgumentBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ControllerActionArgumentBinder.cs
new file mode 100644
index 0000000000..b3d854a079
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/ControllerActionArgumentBinder.cs
@@ -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
+{
+ ///
+ /// Provides a default implementation of .
+ /// Uses ModelBinding to populate action parameters.
+ ///
+ 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> 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(StringComparer.Ordinal);
+ await PopulateActionArgumentsAsync(parameterMetadatas, actionBindingContext, actionArguments);
+ return actionArguments;
+ }
+
+ private async Task PopulateActionArgumentsAsync(IEnumerable modelMetadatas,
+ ActionBindingContext actionBindingContext,
+ IDictionary 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;
+ }
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs
index 5d77e53ff1..6211c156cf 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs
@@ -2,6 +2,7 @@
// 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;
@@ -15,19 +16,20 @@ namespace Microsoft.AspNet.Mvc
private readonly ControllerActionDescriptor _descriptor;
private readonly IControllerFactory _controllerFactory;
private readonly IInputFormattersProvider _inputFormattersProvider;
+ private readonly IControllerActionArgumentBinder _actionInvocationProvider;
public ControllerActionInvoker([NotNull] ActionContext actionContext,
- [NotNull] IActionBindingContextProvider bindingContextProvider,
[NotNull] INestedProviderManager filterProvider,
[NotNull] IControllerFactory controllerFactory,
[NotNull] ControllerActionDescriptor descriptor,
[NotNull] IInputFormattersProvider inputFormattersProvider,
- [NotNull] IBodyModelValidator modelValidator)
- : base(actionContext, bindingContextProvider, filterProvider, modelValidator)
+ [NotNull] IControllerActionArgumentBinder controllerActionArgumentBinder)
+ : base(actionContext, filterProvider)
{
_descriptor = descriptor;
_controllerFactory = controllerFactory;
_inputFormattersProvider = inputFormattersProvider;
+ _actionInvocationProvider = controllerActionArgumentBinder;
if (descriptor.MethodInfo == null)
{
throw new ArgumentException(
@@ -67,6 +69,11 @@ namespace Microsoft.AspNet.Mvc
return actionResult;
}
+ protected override Task> GetActionArgumentsAsync(ActionContext context)
+ {
+ return _actionInvocationProvider.GetActionArgumentsAsync(context);
+ }
+
// Marking as internal for Unit Testing purposes.
internal static IActionResult CreateActionResult([NotNull] Type declaredReturnType, object actionReturnValue)
{
diff --git a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs
index 6cb0d75487..3706723041 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs
@@ -10,22 +10,19 @@ namespace Microsoft.AspNet.Mvc
public class ControllerActionInvokerProvider : IActionInvokerProvider
{
private readonly IControllerFactory _controllerFactory;
- private readonly IActionBindingContextProvider _bindingProvider;
private readonly IInputFormattersProvider _inputFormattersProvider;
private readonly INestedProviderManager _filterProvider;
- private readonly IBodyModelValidator _modelValidator;
+ private readonly IControllerActionArgumentBinder _actionInvocationInfoProvider;
public ControllerActionInvokerProvider(IControllerFactory controllerFactory,
- IActionBindingContextProvider bindingProvider,
IInputFormattersProvider inputFormattersProvider,
INestedProviderManager filterProvider,
- IBodyModelValidator modelValidator)
+ IControllerActionArgumentBinder actionInvocationInfoProvider)
{
_controllerFactory = controllerFactory;
- _bindingProvider = bindingProvider;
_inputFormattersProvider = inputFormattersProvider;
_filterProvider = filterProvider;
- _modelValidator = modelValidator;
+ _actionInvocationInfoProvider = actionInvocationInfoProvider;
}
public int Order
@@ -41,12 +38,11 @@ namespace Microsoft.AspNet.Mvc
{
context.Result = new ControllerActionInvoker(
context.ActionContext,
- _bindingProvider,
_filterProvider,
_controllerFactory,
actionDescriptor,
_inputFormattersProvider,
- _modelValidator);
+ _actionInvocationInfoProvider);
}
callNext();
diff --git a/src/Microsoft.AspNet.Mvc.Core/DefaultControllerFactory.cs b/src/Microsoft.AspNet.Mvc.Core/DefaultControllerFactory.cs
index 8eb6c5f674..898de4bf8e 100644
--- a/src/Microsoft.AspNet.Mvc.Core/DefaultControllerFactory.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/DefaultControllerFactory.cs
@@ -28,7 +28,7 @@ namespace Microsoft.AspNet.Mvc
if (actionDescriptor == null)
{
throw new ArgumentException(
- Resources.FormatDefaultControllerFactory_ActionDescriptorMustBeReflected(
+ Resources.FormatActionDescriptorMustBeBasedOnControllerAction(
typeof(ControllerActionDescriptor)),
"actionContext");
}
diff --git a/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs
index 23b88b19bc..6353c5d622 100644
--- a/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs
@@ -15,9 +15,7 @@ namespace Microsoft.AspNet.Mvc
{
public abstract class FilterActionInvoker : IActionInvoker
{
- private readonly IActionBindingContextProvider _bindingProvider;
private readonly INestedProviderManager _filterProvider;
- private readonly IBodyModelValidator _modelValidator;
private IFilter[] _filters;
private FilterCursor _cursor;
@@ -34,20 +32,18 @@ namespace Microsoft.AspNet.Mvc
public FilterActionInvoker(
[NotNull] ActionContext actionContext,
- [NotNull] IActionBindingContextProvider bindingContextProvider,
- [NotNull] INestedProviderManager filterProvider,
- [NotNull] IBodyModelValidator modelValidator)
+ [NotNull] INestedProviderManager filterProvider)
{
ActionContext = actionContext;
- _bindingProvider = bindingContextProvider;
_filterProvider = filterProvider;
- _modelValidator = modelValidator;
}
protected ActionContext ActionContext { get; private set; }
protected abstract Task InvokeActionAsync(ActionExecutingContext actionExecutingContext);
+ protected abstract Task> GetActionArgumentsAsync([NotNull] ActionContext context);
+
public virtual async Task InvokeAsync()
{
_filters = GetFilters();
@@ -220,80 +216,11 @@ namespace Microsoft.AspNet.Mvc
private async Task InvokeActionMethodWithFilters()
{
_cursor.SetStage(FilterStage.ActionFilters);
-
- var arguments = await GetActionArguments(ActionContext.ModelState);
+ var arguments = await GetActionArgumentsAsync(ActionContext);
_actionExecutingContext = new ActionExecutingContext(ActionContext, _filters, arguments);
-
await InvokeActionMethodFilter();
}
- internal async Task> GetActionArguments(ModelStateDictionary modelState)
- {
- var actionBindingContext = await _bindingProvider.GetActionBindingContextAsync(ActionContext);
- var parameters = ActionContext.ActionDescriptor.Parameters;
- var metadataProvider = actionBindingContext.MetadataProvider;
- var parameterValues = new Dictionary(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 InvokeActionMethodFilter()
{
Contract.Assert(_actionExecutingContext != null);
diff --git a/src/Microsoft.AspNet.Mvc.Core/IControllerActionArgumentBinder.cs b/src/Microsoft.AspNet.Mvc.Core/IControllerActionArgumentBinder.cs
new file mode 100644
index 0000000000..51dc2ed0e8
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/IControllerActionArgumentBinder.cs
@@ -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
+{
+ ///
+ /// Provides a dictionary of action arguments.
+ ///
+ public interface IControllerActionArgumentBinder
+ {
+ ///
+ /// Returns a dictionary of representing the parameter-argument name-value pairs,
+ /// which can be used to invoke the action.
+ ///
+ /// The action context assoicated with the current action.
+ Task> GetActionArgumentsAsync([NotNull] ActionContext context);
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.Core/ModelBinders/BodyModelBinder.cs b/src/Microsoft.AspNet.Mvc.Core/ModelBinders/BodyModelBinder.cs
new file mode 100644
index 0000000000..0cf768ddf7
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.Core/ModelBinders/BodyModelBinder.cs
@@ -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
+{
+ ///
+ /// Represents a model binder which understands and uses
+ /// InputFomatters to bind the model to request's body.
+ ///
+ public class BodyModelBinder : MarkerAwareBinder
+ {
+ private readonly ActionContext _actionContext;
+ private readonly IInputFormatterSelector _formatterSelector;
+ private readonly IBodyModelValidator _bodyModelValidator;
+
+ public BodyModelBinder([NotNull] IContextAccessor context,
+ [NotNull] IInputFormatterSelector selector,
+ [NotNull] IBodyModelValidator bodyModelValidator)
+ {
+ _actionContext = context.Value;
+ _formatterSelector = selector;
+ _bodyModelValidator = bodyModelValidator;
+ }
+
+ protected override async Task 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;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterDescriptor.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterDescriptor.cs
index e29fd00ace..39610e508a 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ParameterDescriptor.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/ParameterDescriptor.cs
@@ -2,6 +2,7 @@
// 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
{
@@ -11,6 +12,8 @@ namespace Microsoft.AspNet.Mvc
public bool IsOptional { get; set; }
+ public Type ParameterType { get; set; }
+
public ParameterBindingInfo ParameterBindingInfo { get; set; }
public BodyParameterInfo BodyParameterInfo { get; set; }
diff --git a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
index db667803ed..c5c0401072 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs
@@ -10,6 +10,22 @@ namespace Microsoft.AspNet.Mvc.Core
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.AspNet.Mvc.Core.Resources", typeof(Resources).GetTypeInfo().Assembly);
+ ///
+ /// More than one parameter is bound to the HTTP request's content.
+ ///
+ internal static string MultipleBodyParametersAreNotAllowed
+ {
+ get { return GetString("MultipleBodyParametersAreNotAllowed"); }
+ }
+
+ ///
+ /// More than one parameter is bound to the HTTP request's content.
+ ///
+ internal static string FormatMultipleBodyParametersAreNotAllowed()
+ {
+ return GetString("MultipleBodyParametersAreNotAllowed");
+ }
+
///
/// The provided anti-forgery token failed a custom data check.
///
@@ -413,17 +429,17 @@ namespace Microsoft.AspNet.Mvc.Core
///
/// The action descriptor must be of type '{0}'.
///
- internal static string DefaultControllerFactory_ActionDescriptorMustBeReflected
+ internal static string ActionDescriptorMustBeBasedOnControllerAction
{
- get { return GetString("DefaultControllerFactory_ActionDescriptorMustBeReflected"); }
+ get { return GetString("ActionDescriptorMustBeBasedOnControllerAction"); }
}
///
/// The action descriptor must be of type '{0}'.
///
- 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);
}
///
diff --git a/src/Microsoft.AspNet.Mvc.Core/Resources.resx b/src/Microsoft.AspNet.Mvc.Core/Resources.resx
index 38e275f72d..14ff825342 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Resources.resx
+++ b/src/Microsoft.AspNet.Mvc.Core/Resources.resx
@@ -117,6 +117,9 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ More than one parameter is bound to the HTTP request's content.
+
The provided anti-forgery token failed a custom data check.
@@ -192,7 +195,7 @@
An action invoker could not be created for action '{0}'.
-
+
The action descriptor must be of type '{0}'.
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/FromBodyAttribute.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/FromBodyAttribute.cs
new file mode 100644
index 0000000000..da41941d13
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/FromBodyAttribute.cs
@@ -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
+{
+ ///
+ /// This attribute is used on action parameters to indicate
+ /// they are bound from the body of the incoming request.
+ ///
+ [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
+ public class FromBodyAttribute : Attribute, IBodyBinderMarker
+ {
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/FromFormAttribute.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/FromFormAttribute.cs
new file mode 100644
index 0000000000..ea3673945e
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/FromFormAttribute.cs
@@ -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
+{
+ ///
+ /// This attribute is used on action parameters to indicate that
+ /// they will be bound using form data of the incoming request.
+ ///
+ [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
+ public class FromFormAttribute : Attribute, IFormDataMarker
+ {
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/FromQueryAttribute.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/FromQueryAttribute.cs
new file mode 100644
index 0000000000..5509419dd7
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/FromQueryAttribute.cs
@@ -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
+{
+ ///
+ /// This attribute is used on action parameters to indicate that
+ /// they will be bound using query data of the incoming request.
+ ///
+ [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
+ public class FromQueryAttribute : Attribute, IQueryBinderMarker
+ {
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/FromRouteAttribute.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/FromRouteAttribute.cs
new file mode 100644
index 0000000000..c4d5fc4da2
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/FromRouteAttribute.cs
@@ -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
+{
+ ///
+ /// This attribute is used on action parameters to indicate that
+ /// they will be bound using route data of the incoming request.
+ ///
+ [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
+ public class FromRouteAttribute : Attribute, IRouteDataMarker
+ {
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/FromBodyAttribute.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IBinderMarker.cs
similarity index 51%
rename from src/Microsoft.AspNet.Mvc.Core/ParameterBinding/FromBodyAttribute.cs
rename to src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IBinderMarker.cs
index 8aa0e27b18..568e9c7685 100644
--- a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/FromBodyAttribute.cs
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IBinderMarker.cs
@@ -3,10 +3,12 @@
using System;
-namespace Microsoft.AspNet.Mvc
+namespace Microsoft.AspNet.Mvc.ModelBinding
{
- [AttributeUsage(AttributeTargets.Parameter)]
- public sealed class FromBodyAttribute : Attribute
+ ///
+ /// Represents a marker used to identify a particular binder applies to a model.
+ ///
+ public interface IBinderMarker
{
}
}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IBodyBinderMarker.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IBodyBinderMarker.cs
new file mode 100644
index 0000000000..5ade36c809
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IBodyBinderMarker.cs
@@ -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
+{
+ ///
+ /// Represents a marker used to identify a binder which can bind request body to a model.
+ ///
+ public interface IBodyBinderMarker : IBinderMarker
+ {
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IFormDataMarker.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IFormDataMarker.cs
new file mode 100644
index 0000000000..7ee357c3ea
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IFormDataMarker.cs
@@ -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
+{
+ ///
+ /// Represents a marker used to identify a binder which can bind form data to a model.
+ ///
+ public interface IFormDataMarker : IValueBinderMarker
+ {
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IQueryBinderMarker.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IQueryBinderMarker.cs
new file mode 100644
index 0000000000..712311ffaf
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IQueryBinderMarker.cs
@@ -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
+{
+ ///
+ /// Represents a marker used to identify a binder which can bind query data to a model.
+ ///
+ public interface IQueryBinderMarker : IValueBinderMarker
+ {
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IRouteDataMarker.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IRouteDataMarker.cs
new file mode 100644
index 0000000000..a0784c2ab7
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IRouteDataMarker.cs
@@ -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
+{
+ ///
+ /// Represents a marker used to identify a binder which can bind route data to a model.
+ ///
+ public interface IRouteDataMarker : IValueBinderMarker
+ {
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IValueBinderMarker.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IValueBinderMarker.cs
new file mode 100644
index 0000000000..0f65cb1bca
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/BinderMarkers/IValueBinderMarker.cs
@@ -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
+{
+ ///
+ /// Represents a binder marker which identifies a binder which is based on a value provider.
+ ///
+ public interface IValueBinderMarker : IBinderMarker
+ {
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CompositeModelBinder.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CompositeModelBinder.cs
index 8bd9d52328..15c312eb29 100644
--- a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CompositeModelBinder.cs
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/CompositeModelBinder.cs
@@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.ModelBinding
@@ -132,7 +134,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
ValidatorProvider = oldBindingContext.ValidatorProvider,
MetadataProvider = oldBindingContext.MetadataProvider,
ModelBinder = oldBindingContext.ModelBinder,
- HttpContext = oldBindingContext.HttpContext
+ HttpContext = oldBindingContext.HttpContext,
};
// 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;
}
+ // 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;
}
}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/IMarkerAwareBinder.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/IMarkerAwareBinder.cs
new file mode 100644
index 0000000000..b0b67a86bc
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/IMarkerAwareBinder.cs
@@ -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
+{
+ ///
+ /// An which is aware of .
+ ///
+ public interface IMarkerAwareBinder : IModelBinder
+ {
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/MarkerAwareBinder.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/MarkerAwareBinder.cs
new file mode 100644
index 0000000000..dd37f83238
--- /dev/null
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Binders/MarkerAwareBinder.cs
@@ -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
+{
+ ///
+ /// Represents an which can select itself based on the
+ /// .
+ ///
+ /// Represents a type implementing
+ public abstract class MarkerAwareBinder : IMarkerAwareBinder
+ where TBinderMarker : IBinderMarker
+ {
+ ///
+ /// Async function which does the actual binding to bind to a particular model.
+ ///
+ /// The binding context which has the object to be bound.
+ /// The associated with the current binder.
+ /// A Task with a bool implying the success or failure of the operation.
+ protected abstract Task BindAsync(ModelBindingContext bindingContext, TBinderMarker marker);
+
+ public Task BindModelAsync(ModelBindingContext context)
+ {
+ if (context.ModelMetadata.Marker is TBinderMarker)
+ {
+ var marker = (TBinderMarker)context.ModelMetadata.Marker;
+ return BindAsync(context, marker);
+ }
+
+ return Task.FromResult(false);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/AssociatedMetadataProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/AssociatedMetadataProvider.cs
index 2e23e34724..1e1508e767 100644
--- a/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/AssociatedMetadataProvider.cs
+++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Metadata/AssociatedMetadataProvider.cs
@@ -46,6 +46,36 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return CreateMetadataFromPrototype(prototype, modelAccessor);
}
+ public IEnumerable 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