Enabling basic input formatter selection.

Conflicts:
	Mvc.sln
	src/Microsoft.AspNet.Mvc.Core/Formatters/TempInputFormatterProvider.cs
	src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj
	src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs
	src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvokerProvider.cs
	src/Microsoft.AspNet.Mvc/MvcServices.cs
	test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj
	test/Microsoft.AspNet.Mvc.Core.Test/ReflectedActionInvokerTest.cs

Conflicts:
	src/Microsoft.AspNet.Mvc.Core/Formatters/TempInputFormatterProvider.cs
This commit is contained in:
harshgMSFT 2014-08-13 13:56:51 -07:00
parent 764b1e64b8
commit 6f0fa67170
15 changed files with 141 additions and 82 deletions

View File

@ -1,6 +1,7 @@
// 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 Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding;
@ -39,6 +40,12 @@ namespace Microsoft.AspNet.Mvc
public ModelStateDictionary ModelState { get; private set; }
public ActionDescriptor ActionDescriptor { get; private set; }
/// <summary>
/// Input formatters associated with this context.
/// The formatters are populated only after IInputFormattersProvider runs.
/// </summary>
public IList<IInputFormatter> InputFormatters { get; set; }
/// <summary>
/// The controller is available only after the controller factory runs.

View File

@ -240,16 +240,10 @@ namespace Microsoft.AspNet.Mvc
var modelMetadata = metadataProvider.GetMetadataForType(
modelAccessor: null,
modelType: parameterType);
var providerContext = new InputFormatterProviderContext(
actionBindingContext.ActionContext,
modelMetadata,
modelState);
var inputFormatter = actionBindingContext.InputFormatterProvider.GetInputFormatter(
providerContext);
var formatterContext = new InputFormatterContext(actionBindingContext.ActionContext,
modelMetadata.ModelType);
var inputFormatter = actionBindingContext.InputFormatterSelector.SelectFormatter(
formatterContext);
parameterValues[parameter.Name] = await inputFormatter.ReadAsync(formatterContext);
}
else

View File

