Add tests for PageActionInvokerProvider (#5882)
This commit is contained in:
parent
7f3f6957be
commit
1c74e31715
|
|
@ -4,6 +4,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
{
|
{
|
||||||
public interface IPageLoader
|
public interface IPageLoader
|
||||||
{
|
{
|
||||||
Type Load(PageActionDescriptor actionDescriptor);
|
CompiledPageActionDescriptor Load(PageActionDescriptor actionDescriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||||
|
|
@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
public class DefaultPageLoader : IPageLoader
|
public class DefaultPageLoader : IPageLoader
|
||||||
{
|
{
|
||||||
|
private const string ModelPropertyName = "Model";
|
||||||
private readonly RazorCompilationService _razorCompilationService;
|
private readonly RazorCompilationService _razorCompilationService;
|
||||||
private readonly ICompilationService _compilationService;
|
private readonly ICompilationService _compilationService;
|
||||||
private readonly RazorProject _project;
|
private readonly RazorProject _project;
|
||||||
|
|
@ -33,22 +34,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Type Load(PageActionDescriptor actionDescriptor)
|
public CompiledPageActionDescriptor Load(PageActionDescriptor actionDescriptor)
|
||||||
{
|
{
|
||||||
var item = _project.GetItem(actionDescriptor.RelativePath);
|
var item = _project.GetItem(actionDescriptor.RelativePath);
|
||||||
if (!item.Exists)
|
if (!item.Exists)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"File {actionDescriptor.RelativePath} was not found.");
|
throw new InvalidOperationException($"File {actionDescriptor.RelativePath} was not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
RazorCodeDocument codeDocument;
|
|
||||||
RazorCSharpDocument cSharpDocument;
|
|
||||||
_logger.RazorFileToCodeCompilationStart(item.Path);
|
_logger.RazorFileToCodeCompilationStart(item.Path);
|
||||||
|
|
||||||
var startTimestamp = _logger.IsEnabled(LogLevel.Debug) ? Stopwatch.GetTimestamp() : 0;
|
var startTimestamp = _logger.IsEnabled(LogLevel.Debug) ? Stopwatch.GetTimestamp() : 0;
|
||||||
|
|
||||||
codeDocument = CreateCodeDocument(item);
|
var codeDocument = CreateCodeDocument(item);
|
||||||
cSharpDocument = _razorCompilationService.ProcessCodeDocument(codeDocument);
|
var cSharpDocument = _razorCompilationService.ProcessCodeDocument(codeDocument);
|
||||||
|
|
||||||
_logger.RazorFileToCodeCompilationEnd(item.Path, startTimestamp);
|
_logger.RazorFileToCodeCompilationEnd(item.Path, startTimestamp);
|
||||||
|
|
||||||
|
|
@ -63,7 +62,21 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
compilationResult.EnsureSuccessful();
|
compilationResult.EnsureSuccessful();
|
||||||
return compilationResult.CompiledType;
|
|
||||||
|
// If a model type wasn't set in code then the model property's type will be the same
|
||||||
|
// as the compiled type.
|
||||||
|
var pageType = compilationResult.CompiledType.GetTypeInfo();
|
||||||
|
var modelType = pageType.GetProperty(ModelPropertyName)?.PropertyType.GetTypeInfo();
|
||||||
|
if (modelType == pageType)
|
||||||
|
{
|
||||||
|
modelType = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CompiledPageActionDescriptor(actionDescriptor)
|
||||||
|
{
|
||||||
|
ModelTypeInfo = modelType,
|
||||||
|
PageTypeInfo = pageType,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private RazorCodeDocument CreateCodeDocument(RazorProjectItem item)
|
private RazorCodeDocument CreateCodeDocument(RazorProjectItem item)
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
public class PageActionInvokerProvider : IActionInvokerProvider
|
public class PageActionInvokerProvider : IActionInvokerProvider
|
||||||
{
|
{
|
||||||
|
private static readonly string[] _handlerMethodNames = new string[] { "OnGet", "OnPost" };
|
||||||
private const string PageStartFileName = "_PageStart.cshtml";
|
private const string PageStartFileName = "_PageStart.cshtml";
|
||||||
private const string ModelPropertyName = "Model";
|
|
||||||
private readonly IPageLoader _loader;
|
private readonly IPageLoader _loader;
|
||||||
private readonly IPageFactoryProvider _pageFactoryProvider;
|
private readonly IPageFactoryProvider _pageFactoryProvider;
|
||||||
private readonly IPageModelFactoryProvider _modelFactoryProvider;
|
private readonly IPageModelFactoryProvider _modelFactoryProvider;
|
||||||
|
|
@ -166,34 +166,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
FilterItem[] cachedFilters)
|
FilterItem[] cachedFilters)
|
||||||
{
|
{
|
||||||
var actionDescriptor = (PageActionDescriptor)context.ActionContext.ActionDescriptor;
|
var actionDescriptor = (PageActionDescriptor)context.ActionContext.ActionDescriptor;
|
||||||
var compiledType = _loader.Load(actionDescriptor).GetTypeInfo();
|
var compiledActionDescriptor = _loader.Load(actionDescriptor);
|
||||||
|
|
||||||
// If a model type wasn't set in code then the model property's type will be the same
|
|
||||||
// as the compiled type.
|
|
||||||
var modelType = compiledType.GetProperty(ModelPropertyName)?.PropertyType.GetTypeInfo();
|
|
||||||
if (modelType == compiledType)
|
|
||||||
{
|
|
||||||
modelType = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var compiledActionDescriptor = new CompiledPageActionDescriptor(actionDescriptor)
|
|
||||||
{
|
|
||||||
ModelTypeInfo = modelType,
|
|
||||||
PageTypeInfo = compiledType,
|
|
||||||
};
|
|
||||||
|
|
||||||
var pageFactory = _pageFactoryProvider.CreatePageFactory(compiledActionDescriptor);
|
var pageFactory = _pageFactoryProvider.CreatePageFactory(compiledActionDescriptor);
|
||||||
var pageDisposer = _pageFactoryProvider.CreatePageDisposer(compiledActionDescriptor);
|
var pageDisposer = _pageFactoryProvider.CreatePageDisposer(compiledActionDescriptor);
|
||||||
|
|
||||||
Func<PageContext, object> modelFactory = null;
|
Func<PageContext, object> modelFactory = null;
|
||||||
Action<PageContext, object> modelReleaser = null;
|
Action<PageContext, object> modelReleaser = null;
|
||||||
if (modelType == null)
|
if (compiledActionDescriptor.ModelTypeInfo == null)
|
||||||
{
|
{
|
||||||
PopulateHandlerMethodDescriptors(compiledType, compiledActionDescriptor);
|
PopulateHandlerMethodDescriptors(compiledActionDescriptor.PageTypeInfo, compiledActionDescriptor);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PopulateHandlerMethodDescriptors(modelType, compiledActionDescriptor);
|
PopulateHandlerMethodDescriptors(compiledActionDescriptor.ModelTypeInfo, compiledActionDescriptor);
|
||||||
|
|
||||||
modelFactory = _modelFactoryProvider.CreateModelFactory(compiledActionDescriptor);
|
modelFactory = _modelFactoryProvider.CreateModelFactory(compiledActionDescriptor);
|
||||||
modelReleaser = _modelFactoryProvider.CreateModelDisposer(compiledActionDescriptor);
|
modelReleaser = _modelFactoryProvider.CreateModelDisposer(compiledActionDescriptor);
|
||||||
|
|
@ -211,30 +197,34 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
cachedFilters);
|
cachedFilters);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Func<IRazorPage>> GetPageStartFactories(CompiledPageActionDescriptor descriptor)
|
// Internal for testing.
|
||||||
|
internal List<Func<IRazorPage>> GetPageStartFactories(CompiledPageActionDescriptor descriptor)
|
||||||
{
|
{
|
||||||
var pageStartFactories = new List<Func<IRazorPage>>();
|
var pageStartFactories = new List<Func<IRazorPage>>();
|
||||||
var pageStartItems = _razorProject.FindHierarchicalItems(descriptor.ViewEnginePath, PageStartFileName);
|
var pageStartItems = _razorProject.FindHierarchicalItems(descriptor.ViewEnginePath, PageStartFileName);
|
||||||
foreach (var item in pageStartItems)
|
foreach (var item in pageStartItems)
|
||||||
{
|
{
|
||||||
var factoryResult = _razorPageFactoryProvider.CreateFactory(item.Path);
|
if(item.Exists)
|
||||||
if (factoryResult.Success)
|
|
||||||
{
|
{
|
||||||
pageStartFactories.Insert(0, factoryResult.RazorPageFactory);
|
var factoryResult = _razorPageFactoryProvider.CreateFactory(item.Path);
|
||||||
|
if (factoryResult.Success)
|
||||||
|
{
|
||||||
|
pageStartFactories.Insert(0, factoryResult.RazorPageFactory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pageStartFactories;
|
return pageStartFactories;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PopulateHandlerMethodDescriptors(TypeInfo type, CompiledPageActionDescriptor actionDescriptor)
|
// Internal for testing.
|
||||||
|
internal static void PopulateHandlerMethodDescriptors(TypeInfo type, CompiledPageActionDescriptor actionDescriptor)
|
||||||
{
|
{
|
||||||
var methods = type.GetMethods();
|
for (var i = 0; i < _handlerMethodNames.Length; i++)
|
||||||
for (var i = 0; i < methods.Length; i++)
|
|
||||||
{
|
{
|
||||||
var method = methods[i];
|
var methodName = _handlerMethodNames[i];
|
||||||
if (method.Name.StartsWith("OnGet", StringComparison.Ordinal) ||
|
var method = type.GetMethod(methodName);
|
||||||
method.Name.StartsWith("OnPost", StringComparison.Ordinal))
|
if (method != null && !method.IsGenericMethod)
|
||||||
{
|
{
|
||||||
actionDescriptor.HandlerMethods.Add(new HandlerMethodDescriptor()
|
actionDescriptor.HandlerMethods.Add(new HandlerMethodDescriptor()
|
||||||
{
|
{
|
||||||
|
|
@ -245,7 +235,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InnerCache
|
internal class InnerCache
|
||||||
{
|
{
|
||||||
public InnerCache(int version)
|
public InnerCache(int version)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,31 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
Assert.Equal("Hello, handler!", content.Trim());
|
Assert.Equal("Hello, handler!", content.Trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task HelloWorldWithPageModelHandler_CanPostContent()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var getRequest = new HttpRequestMessage(HttpMethod.Get, "http://localhost/HelloWorldWithPageModelHandler?message=message");
|
||||||
|
var getResponse = await Client.SendAsync(getRequest);
|
||||||
|
var getResponseBody = await getResponse.Content.ReadAsStringAsync();
|
||||||
|
var formToken = AntiforgeryTestHelper.RetrieveAntiforgeryToken(getResponseBody, "/HelloWorlWithPageModelHandler");
|
||||||
|
var cookie = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getResponse);
|
||||||
|
|
||||||
|
|
||||||
|
var postRequest = new HttpRequestMessage(HttpMethod.Post, "http://localhost/HelloWorldWithPageModelHandler");
|
||||||
|
postRequest.Headers.Add("Cookie", cookie.Key + "=" + cookie.Value);
|
||||||
|
postRequest.Headers.Add("RequestVerificationToken", formToken);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await Client.SendAsync(postRequest);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.StartsWith("Hello, You posted!", content.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task HelloWorldWithPageModelHandler_CanGetContent()
|
public async Task HelloWorldWithPageModelHandler_CanGetContent()
|
||||||
{
|
{
|
||||||
|
|
@ -108,7 +133,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
var content = await response.Content.ReadAsStringAsync();
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
Assert.Equal("Hello, pagemodel!", content.Trim());
|
Assert.StartsWith("Hello, pagemodel!", content.Trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
|
@ -14,6 +16,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Razor.Evolution;
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.Extensions.FileProviders;
|
||||||
using Microsoft.Extensions.Logging.Testing;
|
using Microsoft.Extensions.Logging.Testing;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -37,7 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
var loader = new Mock<IPageLoader>();
|
var loader = new Mock<IPageLoader>();
|
||||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
.Returns(typeof(object));
|
.Returns(CreateCompiledPageActionDescriptor(descriptor));
|
||||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||||
|
|
@ -69,6 +72,61 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
Assert.Null(entry.ReleaseModel);
|
Assert.Null(entry.ReleaseModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OnProvidersExecuting_SetsHandlers()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = new PageActionDescriptor
|
||||||
|
{
|
||||||
|
RelativePath = "Path1",
|
||||||
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
|
};
|
||||||
|
Func<PageContext, object> factory = _ => null;
|
||||||
|
Action<PageContext, object> releaser = (_, __) => { };
|
||||||
|
|
||||||
|
var loader = new Mock<IPageLoader>();
|
||||||
|
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
|
.Returns(CreateCompiledPageActionDescriptor(descriptor, typeof(TestSetPageWithModel)));
|
||||||
|
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||||
|
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||||
|
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||||
|
var pageFactoryProvider = new Mock<IPageFactoryProvider>();
|
||||||
|
pageFactoryProvider.Setup(f => f.CreatePageFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
|
.Returns(factory);
|
||||||
|
pageFactoryProvider.Setup(f => f.CreatePageDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
|
.Returns(releaser);
|
||||||
|
|
||||||
|
var modelFactoryProvider = new Mock<IPageModelFactoryProvider>();
|
||||||
|
|
||||||
|
var invokerProvider = CreateInvokerProvider(
|
||||||
|
loader.Object,
|
||||||
|
actionDescriptorProvider.Object,
|
||||||
|
pageFactoryProvider.Object,
|
||||||
|
modelFactoryProvider.Object);
|
||||||
|
var context = new ActionInvokerProviderContext(
|
||||||
|
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
invokerProvider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(context.Result);
|
||||||
|
var actionInvoker = Assert.IsType<PageActionInvoker>(context.Result);
|
||||||
|
var entry = actionInvoker.CacheEntry;
|
||||||
|
|
||||||
|
Assert.Collection(entry.ActionDescriptor.HandlerMethods,
|
||||||
|
handlerDescriptor =>
|
||||||
|
{
|
||||||
|
Assert.Equal(nameof(TestSetPageModel.OnGet), handlerDescriptor.Method.Name);
|
||||||
|
Assert.NotNull(handlerDescriptor.Executor);
|
||||||
|
},
|
||||||
|
handlerDescriptor =>
|
||||||
|
{
|
||||||
|
Assert.Equal(nameof(TestSetPageModel.OnPost), handlerDescriptor.Method.Name);
|
||||||
|
Assert.NotNull(handlerDescriptor.Executor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void OnProvidersExecuting_WithModel_PopulatesCacheEntry()
|
public void OnProvidersExecuting_WithModel_PopulatesCacheEntry()
|
||||||
{
|
{
|
||||||
|
|
@ -85,7 +143,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
var loader = new Mock<IPageLoader>();
|
var loader = new Mock<IPageLoader>();
|
||||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
.Returns(typeof(PageWithModel));
|
.Returns(CreateCompiledPageActionDescriptor(descriptor, pageType: typeof(PageWithModel)));
|
||||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||||
|
|
@ -137,7 +195,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
var loader = new Mock<IPageLoader>();
|
var loader = new Mock<IPageLoader>();
|
||||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
.Returns(typeof(PageWithModel));
|
.Returns(CreateCompiledPageActionDescriptor(descriptor, pageType: typeof(PageWithModel)));
|
||||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||||
|
|
@ -184,7 +242,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
var loader = new Mock<IPageLoader>();
|
var loader = new Mock<IPageLoader>();
|
||||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
.Returns(typeof(PageWithModel));
|
.Returns(CreateCompiledPageActionDescriptor(descriptor, pageType: typeof(PageWithModel)));
|
||||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||||
|
|
@ -215,7 +273,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void OnProvidersExecuting_CachesEntries()
|
public void OnProvidersExecuting_CachesEntries()
|
||||||
{
|
{
|
||||||
|
|
@ -227,7 +284,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
};
|
};
|
||||||
var loader = new Mock<IPageLoader>();
|
var loader = new Mock<IPageLoader>();
|
||||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
.Returns(typeof(object));
|
.Returns(CreateCompiledPageActionDescriptor(descriptor));
|
||||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||||
|
|
@ -274,7 +331,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
var loader = new Mock<IPageLoader>();
|
var loader = new Mock<IPageLoader>();
|
||||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
.Returns(typeof(object));
|
.Returns(CreateCompiledPageActionDescriptor(descriptor));
|
||||||
var invokerProvider = CreateInvokerProvider(
|
var invokerProvider = CreateInvokerProvider(
|
||||||
loader.Object,
|
loader.Object,
|
||||||
actionDescriptorProvider.Object);
|
actionDescriptorProvider.Object);
|
||||||
|
|
@ -299,6 +356,229 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
Assert.NotSame(entry1, entry2);
|
Assert.NotSame(entry1, entry2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PopulateHandlerMethodDescriptors_DiscoversHandlersFromBaseType()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = new PageActionDescriptor()
|
||||||
|
{
|
||||||
|
RelativePath = "Path1",
|
||||||
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
|
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||||
|
};
|
||||||
|
|
||||||
|
var actionDescriptor = CreateCompiledPageActionDescriptor(descriptor, typeof(InheritsMethods));
|
||||||
|
|
||||||
|
var type = actionDescriptor.ModelTypeInfo ?? actionDescriptor.PageTypeInfo;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
PageActionInvokerProvider.PopulateHandlerMethodDescriptors(type, actionDescriptor);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(actionDescriptor.HandlerMethods,
|
||||||
|
(handler) =>
|
||||||
|
{
|
||||||
|
Assert.Equal("OnGet", handler.Method.Name);
|
||||||
|
Assert.Equal(typeof(InheritsMethods), handler.Method.DeclaringType);
|
||||||
|
},
|
||||||
|
(handler) =>
|
||||||
|
{
|
||||||
|
Assert.Equal("OnPost", handler.Method.Name);
|
||||||
|
Assert.Equal(typeof(TestSetPageModel), handler.Method.DeclaringType);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PopulateHandlerMethodDescriptors_ProtectedMethodsNotFound()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = new PageActionDescriptor()
|
||||||
|
{
|
||||||
|
RelativePath = "Path1",
|
||||||
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
|
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||||
|
};
|
||||||
|
|
||||||
|
var actionDescriptor = CreateCompiledPageActionDescriptor(descriptor, typeof(ProtectedModel));
|
||||||
|
|
||||||
|
var type = actionDescriptor.ModelTypeInfo ?? actionDescriptor.PageTypeInfo;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
PageActionInvokerProvider.PopulateHandlerMethodDescriptors(type, actionDescriptor);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(actionDescriptor.HandlerMethods);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PopulateHandlerMethodDescriptors_IgnoreGenericTypeParameters()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = new PageActionDescriptor()
|
||||||
|
{
|
||||||
|
RelativePath = "Path1",
|
||||||
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
|
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||||
|
};
|
||||||
|
|
||||||
|
var actionDescriptor = CreateCompiledPageActionDescriptor(descriptor, typeof(GenericClassModel));
|
||||||
|
|
||||||
|
var type = actionDescriptor.ModelTypeInfo ?? actionDescriptor.PageTypeInfo;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
PageActionInvokerProvider.PopulateHandlerMethodDescriptors(type, actionDescriptor);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(actionDescriptor.HandlerMethods);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PopulateHandlerMethodDescriptors_AllowOnlyOneMethod()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = new PageActionDescriptor()
|
||||||
|
{
|
||||||
|
RelativePath = "Path1",
|
||||||
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
|
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||||
|
};
|
||||||
|
|
||||||
|
var actionDescriptor = CreateCompiledPageActionDescriptor(descriptor, typeof(TestPageModel));
|
||||||
|
|
||||||
|
var type = actionDescriptor.ModelTypeInfo ?? actionDescriptor.PageTypeInfo;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
PageActionInvokerProvider.PopulateHandlerMethodDescriptors(type, actionDescriptor);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var handler = Assert.Single(actionDescriptor.HandlerMethods);
|
||||||
|
Assert.Equal("OnGet", handler.Method.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetPageStartFactories_FindsFullHeirarchy()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = new PageActionDescriptor()
|
||||||
|
{
|
||||||
|
RelativePath = "Path1",
|
||||||
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
|
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||||
|
};
|
||||||
|
var loader = new Mock<IPageLoader>();
|
||||||
|
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
|
.Returns(CreateCompiledPageActionDescriptor(descriptor, typeof(TestPageModel)));
|
||||||
|
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||||
|
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||||
|
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||||
|
|
||||||
|
var fileProvider = new TestFileProvider();
|
||||||
|
fileProvider.AddFile("/View/Deeper/Not_PageStart.cshtml", "page content");
|
||||||
|
fileProvider.AddFile("/View/Wrong/_PageStart.cshtml", "page content");
|
||||||
|
fileProvider.AddFile("/_PageStart.cshtml", "page content ");
|
||||||
|
fileProvider.AddFile("/Views/_PageStart.cshtml", "@page starts!");
|
||||||
|
fileProvider.AddFile("/Views/Deeper/_PageStart.cshtml", "page content");
|
||||||
|
|
||||||
|
var razorProject = CreateRazorProject(fileProvider);
|
||||||
|
|
||||||
|
var mock = new Mock<IRazorPageFactoryProvider>();
|
||||||
|
mock.Setup(p => p.CreateFactory("/Views/Deeper/_PageStart.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||||
|
.Verifiable();
|
||||||
|
mock.Setup(p => p.CreateFactory("/Views/_PageStart.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||||
|
.Verifiable();
|
||||||
|
mock.Setup(p => p.CreateFactory("/_PageStart.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||||
|
.Verifiable();
|
||||||
|
var razorPageFactoryProvider = mock.Object;
|
||||||
|
|
||||||
|
var invokerProvider = CreateInvokerProvider(
|
||||||
|
loader.Object,
|
||||||
|
actionDescriptorProvider.Object,
|
||||||
|
pageProvider: null,
|
||||||
|
modelProvider: null,
|
||||||
|
razorPageFactoryProvider: razorPageFactoryProvider,
|
||||||
|
razorProject: razorProject);
|
||||||
|
|
||||||
|
var compiledDescriptor = CreateCompiledPageActionDescriptor(descriptor);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var factories = invokerProvider.GetPageStartFactories(compiledDescriptor);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
mock.Verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetPageStartFactories_NoFactoriesForMissingFiles()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = new PageActionDescriptor()
|
||||||
|
{
|
||||||
|
RelativePath = "Path1",
|
||||||
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
|
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||||
|
};
|
||||||
|
var loader = new Mock<IPageLoader>();
|
||||||
|
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
|
.Returns(CreateCompiledPageActionDescriptor(descriptor, typeof(TestPageModel)));
|
||||||
|
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||||
|
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||||
|
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||||
|
|
||||||
|
// No files
|
||||||
|
var fileProvider = new TestFileProvider();
|
||||||
|
|
||||||
|
var razorProject = CreateRazorProject(fileProvider);
|
||||||
|
|
||||||
|
var invokerProvider = CreateInvokerProvider(
|
||||||
|
loader.Object,
|
||||||
|
actionDescriptorProvider.Object,
|
||||||
|
pageProvider: null,
|
||||||
|
modelProvider: null,
|
||||||
|
razorPageFactoryProvider: CreateRazorPageFactoryProvider(),
|
||||||
|
razorProject: razorProject);
|
||||||
|
|
||||||
|
var compiledDescriptor = CreateCompiledPageActionDescriptor(descriptor);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var factories = invokerProvider.GetPageStartFactories(compiledDescriptor);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(factories);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IRazorPageFactoryProvider CreateRazorPageFactoryProvider()
|
||||||
|
{
|
||||||
|
var mock = new Mock<IRazorPageFactoryProvider>();
|
||||||
|
mock.Setup(p => p.CreateFactory(It.IsAny<string>()))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()));
|
||||||
|
return mock.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RazorProject CreateRazorProject(IFileProvider fileProvider)
|
||||||
|
{
|
||||||
|
return new DefaultRazorProject(fileProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CompiledPageActionDescriptor CreateCompiledPageActionDescriptor(
|
||||||
|
PageActionDescriptor descriptor,
|
||||||
|
Type pageType = null)
|
||||||
|
{
|
||||||
|
TypeInfo modelTypeInfo = null;
|
||||||
|
if (pageType != null)
|
||||||
|
{
|
||||||
|
modelTypeInfo = pageType.GetTypeInfo().GetProperty("Model")?.PropertyType.GetTypeInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CompiledPageActionDescriptor(descriptor)
|
||||||
|
{
|
||||||
|
ModelTypeInfo = modelTypeInfo,
|
||||||
|
PageTypeInfo = (pageType ?? typeof(object)).GetTypeInfo()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static PageActionInvokerProvider CreateInvokerProvider(
|
private static PageActionInvokerProvider CreateInvokerProvider(
|
||||||
IPageLoader loader,
|
IPageLoader loader,
|
||||||
IActionDescriptorCollectionProvider actionDescriptorProvider,
|
IActionDescriptorCollectionProvider actionDescriptorProvider,
|
||||||
|
|
@ -334,6 +614,53 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class GenericClassModel
|
||||||
|
{
|
||||||
|
public void OnGet<T>()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestSetPageWithModel
|
||||||
|
{
|
||||||
|
public TestSetPageModel Model { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InheritsMethods : TestSetPageModel
|
||||||
|
{
|
||||||
|
public new void OnGet()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ProtectedModel
|
||||||
|
{
|
||||||
|
protected void OnGet()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPost()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestSetPageModel
|
||||||
|
{
|
||||||
|
public void OnGet()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPost()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class PageWithModel
|
private class PageWithModel
|
||||||
{
|
{
|
||||||
public TestPageModel Model { get; set; }
|
public TestPageModel Model { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -13,5 +13,10 @@ namespace RazorPagesWebSite
|
||||||
{
|
{
|
||||||
Message = message;
|
Message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnPost()
|
||||||
|
{
|
||||||
|
Message = "You posted!";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
@page
|
@page
|
||||||
@model RazorPagesWebSite.HelloWorldWithPageModelHandler
|
@model RazorPagesWebSite.HelloWorldWithPageModelHandler
|
||||||
|
|
||||||
Hello, @Model.Message!
|
Hello, @Model.Message!
|
||||||
|
|
||||||
|
@using (Html.BeginForm())
|
||||||
|
{
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue