From e87f8c372cd50fc21545fa7fdda8b3d7a99f4221 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 24 Feb 2014 19:21:37 -0800 Subject: [PATCH] Consume ModelBinding from Mvc * Modify ReflectedActionInvoker to model bind parameters * Introduce IBodyReader for reading request bodies * Introduce types for per-action-context specific binders, value providers and body readers --- samples/MvcSample/HomeController.cs | 16 ++++ samples/MvcSample/Startup.cs | 10 ++- .../ActionInvokerProvider.cs | 20 ++--- .../BodyParameterInfo.cs | 15 ++++ .../DefaultActionSelector.cs | 23 +++--- .../DefaultParameterDescriptorFactory.cs | 30 ++++---- .../ActionBindingContextExtensions.cs | 38 ++++++++++ .../ParameterBinding/ActionBindingContext.cs | 30 ++++++++ .../DefaultActionBindingContextProvider.cs | 42 +++++++++++ .../ParameterBinding/FromBodyAttribute.cs | 9 +++ .../IActionBindingContextProvider.cs | 10 +++ .../ParameterBindingInfo.cs | 14 ++-- .../ParameterDescriptor.cs | 8 +- .../ReflectedActionInvoker.cs | 75 +++++++++++++++++-- .../Formatters/CompositeInputFormatter.cs | 29 +++++++ .../Formatters/IInputFormatter.cs | 9 +++ .../Formatters/InputFormatterContext.cs | 19 +++++ .../Formatters/JsonInputFormatter.cs | 13 ++++ .../ValueProviders/CompositeValueProvider.cs | 5 +- src/Microsoft.AspNet.Mvc/MvcServices.cs | 17 ++++- 20 files changed, 376 insertions(+), 56 deletions(-) create mode 100644 src/Microsoft.AspNet.Mvc.Core/BodyParameterInfo.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/Internal/ActionBindingContextExtensions.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/ParameterBinding/FromBodyAttribute.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/ParameterBinding/IActionBindingContextProvider.cs create mode 100644 src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/CompositeInputFormatter.cs create mode 100644 src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/IInputFormatter.cs create mode 100644 src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/InputFormatterContext.cs create mode 100644 src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/JsonInputFormatter.cs diff --git a/samples/MvcSample/HomeController.cs b/samples/MvcSample/HomeController.cs index db8481ef11..6843fdec4f 100644 --- a/samples/MvcSample/HomeController.cs +++ b/samples/MvcSample/HomeController.cs @@ -10,6 +10,22 @@ namespace MvcSample return View("MyView", User()); } + /// + /// Action that exercises query\form based model binding. + /// + public IActionResult SaveUser(User user) + { + return View("MyView", user); + } + + /// + /// Action that exercises input formatter + /// + public IActionResult Post([FromBody]User user) + { + return View("MyView", user); + } + public IActionResult Something() { return new ContentResult diff --git a/samples/MvcSample/Startup.cs b/samples/MvcSample/Startup.cs index d3f5adbe64..b01e7ee5b4 100644 --- a/samples/MvcSample/Startup.cs +++ b/samples/MvcSample/Startup.cs @@ -40,14 +40,16 @@ namespace MvcSample var endpoint = ActivatorUtilities.CreateInstance(mvcServices.Services); - router.Add(new TemplateRoute( - endpoint, - "{controller}/{action}", - new Dictionary(StringComparer.OrdinalIgnoreCase) { { "controller", "Home" }, { "action", "Index" } })); router.Add(new TemplateRoute( endpoint, "{controller}", new Dictionary(StringComparer.OrdinalIgnoreCase) { { "controller", "Home" } })); + + router.Add(new TemplateRoute( + endpoint, + "{controller}/{action}", + new Dictionary(StringComparer.OrdinalIgnoreCase) { { "controller", "Home" }, { "action", "Index" } })); + } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionInvokerProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ActionInvokerProvider.cs index 7025a62901..d49f04c2f4 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionInvokerProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionInvokerProvider.cs @@ -7,13 +7,16 @@ namespace Microsoft.AspNet.Mvc private readonly IActionResultFactory _actionResultFactory; private readonly IServiceProvider _serviceProvider; private readonly IControllerFactory _controllerFactory; + private readonly IActionBindingContextProvider _bindingProvider; public ActionInvokerProvider(IActionResultFactory actionResultFactory, IControllerFactory controllerFactory, + IActionBindingContextProvider bindingProvider, IServiceProvider serviceProvider) { _actionResultFactory = actionResultFactory; _controllerFactory = controllerFactory; + _bindingProvider = bindingProvider; _serviceProvider = serviceProvider; } @@ -24,21 +27,20 @@ namespace Microsoft.AspNet.Mvc public void Invoke(ActionInvokerProviderContext context, Action callNext) { - var ad = context.ActionContext.ActionDescriptor as ReflectedActionDescriptor; + var actionDescriptor = context.ActionContext.ActionDescriptor as ReflectedActionDescriptor; - if (ad != null) + if (actionDescriptor != null) { context.Result = new ReflectedActionInvoker( - context.ActionContext, - ad, - _actionResultFactory, - _controllerFactory, - _serviceProvider); + context.ActionContext, + actionDescriptor, + _actionResultFactory, + _controllerFactory, + _bindingProvider, + _serviceProvider); } callNext(); } - - } } diff --git a/src/Microsoft.AspNet.Mvc.Core/BodyParameterInfo.cs b/src/Microsoft.AspNet.Mvc.Core/BodyParameterInfo.cs new file mode 100644 index 0000000000..f05807f086 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/BodyParameterInfo.cs @@ -0,0 +1,15 @@ +using System; + +namespace Microsoft.AspNet.Mvc +{ + public class BodyParameterInfo + { + public BodyParameterInfo(Type parameterType) + { + ParameterType = parameterType; + } + + public Type ParameterType { get; private set; } + } +} + diff --git a/src/Microsoft.AspNet.Mvc.Core/DefaultActionSelector.cs b/src/Microsoft.AspNet.Mvc.Core/DefaultActionSelector.cs index 0a79115bcb..a04de05dfb 100644 --- a/src/Microsoft.AspNet.Mvc.Core/DefaultActionSelector.cs +++ b/src/Microsoft.AspNet.Mvc.Core/DefaultActionSelector.cs @@ -3,21 +3,19 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.DependencyInjection; -using Microsoft.AspNet.Mvc.ModelBinding; namespace Microsoft.AspNet.Mvc { public class DefaultActionSelector : IActionSelector { private readonly INestedProviderManager _actionDescriptorProvider; - private readonly IEnumerable _valueProviderFactory; + private readonly IActionBindingContextProvider _bindingProvider; - public DefaultActionSelector( - INestedProviderManager actionDescriptorProvider, - IEnumerable valueProviderFactories) + public DefaultActionSelector(INestedProviderManager actionDescriptorProvider, + IActionBindingContextProvider bindingProvider) { _actionDescriptorProvider = actionDescriptorProvider; - _valueProviderFactory = valueProviderFactories; + _bindingProvider = bindingProvider; } public async Task SelectAsync(RequestContext context) @@ -61,9 +59,6 @@ namespace Microsoft.AspNet.Mvc protected virtual async Task SelectBestCandidate(RequestContext context, List candidates) { - var valueProviders = await Task.WhenAll(_valueProviderFactory.Select(vpf => vpf.GetValueProviderAsync(context))); - valueProviders = valueProviders.Where(vp => vp != null).ToArray(); - var applicableCandiates = new List(); foreach (var action in candidates) { @@ -72,18 +67,20 @@ namespace Microsoft.AspNet.Mvc { Action = action, }; + var actionContext = new ActionContext(context.HttpContext, context.RouteValues, action); + var actionBindingContext = await _bindingProvider.GetActionBindingContextAsync(actionContext); - foreach (var parameter in action.Parameters.Where(p => !p.Binding.IsFromBody)) + foreach (var parameter in action.Parameters.Where(p => p.ParameterBindingInfo != null)) { - if (valueProviders.Any(vp => vp.ContainsPrefix(parameter.Binding.Prefix))) + if (actionBindingContext.ValueProvider.ContainsPrefix(parameter.ParameterBindingInfo.Prefix)) { candidate.FoundParameters++; - if (parameter.Binding.IsOptional) + if (parameter.IsOptional) { candidate.FoundOptionalParameters++; } } - else if (!parameter.Binding.IsOptional) + else if (!parameter.IsOptional) { isApplicable = false; break; diff --git a/src/Microsoft.AspNet.Mvc.Core/DefaultParameterDescriptorFactory.cs b/src/Microsoft.AspNet.Mvc.Core/DefaultParameterDescriptorFactory.cs index 4bb1ec6b4a..a32167bcae 100644 --- a/src/Microsoft.AspNet.Mvc.Core/DefaultParameterDescriptorFactory.cs +++ b/src/Microsoft.AspNet.Mvc.Core/DefaultParameterDescriptorFactory.cs @@ -1,5 +1,4 @@ - -using System.Reflection; +using System.Reflection; namespace Microsoft.AspNet.Mvc { @@ -7,24 +6,29 @@ namespace Microsoft.AspNet.Mvc { public ParameterDescriptor GetDescriptor(ParameterInfo parameter) { - var bindingInfo = new ParameterBindingInfo() - { - IsOptional = parameter.IsOptional, - IsFromBody = IsFromBody(parameter), - Prefix = parameter.Name, - }; + bool isFromBody = IsFromBody(parameter); - return new ParameterDescriptor() + return new ParameterDescriptor { Name = parameter.Name, - Binding = bindingInfo, + IsOptional = parameter.IsOptional, + ParameterBindingInfo = isFromBody ? null : GetParameterBindingInfo(parameter), + BodyParameterInfo = isFromBody ? GetBodyParameterInfo(parameter) : null }; } - public virtual bool IsFromBody(ParameterInfo parameter) { - // Assume for now everything is read from value providers - return false; + return parameter.GetCustomAttribute() != null; + } + + private ParameterBindingInfo GetParameterBindingInfo(ParameterInfo parameter) + { + return new ParameterBindingInfo(parameter.Name, parameter.ParameterType); + } + + private BodyParameterInfo GetBodyParameterInfo(ParameterInfo parameter) + { + return new BodyParameterInfo(parameter.ParameterType); } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/Internal/ActionBindingContextExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/Internal/ActionBindingContextExtensions.cs new file mode 100644 index 0000000000..9e311f0039 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Internal/ActionBindingContextExtensions.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNet.Mvc.ModelBinding; + +namespace Microsoft.AspNet.Mvc.Internal +{ + public static class ActionBindingContextExtensions + { + public static InputFormatterContext CreateInputFormatterContext(this ActionBindingContext actionBindingContext, + ModelStateDictionary modelState, + ParameterDescriptor parameter) + { + var metadataProvider = actionBindingContext.MetadataProvider; + var parameterType = parameter.BodyParameterInfo.ParameterType; + var modelMetadata = metadataProvider.GetMetadataForType(modelAccessor: null, modelType: parameterType); + return new InputFormatterContext(modelMetadata, modelState); + } + + public static ModelBindingContext CreateModelBindingContext(this ActionBindingContext actionBindingContext, + ModelStateDictionary modelState, + ParameterDescriptor parameter) + { + var metadataProvider = actionBindingContext.MetadataProvider; + var parameterType = parameter.ParameterBindingInfo.ParameterType; + var modelMetadata = metadataProvider.GetMetadataForType(modelAccessor: null, modelType: parameterType); + + return new ModelBindingContext + { + ModelName = parameter.Name, + ModelState = modelState, + ModelMetadata = modelMetadata, + ModelBinder = actionBindingContext.ModelBinder, + ValueProvider = actionBindingContext.ValueProvider, + MetadataProvider = metadataProvider, + HttpContext = actionBindingContext.ActionContext.HttpContext, + FallbackToEmptyPrefix = true + }; + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs new file mode 100644 index 0000000000..debc2a555e --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNet.Mvc.ModelBinding; + +namespace Microsoft.AspNet.Mvc +{ + public class ActionBindingContext + { + public ActionBindingContext(ActionContext context, + IModelMetadataProvider metadataProvider, + IModelBinder modelBinder, + IValueProvider valueProvider, + IInputFormatter inputFormatter) + { + ActionContext = context; + MetadataProvider = metadataProvider; + ModelBinder = modelBinder; + ValueProvider = valueProvider; + InputFormatter = inputFormatter; + } + + public ActionContext ActionContext { get; private set; } + + public IModelMetadataProvider MetadataProvider { get; private set; } + + public IModelBinder ModelBinder { get; private set; } + + public IValueProvider ValueProvider { get; private set; } + + public IInputFormatter InputFormatter { get; private set; } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs new file mode 100644 index 0000000000..9b515aec14 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/DefaultActionBindingContextProvider.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc.ModelBinding; + +namespace Microsoft.AspNet.Mvc +{ + public class DefaultActionBindingContextProvider : IActionBindingContextProvider + { + private readonly IModelMetadataProvider _modelMetadataProvider; + private readonly IEnumerable _modelBinders; + private readonly IEnumerable _valueProviderFactories; + private readonly IEnumerable _bodyReaders; + + public DefaultActionBindingContextProvider(IModelMetadataProvider modelMetadataProvider, + IEnumerable modelBinders, + IEnumerable valueProviderFactories, + IEnumerable bodyReaders) + { + _modelMetadataProvider = modelMetadataProvider; + _modelBinders = modelBinders.OrderBy(binder => binder.GetType() == typeof(ComplexModelDtoModelBinder) ? 1 : 0); + _valueProviderFactories = valueProviderFactories; + _bodyReaders = bodyReaders; + } + + public async Task GetActionBindingContextAsync(ActionContext actionContext) + { + var requestContext = new RequestContext(actionContext.HttpContext, actionContext.RouteValues); + var valueProviders = await Task.WhenAll(_valueProviderFactories.Select(factory => factory.GetValueProviderAsync(requestContext))); + valueProviders = valueProviders.Where(vp => vp != null) + .ToArray(); + + return new ActionBindingContext( + actionContext, + _modelMetadataProvider, + new CompositeModelBinder(_modelBinders), + new CompositeValueProvider(valueProviders), + new CompositeInputFormatter(_bodyReaders) + ); + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/FromBodyAttribute.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/FromBodyAttribute.cs new file mode 100644 index 0000000000..4eae2f9f03 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/FromBodyAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Microsoft.AspNet.Mvc +{ + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class FromBodyAttribute : Attribute + { + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/IActionBindingContextProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/IActionBindingContextProvider.cs new file mode 100644 index 0000000000..838768c25e --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/IActionBindingContextProvider.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc.ModelBinding; + +namespace Microsoft.AspNet.Mvc +{ + public interface IActionBindingContextProvider + { + Task GetActionBindingContextAsync(ActionContext actionContext); + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBindingInfo.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterBindingInfo.cs index eae59a2dba..f4e5830984 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ParameterBindingInfo.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterBindingInfo.cs @@ -1,13 +1,17 @@ - +using System; + namespace Microsoft.AspNet.Mvc { - // This is a placeholder and is missing things that we'll need for real model binding public class ParameterBindingInfo { - public bool IsOptional { get; set; } + public ParameterBindingInfo(string prefix, Type parameterType) + { + Prefix = prefix; + ParameterType = parameterType; + } - public bool IsFromBody { get; set; } + public string Prefix { get; private set; } - public string Prefix { get; set; } + public Type ParameterType { get; private set; } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterDescriptor.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterDescriptor.cs index f4d6a65778..ca295bcd99 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ParameterDescriptor.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterDescriptor.cs @@ -1,4 +1,4 @@ - +using System; namespace Microsoft.AspNet.Mvc { @@ -6,6 +6,10 @@ namespace Microsoft.AspNet.Mvc { public string Name { get; set; } - public ParameterBindingInfo Binding { get; set; } + public bool IsOptional { get; set; } + + public ParameterBindingInfo ParameterBindingInfo { get; set; } + + public BodyParameterInfo BodyParameterInfo { get; set; } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs index 11a1f0621b..0a97918845 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs @@ -1,8 +1,11 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.Mvc.Internal; +using Microsoft.AspNet.Mvc.ModelBinding; namespace Microsoft.AspNet.Mvc { @@ -13,21 +16,24 @@ namespace Microsoft.AspNet.Mvc private readonly IActionResultFactory _actionResultFactory; private readonly IServiceProvider _serviceProvider; private readonly IControllerFactory _controllerFactory; + private readonly IActionBindingContextProvider _bindingProvider; public ReflectedActionInvoker(ActionContext actionContext, ReflectedActionDescriptor descriptor, IActionResultFactory actionResultFactory, IControllerFactory controllerFactory, + IActionBindingContextProvider bindingContextProvider, IServiceProvider serviceProvider) { _actionContext = actionContext; _descriptor = descriptor; _actionResultFactory = actionResultFactory; _controllerFactory = controllerFactory; + _bindingProvider = bindingContextProvider; _serviceProvider = serviceProvider; } - public Task InvokeActionAsync() + public async Task InvokeActionAsync() { IActionResult actionResult = null; @@ -39,7 +45,8 @@ namespace Microsoft.AspNet.Mvc } else { - Initialize(controller); + var modelState = new ModelStateDictionary(); + InitializeController(controller, modelState); var method = _descriptor.MethodInfo; @@ -49,17 +56,19 @@ namespace Microsoft.AspNet.Mvc } else { - object actionReturnValue = method.Invoke(controller, null); + var parameterValues = await GetParameterValues(modelState); + + object actionReturnValue = method.Invoke(controller, GetArgumentValues(parameterValues)); actionResult = _actionResultFactory.CreateActionResult(method.ReturnType, actionReturnValue, _actionContext); } } // TODO: This will probably move out once we got filters - return actionResult.ExecuteResultAsync(_actionContext); + await actionResult.ExecuteResultAsync(_actionContext); } - private void Initialize(object controller) + private void InitializeController(object controller, ModelStateDictionary modelState) { var controllerType = controller.GetType(); @@ -72,6 +81,10 @@ namespace Microsoft.AspNet.Mvc prop.SetValue(controller, _actionContext.HttpContext); } } + else if (prop.Name == "ModelState" && prop.PropertyType == typeof(ModelStateDictionary)) + { + prop.SetValue(controller, modelState); + } } var method = controllerType.GetRuntimeMethods().FirstOrDefault(m => m.Name.Equals("Initialize", StringComparison.OrdinalIgnoreCase)); @@ -86,5 +99,57 @@ namespace Microsoft.AspNet.Mvc method.Invoke(controller, args); } + + private async Task> GetParameterValues(ModelStateDictionary modelState) + { + var actionBindingContext = await _bindingProvider.GetActionBindingContextAsync(_actionContext); + var parameters = _descriptor.Parameters; + + var parameterValues = new Dictionary(parameters.Count, StringComparer.Ordinal); + for (int i = 0; i < parameters.Count; i++) + { + var parameter = parameters[i]; + if (parameter.BodyParameterInfo != null) + { + var inputFormatterContext = actionBindingContext.CreateInputFormatterContext( + modelState, + parameter); + await actionBindingContext.InputFormatter.ReadAsync(inputFormatterContext); + parameterValues[parameter.Name] = inputFormatterContext.Model; + } + else + { + var modelBindingContext = actionBindingContext.CreateModelBindingContext( + modelState, + parameter); + actionBindingContext.ModelBinder.BindModel(modelBindingContext); + parameterValues[parameter.Name] = modelBindingContext.Model; + } + + } + + return parameterValues; + } + + private object[] GetArgumentValues(IDictionary parameterValues) + { + var parameters = _descriptor.MethodInfo.GetParameters(); + var arguments = new object[parameters.Length]; + + for (int i = 0; i < arguments.Length; i++) + { + object value; + if (parameterValues.TryGetValue(parameters[i].Name, out value)) + { + arguments[i] = value; + } + else + { + arguments[i] = parameters[i].DefaultValue; + } + } + + return arguments; + } } } diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/CompositeInputFormatter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/CompositeInputFormatter.cs new file mode 100644 index 0000000000..6386bcfe04 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/CompositeInputFormatter.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + public class CompositeInputFormatter : IInputFormatter + { + private IInputFormatter[] _bodyReaders; + + public CompositeInputFormatter(IEnumerable bodyReaders) + { + _bodyReaders = bodyReaders.ToArray(); + } + + public async Task ReadAsync(InputFormatterContext context) + { + for(int i = 0; i < _bodyReaders.Length; i++) + { + if (await _bodyReaders[i].ReadAsync(context)) + { + return true; + } + } + + return false; + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/IInputFormatter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/IInputFormatter.cs new file mode 100644 index 0000000000..d32c85a9c2 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/IInputFormatter.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + public interface IInputFormatter + { + Task ReadAsync(InputFormatterContext context); + } +} diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/InputFormatterContext.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/InputFormatterContext.cs new file mode 100644 index 0000000000..f123e66293 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/InputFormatterContext.cs @@ -0,0 +1,19 @@ +using System; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + public class InputFormatterContext + { + public InputFormatterContext(ModelMetadata metadata, ModelStateDictionary modelState) + { + Metadata = metadata; + ModelState = modelState; + } + + public ModelMetadata Metadata { get; private set; } + + public ModelStateDictionary ModelState { get; private set; } + + public object Model { get; set; } + } +} diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/JsonInputFormatter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/JsonInputFormatter.cs new file mode 100644 index 0000000000..269f3077a5 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/JsonInputFormatter.cs @@ -0,0 +1,13 @@ +using System; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Mvc.ModelBinding +{ + public class JsonInputFormatter : IInputFormatter + { + public Task ReadAsync(InputFormatterContext bindingContext) + { + return Task.FromResult(false); + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/ValueProviders/CompositeValueProvider.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/ValueProviders/CompositeValueProvider.cs index 1ee8243f00..1c3a593b88 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/ValueProviders/CompositeValueProvider.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/ValueProviders/CompositeValueProvider.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; +using System.Linq; using Microsoft.AspNet.Mvc.ModelBinding.Internal; namespace Microsoft.AspNet.Mvc.ModelBinding @@ -14,8 +15,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding { } - public CompositeValueProvider(IList list) - : base(list) + public CompositeValueProvider(IEnumerable valueProviders) + : base(valueProviders.ToList()) { } diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs index 789ebf9bd1..d7767fb661 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs @@ -4,7 +4,6 @@ using Microsoft.AspNet.DependencyInjection.NestedProviders; using Microsoft.AspNet.FileSystems; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.Razor; -using Microsoft.AspNet.Mvc.Razor.Compilation; namespace Microsoft.AspNet.Mvc { @@ -28,8 +27,6 @@ namespace Microsoft.AspNet.Mvc Add(); Add(); Add(); - Add(); - Add(); Add(); Add(); AddInstance(new PhysicalFileSystem(appRoot)); @@ -48,11 +45,25 @@ namespace Microsoft.AspNet.Mvc Add(); Add(); + Add(); + Add(); + // This is temporary until DI has some magic for it Add, NestedProviderManager>(); Add, NestedProviderManager>(); Add, ReflectedActionDescriptorProvider>(); Add, ActionInvokerProvider>(); + + Add(); + Add(); + + Add(); + Add(); + Add(); + Add(); + Add(); + + Add(); } private void Add() where TU : T