@ -6,32 +6,22 @@ using System.Globalization;
namespace Microsoft.AspNet.Mvc
{
public class TempInputFormatterProvider : IInputFormatterProvider
public class DefaultInputFormatterSelector : IInputFormatterSelector
{
private IInputFormattersProvider _defaultFormattersProvider;
public TempInputFormatterProvider([NotNull] IInputFormattersProvider formattersProvider)
public IInputFormatter SelectFormatter(InputFormatterContext context)
{
_defaultFormattersProvider = formattersProvider;
}
public IInputFormatter GetInputFormatter(InputFormatterProviderContext context)
{
var request = context.ActionContext.HttpContext.Request;
var formatterContext = new InputFormatterContext(context.ActionContext,
context.Metadata.ModelType);
// TODO: https://github.com/aspnet/Mvc/issues/1014
var formatters = _defaultFormattersProvider.InputFormatters;
var formatters = context.ActionContext.InputFormatters;
foreach (var formatter in formatters)
{
var formatterMatched = formatter.CanRead(formatterContext);
if (formatterMatched)
if (formatter.CanRead(context))
{
return formatter;
}
}
var request = context.ActionContext.HttpContext.Request;
// TODO: https://github.com/aspnet/Mvc/issues/458
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"415: Unsupported content type {0}",

View File

@ -9,8 +9,8 @@ using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc
{
public interface IInputFormatterProvider
public interface IInputFormatterSelector
{
IInputFormatter GetInputFormatter(InputFormatterProviderContext context);
IInputFormatter SelectFormatter(InputFormatterContext context);
}
}

View File

@ -1,26 +0,0 @@
// 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.Http;
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc
{
public class InputFormatterProviderContext
{
public InputFormatterProviderContext([NotNull] ActionContext actionContext,
[NotNull] ModelMetadata metadata,
[NotNull] ModelStateDictionary modelState)
{
ActionContext = actionContext;
Metadata = metadata;
ModelState = modelState;
}
public ActionContext ActionContext { get; private set; }
public ModelMetadata Metadata { get; private set; }
public ModelStateDictionary ModelState { get; private set; }
}
}

View File

@ -59,7 +59,7 @@ namespace Microsoft.AspNet.Mvc
/// <summary>
/// Get a list of the <see cref="InputFormatterDescriptor" /> which are used to construct
/// a list of <see cref="IInputFormatter"/> by <see cref="IInputFormatterProvider"/>.
/// a list of <see cref="IInputFormatter"/> by <see cref="IInputFormattersProvider"/>.
/// </summary>
public List<InputFormatterDescriptor> InputFormatters { get; private set; }

View File

@ -12,14 +12,14 @@ namespace Microsoft.AspNet.Mvc
IModelMetadataProvider metadataProvider,
IModelBinder modelBinder,
IValueProvider valueProvider,
IInputFormatterProvider inputFormatterProvider,
IInputFormatterSelector inputFormatterSelector,
IEnumerable<IModelValidatorProvider> validatorProviders)
{
ActionContext = context;
MetadataProvider = metadataProvider;
ModelBinder = modelBinder;
ValueProvider = valueProvider;
InputFormatterProvider = inputFormatterProvider;
InputFormatterSelector = inputFormatterSelector;
ValidatorProviders = validatorProviders;
}
@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Mvc
public IValueProvider ValueProvider { get; private set; }
public IInputFormatterProvider InputFormatterProvider { get; private set; }
public IInputFormatterSelector InputFormatterSelector { get; private set; }
public IEnumerable<IModelValidatorProvider> ValidatorProviders { get; private set; }
}

View File

@ -13,20 +13,20 @@ namespace Microsoft.AspNet.Mvc
private readonly IModelMetadataProvider _modelMetadataProvider;
private readonly ICompositeModelBinder _compositeModelBinder;
private readonly IValueProviderFactory _compositeValueProviderFactory;
private readonly IInputFormatterProvider _inputFormatterProvider;
private readonly IInputFormatterSelector _inputFormatterSelector;
private readonly IEnumerable<IModelValidatorProvider> _validatorProviders;
private Tuple<ActionContext, ActionBindingContext> _bindingContext;
public DefaultActionBindingContextProvider(IModelMetadataProvider modelMetadataProvider,
ICompositeModelBinder compositeModelBinder,
ICompositeValueProviderFactory compositeValueProviderFactory,
IInputFormatterProvider inputFormatterProvider,
IInputFormatterSelector inputFormatterProvider,
IEnumerable<IModelValidatorProvider> validatorProviders)
{
_modelMetadataProvider = modelMetadataProvider;
_compositeModelBinder = compositeModelBinder;
_compositeValueProviderFactory = compositeValueProviderFactory;
_inputFormatterProvider = inputFormatterProvider;
_inputFormatterSelector = inputFormatterProvider;
_validatorProviders = validatorProviders;
}
@ -51,7 +51,7 @@ namespace Microsoft.AspNet.Mvc
_modelMetadataProvider,
_compositeModelBinder,
valueProvider,
_inputFormatterProvider,
_inputFormatterSelector,
_validatorProviders);
_bindingContext = new Tuple<ActionContext, ActionBindingContext>(actionContext, context);

View File

@ -2,6 +2,9 @@
// 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;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.Framework.DependencyInjection;
@ -12,16 +15,19 @@ namespace Microsoft.AspNet.Mvc
{
private readonly ReflectedActionDescriptor _descriptor;
private readonly IControllerFactory _controllerFactory;
private readonly IInputFormattersProvider _inputFormattersProvider;
public ReflectedActionInvoker([NotNull] ActionContext actionContext,
[NotNull] IActionBindingContextProvider bindingContextProvider,
[NotNull] INestedProviderManager<FilterProviderContext> filterProvider,
[NotNull] IControllerFactory controllerFactory,
[NotNull] ReflectedActionDescriptor descriptor)
[NotNull] ReflectedActionDescriptor descriptor,
[NotNull] IInputFormattersProvider inputFormattersProvider)
: base(actionContext, bindingContextProvider, filterProvider)
{
_descriptor = descriptor;
_controllerFactory = controllerFactory;
_inputFormattersProvider = inputFormattersProvider;
if (descriptor.MethodInfo == null)
{
throw new ArgumentException(
@ -34,9 +40,11 @@ namespace Microsoft.AspNet.Mvc
public override Task InvokeAsync()
{
ActionContext.Controller = _controllerFactory.CreateController(ActionContext);
ActionContext.InputFormatters = _inputFormattersProvider.InputFormatters
.ToList();
return base.InvokeAsync();
}
protected override async Task<IActionResult> InvokeActionAsync(ActionExecutingContext actionExecutingContext)
{
var actionMethodInfo = _descriptor.MethodInfo;
@ -81,4 +89,4 @@ namespace Microsoft.AspNet.Mvc
};
}
}
}
}

