diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/HandlerMethodDescriptor.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/HandlerMethodDescriptor.cs index b19700dad6..a4165252af 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/HandlerMethodDescriptor.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/HandlerMethodDescriptor.cs @@ -12,10 +12,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure { public MethodInfo Method { get; set; } - public Func> Executor { get; set; } + public Func> Executor { get; set; } public string HttpMethod { get; set; } public StringSegment FormAction { get; set; } + + public HandlerParameterDescriptor[] Parameters { get; set; } + + public bool OnPage { get; set; } } -} \ No newline at end of file +} diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/HandlerParameterDescriptor.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/HandlerParameterDescriptor.cs new file mode 100644 index 0000000000..a11410eeb5 --- /dev/null +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/HandlerParameterDescriptor.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation. 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.Reflection; +using Microsoft.AspNetCore.Mvc.Abstractions; + +namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure +{ + public class HandlerParameterDescriptor : ParameterDescriptor + { + public object DefaultValue { get; set; } + + public ParameterInfo Parameter { get; set; } + } +} diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/ExecutorFactory.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/ExecutorFactory.cs index 5aa4593d0d..8d99d701da 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/ExecutorFactory.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/ExecutorFactory.cs @@ -5,15 +5,17 @@ using System; using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.Extensions.Internal; namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal { public static class ExecutorFactory { - public static Func> CreateExecutor( + public static Func> CreateExecutor( CompiledPageActionDescriptor actionDescriptor, - MethodInfo method) + MethodInfo method, + HandlerParameterDescriptor[] parameters) { if (actionDescriptor == null) { @@ -25,48 +27,24 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal throw new ArgumentNullException(nameof(method)); } - var methodIsDeclaredOnPage = method.DeclaringType.GetTypeInfo().IsAssignableFrom(actionDescriptor.PageTypeInfo); - var handler = CreateHandlerMethod(method); - - return async (page, model) => + if (parameters == null) { - var arguments = new object[handler.Parameters.Length]; - for (var i = 0; i < handler.Parameters.Length; i++) - { - var parameter = handler.Parameters[i]; - arguments[i] = await page.Binder.BindModelAsync( - page.PageContext, - parameter.Type, - parameter.DefaultValue, - parameter.Name); - } + throw new ArgumentNullException(nameof(parameters)); + } - var receiver = methodIsDeclaredOnPage ? page : model; + var methodIsDeclaredOnPage = method.DeclaringType.GetTypeInfo().IsAssignableFrom(actionDescriptor.PageTypeInfo); + var handler = CreateHandlerMethod(method, parameters); + + return async (receiver, arguments) => + { var result = await handler.Execute(receiver, arguments); return result; }; } - private static HandlerMethod CreateHandlerMethod(MethodInfo method) + private static HandlerMethod CreateHandlerMethod(MethodInfo method, HandlerParameterDescriptor[] parameters) { var methodParameters = method.GetParameters(); - var parameters = new HandlerParameter[methodParameters.Length]; - - for (var i = 0; i < methodParameters.Length; i++) - { - var methodParameter = methodParameters[i]; - object defaultValue = null; - if (methodParameter.HasDefaultValue) - { - defaultValue = methodParameter.DefaultValue; - } - else if (methodParameter.ParameterType.GetTypeInfo().IsValueType) - { - defaultValue = Activator.CreateInstance(methodParameter.ParameterType); - } - - parameters[i] = new HandlerParameter(methodParameter.Name, methodParameter.ParameterType, defaultValue); - } var returnType = method.ReturnType; var returnTypeInfo = method.ReturnType.GetTypeInfo(); @@ -96,25 +74,25 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal private abstract class HandlerMethod { - protected static Expression[] Unpack(Expression arguments, HandlerParameter[] parameters) + protected static Expression[] Unpack(Expression arguments, HandlerParameterDescriptor[] parameters) { var unpackExpressions = new Expression[parameters.Length]; for (var i = 0; i < parameters.Length; i++) { unpackExpressions[i] = Expression.Convert( Expression.ArrayIndex(arguments, Expression.Constant(i)), - parameters[i].Type); + parameters[i].ParameterType); } return unpackExpressions; } - protected HandlerMethod(HandlerParameter[] parameters) + protected HandlerMethod(HandlerParameterDescriptor[] parameters) { Parameters = parameters; } - public HandlerParameter[] Parameters { get; } + public HandlerParameterDescriptor[] Parameters { get; } public abstract Task Execute(object receiver, object[] arguments); } @@ -123,7 +101,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal { private readonly Func _thunk; - public NonGenericTaskHandlerMethod(HandlerParameter[] parameters, MethodInfo method) + public NonGenericTaskHandlerMethod(HandlerParameterDescriptor[] parameters, MethodInfo method) : base(parameters) { var receiver = Expression.Parameter(typeof(object), "receiver"); @@ -153,7 +131,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal private readonly Func> _thunk; - public GenericTaskHandlerMethod(HandlerParameter[] parameters, MethodInfo method) + public GenericTaskHandlerMethod(HandlerParameterDescriptor[] parameters, MethodInfo method) : base(parameters) { var receiver = Expression.Parameter(typeof(object), "receiver"); @@ -189,7 +167,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal { private readonly Action _thunk; - public VoidHandlerMethod(HandlerParameter[] parameters, MethodInfo method) + public VoidHandlerMethod(HandlerParameterDescriptor[] parameters, MethodInfo method) : base(parameters) { var receiver = Expression.Parameter(typeof(object), "receiver"); @@ -215,7 +193,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal { private readonly Func _thunk; - public ActionResultHandlerMethod(HandlerParameter[] parameters, MethodInfo method) + public ActionResultHandlerMethod(HandlerParameterDescriptor[] parameters, MethodInfo method) : base(parameters) { var receiver = Expression.Parameter(typeof(object), "receiver"); @@ -237,21 +215,5 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal return Task.FromResult(_thunk(receiver, arguments)); } } - - private struct HandlerParameter - { - public HandlerParameter(string name, Type type, object defaultValue) - { - Name = name; - Type = type; - DefaultValue = defaultValue; - } - - public string Name { get; } - - public Type Type { get; } - - public object DefaultValue { get; } - } } -} \ No newline at end of file +} diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvoker.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvoker.cs index db1d9f64d5..104c53af00 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvoker.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvoker.cs @@ -22,6 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal { private readonly IPageHandlerMethodSelector _selector; private readonly PageContext _pageContext; + private readonly ParameterBinder _parameterBinder; private Page _page; private object _model; @@ -34,7 +35,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal PageContext pageContext, IFilterMetadata[] filterMetadata, IList valueProviderFactories, - PageActionInvokerCacheEntry cacheEntry) + PageActionInvokerCacheEntry cacheEntry, + ParameterBinder parameterBinder) : base( diagnosticSource, logger, @@ -45,6 +47,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal _selector = handlerMethodSelector; _pageContext = pageContext; CacheEntry = cacheEntry; + _parameterBinder = parameterBinder; } public PageActionInvokerCacheEntry CacheEntry { get; } @@ -374,8 +377,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var handler = _selector.Select(_pageContext); if (handler != null) { + var arguments = await GetArguments(handler); + var executor = handler.Executor; - result = await executor(_page, _model); + result = await executor(handler.OnPage ? _page : _model, arguments); } if (result == null) @@ -386,6 +391,27 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal await result.ExecuteResultAsync(_pageContext); } + private async Task GetArguments(HandlerMethodDescriptor handler) + { + var arguments = new object[handler.Parameters.Length]; + var valueProvider = await CompositeValueProvider.CreateAsync(_pageContext, _pageContext.ValueProviderFactories); + + for (var i = 0; i < handler.Parameters.Length; i++) + { + var parameter = handler.Parameters[i]; + + var result = await _parameterBinder.BindModelAsync( + _page.PageContext, + valueProvider, + parameter, + value: null); + + arguments[i] = result.IsModelSet ? result.Model : parameter.DefaultValue; + } + + return arguments; + } + private async Task InvokeNextExceptionFilterAsync() { try diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerCacheEntry.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerCacheEntry.cs index f1b712d90d..6e148ce7b1 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerCacheEntry.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerCacheEntry.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Razor; namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerProvider.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerProvider.cs index 3e9228daf0..6b4e12e057 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvokerProvider.cs @@ -15,7 +15,6 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal; using Microsoft.AspNetCore.Razor.Language; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -162,7 +161,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal pageContext, filters, new CopyOnWriteList(_valueProviderFactories), - cacheEntry); + cacheEntry, + _parameterBinder); } private PageActionInvokerCacheEntry CreateCacheEntry( @@ -264,18 +264,59 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal var formAction = new StringSegment(method.Name, formActionStart, formActionLength); + var parameters = GetHandlerParameters(method); + var handlerMethodDescriptor = new HandlerMethodDescriptor { Method = method, - Executor = ExecutorFactory.CreateExecutor(actionDescriptor, method), + Executor = ExecutorFactory.CreateExecutor(actionDescriptor, method, parameters), FormAction = formAction, HttpMethod = httpMethod, + Parameters = parameters, + OnPage = actionDescriptor.PageTypeInfo == type, }; actionDescriptor.HandlerMethods.Add(handlerMethodDescriptor); } } + private static HandlerParameterDescriptor[] GetHandlerParameters(MethodInfo methodInfo) + { + var methodParameters = methodInfo.GetParameters(); + var parameters = new HandlerParameterDescriptor[methodParameters.Length]; + + for (var i = 0; i < methodParameters.Length; i++) + { + var parameter = methodParameters[i]; + + parameters[i] = new HandlerParameterDescriptor() + { + BindingInfo = BindingInfo.GetBindingInfo(parameter.GetCustomAttributes()), + DefaultValue = GetDefaultValue(parameter), + Name = parameter.Name, + Parameter = parameter, + ParameterType = parameter.ParameterType, + }; + } + + return parameters; + } + + private static object GetDefaultValue(ParameterInfo methodParameter) + { + object defaultValue = null; + if (methodParameter.HasDefaultValue) + { + defaultValue = methodParameter.DefaultValue; + } + else if (methodParameter.ParameterType.GetTypeInfo().IsValueType) + { + defaultValue = Activator.CreateInstance(methodParameter.ParameterType); + } + + return defaultValue; + } + private static bool IsValidHandler(MethodInfo methodInfo) { // The SpecialName bit is set to flag members that are treated in a special way by some compilers diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs index 3b633918ad..f6c38e2600 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Page.cs @@ -27,7 +27,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages /// public abstract class Page : RazorPageBase, IRazorPage { - private PageArgumentBinder _binder; private IObjectModelValidator _objectValidator; private IModelMetadataProvider _metadataProvider; private IModelBinderFactory _modelBinderFactory; @@ -62,32 +61,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages /// public HttpResponse Response => HttpContext?.Response; - /// - /// Gets or sets the . - /// - public PageArgumentBinder Binder - { - get - { - if (_binder == null) - { - _binder = PageContext.HttpContext.RequestServices.GetRequiredService(); - } - - return _binder; - } - - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - _binder = value; - } - } - /// /// Gets the for the executing action. /// diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs index 6d1ea8370d..0438afc757 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/PageModel.cs @@ -16,7 +16,6 @@ using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Net.Http.Headers; @@ -25,38 +24,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages { public abstract class PageModel { - private PageArgumentBinder _binder; private IObjectModelValidator _objectValidator; private IModelMetadataProvider _metadataProvider; private IModelBinderFactory _modelBinderFactory; private IUrlHelper _urlHelper; - /// - /// Gets or sets the . - /// - public PageArgumentBinder Binder - { - get - { - if (_binder == null) - { - _binder = HttpContext?.RequestServices?.GetRequiredService(); - } - - return _binder; - } - - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - _binder = value; - } - } - /// /// Gets or sets the . /// diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/ExecutorFactoryTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/ExecutorFactoryTest.cs index ca1540cb63..fdd54c378b 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/ExecutorFactoryTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/ExecutorFactoryTest.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.Linq; using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.ModelBinding; @@ -25,7 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ActionResultReturningHandler)); // Act - var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); + var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]); // Assert Assert.NotNull(executor); @@ -45,7 +46,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ConcreteActionResult)); // Act - var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); + var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]); // Assert Assert.NotNull(executor); @@ -63,13 +64,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal PageTypeInfo = typeof(TestPage).GetTypeInfo(), }; var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ActionResultReturnHandlerWithParameters)); + var parameters = CreateParameters(methodInfo); // Act - var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); + var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, parameters); // Assert Assert.NotNull(executor); - var actionResultTask = executor(new TestPage(), null); + var actionResultTask = executor(new TestPage(), CreateArguments(methodInfo)); var actionResult = await actionResultTask; var contentResult = Assert.IsType(actionResult); Assert.Equal("Hello 0", contentResult.Content); @@ -87,7 +89,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.VoidReturningHandler)); // Act - var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); + var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]); // Assert Assert.NotNull(executor); @@ -109,7 +111,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.VoidTaskReturningHandler)); // Act - var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); + var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]); // Assert Assert.NotNull(executor); @@ -130,7 +132,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.GenericTaskHandler)); // Act - var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); + var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo)); // Assert Assert.NotNull(executor); @@ -150,11 +152,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.TaskReturningConcreteSubtype)); // Act - var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); + var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo)); // Assert Assert.NotNull(executor); - var actionResultTask = executor(new TestPage(), null); + var actionResultTask = executor(new TestPage(), CreateArguments(methodInfo)); var actionResult = await actionResultTask; var contentResult = Assert.IsType(actionResult); Assert.Equal("value", contentResult.Content); @@ -172,11 +174,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ActionResultReturningHandler)); // Act - var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); + var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]); // Assert Assert.NotNull(executor); - var actionResultTask = executor(new EmptyPage(), new TestPageModel()); + var actionResultTask = executor(new TestPageModel(), null); var actionResult = await actionResultTask; Assert.IsType(actionResult); } @@ -193,11 +195,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ConcreteActionResult)); // Act - var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); + var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo)); // Assert Assert.NotNull(executor); - var actionResultTask = executor(new EmptyPage(), new TestPageModel()); + var actionResultTask = executor(new TestPageModel(), null); var actionResult = await actionResultTask; Assert.IsType(actionResult); } @@ -214,11 +216,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ActionResultReturnHandlerWithParameters)); // Act - var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); + var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo)); // Assert Assert.NotNull(executor); - var actionResultTask = executor(new EmptyPage(), new TestPageModel()); + var actionResultTask = executor(new TestPageModel(), CreateArguments(methodInfo)); var actionResult = await actionResultTask; var contentResult = Assert.IsType(actionResult); Assert.Equal("Hello 0", contentResult.Content); @@ -237,11 +239,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.VoidReturningHandler)); // Act - var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); + var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]); // Assert Assert.NotNull(executor); - var actionResultTask = executor(new EmptyPage(), model); + var actionResultTask = executor(model, null); var actionResult = await actionResultTask; Assert.Null(actionResult); Assert.True(model.SideEffects); @@ -260,11 +262,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.VoidTaskReturningHandler)); // Act - var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); + var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]); // Assert Assert.NotNull(executor); - var actionResultTask = executor(new EmptyPage(), model); + var actionResultTask = executor(model, null); var actionResult = await actionResultTask; Assert.Null(actionResult); Assert.True(model.SideEffects); @@ -282,11 +284,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.GenericTaskHandler)); // Act - var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); + var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo)); // Assert Assert.NotNull(executor); - var actionResultTask = executor(new EmptyPage(), new TestPageModel()); + var actionResultTask = executor(new TestPageModel(), null); var actionResult = await actionResultTask; Assert.IsType(actionResult); } @@ -303,11 +305,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.TaskReturningConcreteSubtype)); // Act - var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); + var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo)); // Assert Assert.NotNull(executor); - var actionResultTask = executor(new EmptyPage(), new TestPageModel()); + var actionResultTask = executor(new TestPageModel(), CreateArguments(methodInfo)); var actionResult = await actionResultTask; var contentResult = Assert.IsType(actionResult); Assert.Equal("value", contentResult.Content); @@ -328,15 +330,50 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal var methodInfo = typeof(TestPageModel).GetMethod(methodName); // Act & Assert - var ex = Assert.Throws(() => ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo)); + var ex = Assert.Throws(() => + ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0])); Assert.Equal($"Unsupported handler method return type '{methodInfo.ReturnType}'.", ex.Message); } + private static object[] CreateArguments(MethodInfo methodInfo) + { + var parameters = methodInfo.GetParameters(); + + return parameters.Select(s => GetDefaultValue(s)).ToArray(); + } + + private static object GetDefaultValue(ParameterInfo methodParameter) + { + object defaultValue = null; + if (methodParameter.HasDefaultValue) + { + defaultValue = methodParameter.DefaultValue; + } + else if (methodParameter.ParameterType.GetTypeInfo().IsValueType) + { + defaultValue = Activator.CreateInstance(methodParameter.ParameterType); + } + + return defaultValue; + } + + private static HandlerParameterDescriptor[] CreateParameters(MethodInfo methodInfo) + { + var parameters = methodInfo.GetParameters(); + + return parameters.Select(p => new HandlerParameterDescriptor() + { + BindingInfo = BindingInfo.GetBindingInfo(p.GetCustomAttributes()), + Name = p.Name, + Parameter = p, + ParameterType = p.ParameterType, + }).ToArray(); + } + private class TestPage : Page { public TestPage() { - Binder = new MockBinder(); } public bool SideEffects { get; private set; } @@ -433,7 +470,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal { public EmptyPage() { - Binder = new MockBinder(); } public override Task ExecuteAsync() diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs index a86203f431..1273c54b63 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs @@ -11,11 +11,11 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -608,7 +608,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal null, null, new FilterItem[0]); - + var invoker = new PageActionInvoker( selector, diagnosticSource, @@ -616,10 +616,43 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal pageContext, filters, valueProviderFactories.AsReadOnly(), - cacheEntry); + cacheEntry, + GetParameterBinder()); return invoker; } + private static ParameterBinder GetParameterBinder( + IModelBinderFactory factory = null, + IObjectModelValidator validator = null) + { + if (validator == null) + { + validator = CreateMockValidator(); + } + + if (factory == null) + { + factory = TestModelBinderFactory.CreateDefault(); + } + + return new ParameterBinder( + TestModelMetadataProvider.CreateDefaultProvider(), + factory, + validator); + } + + private static IObjectModelValidator CreateMockValidator() + { + var mockValidator = new Mock(MockBehavior.Strict); + mockValidator + .Setup(o => o.Validate( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())); + return mockValidator.Object; + } + private class TestPageResultExecutor : PageResultExecutor { private readonly Func _executeAction;