View File

@ -10,14 +10,17 @@ namespace Microsoft.AspNet.Mvc
{
private readonly IControllerFactory _controllerFactory;
private readonly IActionBindingContextProvider _bindingProvider;
private readonly IInputFormattersProvider _inputFormattersProvider;
private readonly INestedProviderManager<FilterProviderContext> _filterProvider;
public ReflectedActionInvokerProvider(IControllerFactory controllerFactory,
IActionBindingContextProvider bindingProvider,
IInputFormattersProvider inputFormattersProvider,
INestedProviderManager<FilterProviderContext> filterProvider)
{
_controllerFactory = controllerFactory;
_bindingProvider = bindingProvider;
_inputFormattersProvider = inputFormattersProvider;
_filterProvider = filterProvider;
}
@ -37,7 +40,8 @@ namespace Microsoft.AspNet.Mvc
_bindingProvider,
_filterProvider,
_controllerFactory,
actionDescriptor);
actionDescriptor,
_inputFormattersProvider);
}
callNext();

View File

@ -64,8 +64,8 @@ namespace Microsoft.AspNet.Mvc
yield return describe.Transient<IModelMetadataProvider, DataAnnotationsModelMetadataProvider>();
yield return describe.Scoped<IActionBindingContextProvider, DefaultActionBindingContextProvider>();
yield return describe.Transient<IInputFormatterProvider, TempInputFormatterProvider>();
yield return describe.Transient<IInputFormattersProvider, DefaultInputFormattersProvider>();
yield return describe.Transient<IInputFormatterSelector, DefaultInputFormatterSelector>();
yield return describe.Scoped<IInputFormattersProvider, DefaultInputFormattersProvider>();
yield return describe.Transient<IModelBinderProvider, DefaultModelBindersProvider>();
yield return describe.Scoped<ICompositeModelBinder, CompositeModelBinder>();

View File

@ -577,7 +577,7 @@ namespace Microsoft.AspNet.Mvc.Test
metadataProvider,
binder.Object,
valueProvider,
Mock.Of<IInputFormatterProvider>(),
Mock.Of<IInputFormatterSelector>(),
Enumerable.Empty<IModelValidatorProvider>());
var bindingContextProvider = new Mock<IActionBindingContextProvider>();
bindingContextProvider.Setup(b => b.GetActionBindingContextAsync(actionContext))
@ -618,7 +618,7 @@ namespace Microsoft.AspNet.Mvc.Test
metadataProvider,
binder.Object,
valueProvider,
Mock.Of<IInputFormatterProvider>(),
Mock.Of<IInputFormatterSelector>(),
Enumerable.Empty<IModelValidatorProvider>());
var bindingContextProvider = new Mock<IActionBindingContextProvider>();
bindingContextProvider.Setup(b => b.GetActionBindingContextAsync(actionContext))
@ -659,7 +659,7 @@ namespace Microsoft.AspNet.Mvc.Test
metadataProvider,
binder.Object,
Mock.Of<IValueProvider>(),
Mock.Of<IInputFormatterProvider>(),
Mock.Of<IInputFormatterSelector>(),
Enumerable.Empty<IModelValidatorProvider>());
var bindingContextProvider = new Mock<IActionBindingContextProvider>();
bindingContextProvider.Setup(b => b.GetActionBindingContextAsync(actionContext))

View File

@ -0,0 +1,70 @@
// 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.Routing;
using Microsoft.Framework.DependencyInjection;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test
{
public class DefaultInputFormatterSelectorTests
{
[Fact]
public void DefaultInputFormatterSelectorTests_ReturnsFirstFormatterWhichReturnsTrue()
{
// Arrange
var actionContext = GetActionContext();
var context = new InputFormatterContext(actionContext, typeof(int));
actionContext.InputFormatters = new List<IInputFormatter>()
{
new TestInputFormatter(false, 0),
new TestInputFormatter(false, 1),
new TestInputFormatter(true, 2),
new TestInputFormatter(true, 3)
};
var selector = new DefaultInputFormatterSelector();
// Act
var selectedFormatter = selector.SelectFormatter(context);
// Assert
var testFormatter = Assert.IsType<TestInputFormatter>(selectedFormatter);
Assert.Equal(2, testFormatter.Index);
}
private static ActionContext GetActionContext()
{
var httpContext = Mock.Of<HttpContext>();
return new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
}
private class TestInputFormatter : IInputFormatter
{
private bool _canRead = false;
public TestInputFormatter(bool canRead, int index)
{
_canRead = canRead;
Index = index;
}
public int Index { get; set; }
public bool CanRead(InputFormatterContext context)
{
return _canRead;
}
public Task<object> ReadAsync(InputFormatterContext context)
{
return Task.FromResult<object>(Index);
}
}
}
}

View File

@ -1329,13 +1329,16 @@ namespace Microsoft.AspNet.Mvc
.Setup(fp => fp.Invoke(It.IsAny<FilterProviderContext>()))
.Callback<FilterProviderContext>(
context => context.Results.AddRange(filters.Select(f => new FilterItem(null, f))));
var inputFormattersProvider = new Mock<IInputFormattersProvider>();
inputFormattersProvider.SetupGet(o => o.InputFormatters)
.Returns(new List<IInputFormatter>());
var invoker = new ReflectedActionInvoker(
actionContext,
actionBindingContextProvider.Object,
filterProvider.Object,
controllerFactory.Object,
actionDescriptor);
actionDescriptor,
inputFormattersProvider.Object);
return invoker;
}
@ -1366,18 +1369,21 @@ namespace Microsoft.AspNet.Mvc
Mock.Of<IModelMetadataProvider>(),
binder.Object,
Mock.Of<IValueProvider>(),
Mock.Of<IInputFormatterProvider>(),
Mock.Of<IInputFormatterSelector>(),
Enumerable.Empty<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 ReflectedActionInvoker(actionContext,
actionBindingContextProvider.Object,
Mock.Of<INestedProviderManager<FilterProviderContext>>(),
Mock.Of<IControllerFactory>(),
actionDescriptor);
actionDescriptor,
inputFormattersProvider.Object);
var modelStateDictionary = new ModelStateDictionary();
@ -1422,18 +1428,21 @@ namespace Microsoft.AspNet.Mvc
Mock.Of<IModelMetadataProvider>(),
binder.Object,
Mock.Of<IValueProvider>(),
Mock.Of<IInputFormatterProvider>(),
Mock.Of<IInputFormatterSelector>(),
Enumerable.Empty<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 ReflectedActionInvoker(actionContext,
actionBindingContextProvider.Object,
Mock.Of<INestedProviderManager<FilterProviderContext>>(),
Mock.Of<IControllerFactory>(),
actionDescriptor);
actionDescriptor,
inputFormattersProvider.Object);
var modelStateDictionary = new ModelStateDictionary();
@ -1479,7 +1488,7 @@ namespace Microsoft.AspNet.Mvc
Mock.Of<IModelMetadataProvider>(),
binder.Object,
Mock.Of<IValueProvider>(),
Mock.Of<IInputFormatterProvider>(),
Mock.Of<IInputFormatterSelector>(),
Enumerable.Empty<IModelValidatorProvider>());
var actionBindingContextProvider = new Mock<IActionBindingContextProvider>();
@ -1488,12 +1497,15 @@ namespace Microsoft.AspNet.Mvc
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 ReflectedActionInvoker(actionContext,
actionBindingContextProvider.Object,
Mock.Of<INestedProviderManager<FilterProviderContext>>(),
controllerFactory.Object,
actionDescriptor);
actionDescriptor,
inputFormattersProvider.Object);
// Act
await invoker.InvokeAsync();

View File

@ -92,7 +92,7 @@ namespace Microsoft.AspNet.Mvc.Core
provider,
Mock.Of<IModelBinder>(),
Mock.Of<IValueProvider>(),
Mock.Of<IInputFormatterProvider>(),
Mock.Of<IInputFormatterSelector>(),
Enumerable.Empty<IModelValidatorProvider>());
var urlHelper = Mock.Of<IUrlHelper>();
var actionBindingContextProvider = new Mock<IActionBindingContextProvider>();