Refactor PageRouteModel generation
This commit is contained in:
parent
66c13ae5e5
commit
094b61dfc6
|
|
@ -1,2 +1,2 @@
|
||||||
version:2.1.0-preview1-15661
|
version:2.1.0-preview1-15670
|
||||||
commithash:c9349d4c8a495d3085d9b879214d80f2f45e2193
|
commithash:49176144e03c3015d83b21e3f1d0ce093c05ecc3
|
||||||
|
|
|
||||||
|
|
@ -22,37 +22,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
private readonly RazorPagesOptions _pagesOptions;
|
private readonly RazorPagesOptions _pagesOptions;
|
||||||
private readonly RazorTemplateEngine _templateEngine;
|
private readonly RazorTemplateEngine _templateEngine;
|
||||||
private readonly ILogger<CompiledPageRouteModelProvider> _logger;
|
private readonly ILogger<CompiledPageRouteModelProvider> _logger;
|
||||||
|
private readonly PageRouteModelFactory _routeModelFactory;
|
||||||
|
|
||||||
public CompiledPageRouteModelProvider(
|
public CompiledPageRouteModelProvider(
|
||||||
ApplicationPartManager applicationManager,
|
ApplicationPartManager applicationManager,
|
||||||
IOptions<RazorPagesOptions> pagesOptionsAccessor,
|
IOptions<RazorPagesOptions> pagesOptionsAccessor,
|
||||||
RazorTemplateEngine templateEngine,
|
RazorTemplateEngine templateEngine,
|
||||||
ILoggerFactory loggerFactory)
|
ILogger<CompiledPageRouteModelProvider> logger)
|
||||||
{
|
{
|
||||||
if (applicationManager == null)
|
_applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
|
||||||
{
|
_pagesOptions = pagesOptionsAccessor?.Value ?? throw new ArgumentNullException(nameof(pagesOptionsAccessor));
|
||||||
throw new ArgumentNullException(nameof(applicationManager));
|
_templateEngine = templateEngine ?? throw new ArgumentNullException(nameof(templateEngine));
|
||||||
}
|
_logger = logger ?? throw new ArgumentNullException(nameof(templateEngine));
|
||||||
|
_routeModelFactory = new PageRouteModelFactory(_pagesOptions, _logger);
|
||||||
if (pagesOptionsAccessor == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(pagesOptionsAccessor));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (templateEngine == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(templateEngine));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loggerFactory == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(loggerFactory));
|
|
||||||
}
|
|
||||||
|
|
||||||
_applicationManager = applicationManager;
|
|
||||||
_pagesOptions = pagesOptionsAccessor.Value;
|
|
||||||
_templateEngine = templateEngine;
|
|
||||||
_logger = loggerFactory.CreateLogger<CompiledPageRouteModelProvider>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Order => -1000;
|
public int Order => -1000;
|
||||||
|
|
@ -64,7 +46,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateModels(context.RouteModels);
|
CreateModels(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnProvidersExecuted(PageRouteModelProviderContext context)
|
public void OnProvidersExecuted(PageRouteModelProviderContext context)
|
||||||
|
|
@ -75,7 +57,25 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateModels(IList<PageRouteModel> results)
|
/// <summary>
|
||||||
|
/// Gets the sequence of <see cref="CompiledViewDescriptor"/> from <paramref name="applicationManager"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="applicationManager">The <see cref="ApplicationPartManager"/>s</param>
|
||||||
|
/// <returns>The sequence of <see cref="CompiledViewDescriptor"/>.</returns>
|
||||||
|
protected virtual IEnumerable<CompiledViewDescriptor> GetViewDescriptors(ApplicationPartManager applicationManager)
|
||||||
|
{
|
||||||
|
if (applicationManager == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(applicationManager));
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewsFeature = new ViewsFeature();
|
||||||
|
applicationManager.PopulateFeature(viewsFeature);
|
||||||
|
|
||||||
|
return viewsFeature.ViewDescriptors.Where(d => d.IsPrecompiled && d.ViewAttribute is RazorPageAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateModels(PageRouteModelProviderContext context)
|
||||||
{
|
{
|
||||||
var rootDirectory = _pagesOptions.RootDirectory;
|
var rootDirectory = _pagesOptions.RootDirectory;
|
||||||
if (!rootDirectory.EndsWith("/", StringComparison.Ordinal))
|
if (!rootDirectory.EndsWith("/", StringComparison.Ordinal))
|
||||||
|
|
@ -97,84 +97,25 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
PageRouteModel model = null;
|
var pageAttribute = (RazorPageAttribute)viewDescriptor.ViewAttribute;
|
||||||
|
PageRouteModel routeModel = null;
|
||||||
|
|
||||||
// When RootDirectory and AreaRootDirectory overlap (e.g. RootDirectory = '/', AreaRootDirectory = '/Areas'), we
|
// When RootDirectory and AreaRootDirectory overlap (e.g. RootDirectory = '/', AreaRootDirectory = '/Areas'), we
|
||||||
// only want to allow a page to be associated with the area route.
|
// only want to allow a page to be associated with the area route.
|
||||||
if (_pagesOptions.AllowAreas && viewDescriptor.RelativePath.StartsWith(areaRootDirectory, StringComparison.OrdinalIgnoreCase))
|
if (_pagesOptions.AllowAreas && viewDescriptor.RelativePath.StartsWith(areaRootDirectory, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
model = GetAreaPageRouteModel(areaRootDirectory, viewDescriptor);
|
routeModel = _routeModelFactory.CreateAreaRouteModel(viewDescriptor.RelativePath, pageAttribute.RouteTemplate);
|
||||||
}
|
}
|
||||||
else if (viewDescriptor.RelativePath.StartsWith(rootDirectory, StringComparison.OrdinalIgnoreCase))
|
else if (viewDescriptor.RelativePath.StartsWith(rootDirectory, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
model = GetPageRouteModel(rootDirectory, viewDescriptor);
|
routeModel = _routeModelFactory.CreateRouteModel(pageAttribute.Path, pageAttribute.RouteTemplate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model != null)
|
if (routeModel != null)
|
||||||
{
|
{
|
||||||
results.Add(model);
|
context.RouteModels.Add(routeModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PageRouteModel GetPageRouteModel(string rootDirectory, CompiledViewDescriptor viewDescriptor)
|
|
||||||
{
|
|
||||||
var viewEnginePath = GetRootTrimmedPath(rootDirectory, viewDescriptor.RelativePath);
|
|
||||||
if (viewEnginePath.EndsWith(RazorViewEngine.ViewExtension, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
viewEnginePath = viewEnginePath.Substring(0, viewEnginePath.Length - RazorViewEngine.ViewExtension.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
var model = new PageRouteModel(viewDescriptor.RelativePath, viewEnginePath);
|
|
||||||
var pageAttribute = (RazorPageAttribute)viewDescriptor.ViewAttribute;
|
|
||||||
PageSelectorModel.PopulateDefaults(model, viewEnginePath, pageAttribute.RouteTemplate);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PageRouteModel GetAreaPageRouteModel(string areaRootDirectory, CompiledViewDescriptor viewDescriptor)
|
|
||||||
{
|
|
||||||
var rootTrimmedPath = GetRootTrimmedPath(areaRootDirectory, viewDescriptor.RelativePath);
|
|
||||||
|
|
||||||
if (PageSelectorModel.TryParseAreaPath(_pagesOptions, rootTrimmedPath, _logger, out var result))
|
|
||||||
{
|
|
||||||
var model = new PageRouteModel(viewDescriptor.RelativePath, result.viewEnginePath)
|
|
||||||
{
|
|
||||||
RouteValues = { ["area"] = result.areaName },
|
|
||||||
};
|
|
||||||
var pageAttribute = (RazorPageAttribute)viewDescriptor.ViewAttribute;
|
|
||||||
PageSelectorModel.PopulateDefaults(model, result.pageRoute, pageAttribute.RouteTemplate);
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We were unable to parse the path to match the format we expect /Areas/AreaName/Pages/PagePath.cshtml
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the sequence of <see cref="CompiledViewDescriptor"/> from <paramref name="applicationManager"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="applicationManager">The <see cref="ApplicationPartManager"/>s</param>
|
|
||||||
/// <returns>The sequence of <see cref="CompiledViewDescriptor"/>.</returns>
|
|
||||||
protected virtual IEnumerable<CompiledViewDescriptor> GetViewDescriptors(ApplicationPartManager applicationManager)
|
|
||||||
{
|
|
||||||
if (applicationManager == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(applicationManager));
|
|
||||||
}
|
|
||||||
|
|
||||||
var viewsFeature = new ViewsFeature();
|
|
||||||
applicationManager.PopulateFeature(viewsFeature);
|
|
||||||
|
|
||||||
return viewsFeature.ViewDescriptors.Where(d => d.IsPrecompiled && d.ViewAttribute is RazorPageAttribute);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetRootTrimmedPath(string rootDirectory, string path)
|
|
||||||
{
|
|
||||||
// rootDirectory = "/Pages/AllMyPages/"
|
|
||||||
// path = "/Pages/AllMyPages/Home.cshtml"
|
|
||||||
// Result = "/Home.cshtml"
|
|
||||||
var startIndex = rootDirectory.Length - 1;
|
|
||||||
return path.Substring(startIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,196 @@
|
||||||
|
// 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.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Razor;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
{
|
||||||
|
internal class PageRouteModelFactory
|
||||||
|
{
|
||||||
|
private static readonly string IndexFileName = "Index" + RazorViewEngine.ViewExtension;
|
||||||
|
private readonly RazorPagesOptions _options;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly string _normalizedRootDirectory;
|
||||||
|
private readonly string _normalizedAreaRootDirectory;
|
||||||
|
|
||||||
|
public PageRouteModelFactory(
|
||||||
|
RazorPagesOptions options,
|
||||||
|
ILogger logger)
|
||||||
|
{
|
||||||
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
|
_normalizedRootDirectory = NormalizeDirectory(options.RootDirectory);
|
||||||
|
_normalizedAreaRootDirectory = NormalizeDirectory(options.AreaRootDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageRouteModel CreateRouteModel(string relativePath, string routeTemplate)
|
||||||
|
{
|
||||||
|
var viewEnginePath = GetViewEnginePath(_normalizedRootDirectory, relativePath);
|
||||||
|
var routeModel = new PageRouteModel(relativePath, viewEnginePath);
|
||||||
|
|
||||||
|
PopulateRouteModel(routeModel, viewEnginePath, routeTemplate);
|
||||||
|
|
||||||
|
return routeModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PageRouteModel CreateAreaRouteModel(string relativePath, string routeTemplate)
|
||||||
|
{
|
||||||
|
if (!TryParseAreaPath(relativePath, out var areaResult))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var routeModel = new PageRouteModel(relativePath, areaResult.viewEnginePath);
|
||||||
|
|
||||||
|
var routePrefix = CreateAreaRoute(areaResult.areaName, areaResult.viewEnginePath);
|
||||||
|
PopulateRouteModel(routeModel, routePrefix, routeTemplate);
|
||||||
|
routeModel.RouteValues["area"] = areaResult.areaName;
|
||||||
|
|
||||||
|
return routeModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PopulateRouteModel(PageRouteModel model, string pageRoute, string routeTemplate)
|
||||||
|
{
|
||||||
|
model.RouteValues.Add("page", model.ViewEnginePath);
|
||||||
|
|
||||||
|
if (AttributeRouteModel.IsOverridePattern(routeTemplate))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(string.Format(
|
||||||
|
Resources.PageActionDescriptorProvider_RouteTemplateCannotBeOverrideable,
|
||||||
|
model.RelativePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectorModel = CreateSelectorModel(pageRoute, routeTemplate);
|
||||||
|
model.Selectors.Add(selectorModel);
|
||||||
|
|
||||||
|
var fileName = Path.GetFileName(model.RelativePath);
|
||||||
|
if (string.Equals(IndexFileName, fileName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// For pages ending in /Index.cshtml, we want to allow incoming routing, but
|
||||||
|
// force outgoing routes to match to the path sans /Index.
|
||||||
|
selectorModel.AttributeRouteModel.SuppressLinkGeneration = true;
|
||||||
|
|
||||||
|
var index = pageRoute.LastIndexOf('/');
|
||||||
|
var parentDirectoryPath = index == -1 ?
|
||||||
|
string.Empty :
|
||||||
|
pageRoute.Substring(0, index);
|
||||||
|
model.Selectors.Add(CreateSelectorModel(parentDirectoryPath, routeTemplate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal for unit testing
|
||||||
|
internal bool TryParseAreaPath(
|
||||||
|
string relativePath,
|
||||||
|
out (string areaName, string viewEnginePath) result)
|
||||||
|
{
|
||||||
|
// path = "/Areas/Products/Pages/Manage/Home.cshtml"
|
||||||
|
// Result ("Products", "/Manage/Home")
|
||||||
|
|
||||||
|
result = default;
|
||||||
|
Debug.Assert(relativePath.StartsWith("/", StringComparison.Ordinal));
|
||||||
|
// Parse the area root directory.
|
||||||
|
var areaRootEndIndex = relativePath.IndexOf('/', startIndex: 1);
|
||||||
|
if (areaRootEndIndex == -1 ||
|
||||||
|
areaRootEndIndex >= relativePath.Length - 1 || // There's at least one token after the area root.
|
||||||
|
!relativePath.StartsWith(_normalizedAreaRootDirectory, StringComparison.OrdinalIgnoreCase)) // The path must start with area root.
|
||||||
|
{
|
||||||
|
_logger.UnsupportedAreaPath(_options, relativePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first directory that follows the area root is the area name.
|
||||||
|
var areaEndIndex = relativePath.IndexOf('/', startIndex: areaRootEndIndex + 1);
|
||||||
|
if (areaEndIndex == -1 || areaEndIndex == relativePath.Length)
|
||||||
|
{
|
||||||
|
_logger.UnsupportedAreaPath(_options, relativePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var areaName = relativePath.Substring(areaRootEndIndex + 1, areaEndIndex - areaRootEndIndex - 1);
|
||||||
|
|
||||||
|
string viewEnginePath;
|
||||||
|
if (_options.RootDirectory == "/")
|
||||||
|
{
|
||||||
|
// When RootDirectory is "/", every thing past the area name is the page path.
|
||||||
|
Debug.Assert(relativePath.EndsWith(RazorViewEngine.ViewExtension), $"{relativePath} does not end in extension '{RazorViewEngine.ViewExtension}'.");
|
||||||
|
viewEnginePath = relativePath.Substring(areaEndIndex, relativePath.Length - areaEndIndex - RazorViewEngine.ViewExtension.Length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Normalize the pages root directory so that it has a trailing slash. This ensures we're looking at a directory delimiter
|
||||||
|
// and not just the area name occuring as part of a segment.
|
||||||
|
Debug.Assert(_options.RootDirectory.StartsWith("/", StringComparison.Ordinal));
|
||||||
|
// If the pages root has a value i.e. it's not the app root "/", ensure that the area path contains this value.
|
||||||
|
if (string.Compare(relativePath, areaEndIndex, _normalizedRootDirectory, 0, _normalizedRootDirectory.Length, StringComparison.OrdinalIgnoreCase) != 0)
|
||||||
|
{
|
||||||
|
_logger.UnsupportedAreaPath(_options, relativePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include the trailing slash of the root directory at the start of the viewEnginePath
|
||||||
|
var pageNameIndex = areaEndIndex + _normalizedRootDirectory.Length - 1;
|
||||||
|
viewEnginePath = relativePath.Substring(pageNameIndex, relativePath.Length - pageNameIndex - RazorViewEngine.ViewExtension.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = (areaName, viewEnginePath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetViewEnginePath(string rootDirectory, string path)
|
||||||
|
{
|
||||||
|
// rootDirectory = "/Pages/AllMyPages/"
|
||||||
|
// path = "/Pages/AllMyPages/Home.cshtml"
|
||||||
|
// Result = "/Home"
|
||||||
|
Debug.Assert(path.StartsWith(rootDirectory, StringComparison.OrdinalIgnoreCase));
|
||||||
|
Debug.Assert(path.EndsWith(RazorViewEngine.ViewExtension, StringComparison.OrdinalIgnoreCase));
|
||||||
|
var startIndex = rootDirectory.Length - 1;
|
||||||
|
var endIndex = path.Length - RazorViewEngine.ViewExtension.Length;
|
||||||
|
return path.Substring(startIndex, endIndex - startIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CreateAreaRoute(string areaName, string viewEnginePath)
|
||||||
|
{
|
||||||
|
// AreaName = Products, ViewEnginePath = /List/Categories
|
||||||
|
// Result = /Products/List/Categories
|
||||||
|
Debug.Assert(!string.IsNullOrEmpty(areaName));
|
||||||
|
Debug.Assert(!string.IsNullOrEmpty(viewEnginePath));
|
||||||
|
Debug.Assert(viewEnginePath.StartsWith("/", StringComparison.Ordinal));
|
||||||
|
|
||||||
|
var builder = new InplaceStringBuilder(1 + areaName.Length + viewEnginePath.Length);
|
||||||
|
builder.Append('/');
|
||||||
|
builder.Append(areaName);
|
||||||
|
builder.Append(viewEnginePath);
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SelectorModel CreateSelectorModel(string prefix, string routeTemplate)
|
||||||
|
{
|
||||||
|
return new SelectorModel
|
||||||
|
{
|
||||||
|
AttributeRouteModel = new AttributeRouteModel
|
||||||
|
{
|
||||||
|
Template = AttributeRouteModel.CombineTemplates(prefix, routeTemplate),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeDirectory(string directory)
|
||||||
|
{
|
||||||
|
Debug.Assert(directory.StartsWith("/", StringComparison.Ordinal));
|
||||||
|
if (directory.Length > 1 && !directory.EndsWith("/", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return directory + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
// 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.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Razor;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Primitives;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|
||||||
{
|
|
||||||
public static class PageSelectorModel
|
|
||||||
{
|
|
||||||
private static readonly string IndexFileName = "Index" + RazorViewEngine.ViewExtension;
|
|
||||||
|
|
||||||
public static void PopulateDefaults(PageRouteModel model, string pageRoute, string routeTemplate)
|
|
||||||
{
|
|
||||||
model.RouteValues.Add("page", model.ViewEnginePath);
|
|
||||||
|
|
||||||
if (AttributeRouteModel.IsOverridePattern(routeTemplate))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(string.Format(
|
|
||||||
Resources.PageActionDescriptorProvider_RouteTemplateCannotBeOverrideable,
|
|
||||||
model.RelativePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
var selectorModel = CreateSelectorModel(pageRoute, routeTemplate);
|
|
||||||
model.Selectors.Add(selectorModel);
|
|
||||||
|
|
||||||
var fileName = Path.GetFileName(model.RelativePath);
|
|
||||||
if (string.Equals(IndexFileName, fileName, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
// For pages ending in /Index.cshtml, we want to allow incoming routing, but
|
|
||||||
// force outgoing routes to match to the path sans /Index.
|
|
||||||
selectorModel.AttributeRouteModel.SuppressLinkGeneration = true;
|
|
||||||
|
|
||||||
var index = pageRoute.LastIndexOf('/');
|
|
||||||
var parentDirectoryPath = index == -1 ?
|
|
||||||
string.Empty :
|
|
||||||
pageRoute.Substring(0, index);
|
|
||||||
model.Selectors.Add(CreateSelectorModel(parentDirectoryPath, routeTemplate));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool TryParseAreaPath(
|
|
||||||
RazorPagesOptions razorPagesOptions,
|
|
||||||
string path,
|
|
||||||
ILogger logger,
|
|
||||||
out (string areaName, string viewEnginePath, string pageRoute) result)
|
|
||||||
{
|
|
||||||
// path = "/Products/Pages/Manage/Home.cshtml"
|
|
||||||
// Result = ("Products", "/Manage/Home", "/Products/Manage/Home")
|
|
||||||
|
|
||||||
result = default;
|
|
||||||
Debug.Assert(path.StartsWith("/", StringComparison.Ordinal));
|
|
||||||
|
|
||||||
// 1. Parse the area name. This will be the first token we encounter.
|
|
||||||
var areaEndIndex = path.IndexOf('/', startIndex: 1);
|
|
||||||
if (areaEndIndex == -1 || areaEndIndex == path.Length)
|
|
||||||
{
|
|
||||||
logger.UnsupportedAreaPath(razorPagesOptions, path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var areaName = path.Substring(1, areaEndIndex - 1);
|
|
||||||
|
|
||||||
string pageName;
|
|
||||||
if (razorPagesOptions.RootDirectory == "/")
|
|
||||||
{
|
|
||||||
// When RootDirectory is "/", every thing past the area name is the page path.
|
|
||||||
Debug.Assert(path.EndsWith(RazorViewEngine.ViewExtension), $"{path} does not end in extension '{RazorViewEngine.ViewExtension}'.");
|
|
||||||
pageName = path.Substring(areaEndIndex, path.Length - areaEndIndex - RazorViewEngine.ViewExtension.Length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Normalize the pages root directory so that it has a trailing slash. This ensures we're looking at a directory delimiter
|
|
||||||
// and not just the area name occuring as part of a segment.
|
|
||||||
Debug.Assert(razorPagesOptions.RootDirectory.StartsWith("/", StringComparison.Ordinal));
|
|
||||||
var normalizedPagesRootDirectory = razorPagesOptions.RootDirectory.Substring(1);
|
|
||||||
if (!normalizedPagesRootDirectory.EndsWith("/", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
normalizedPagesRootDirectory += "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Assert(normalizedPagesRootDirectory.Length > 0);
|
|
||||||
// If the pages root has a value i.e. it's not the app root "/", ensure that the area path contains this value.
|
|
||||||
if (string.Compare(path, areaEndIndex + 1, normalizedPagesRootDirectory, 0, normalizedPagesRootDirectory.Length, StringComparison.OrdinalIgnoreCase) != 0)
|
|
||||||
{
|
|
||||||
logger.UnsupportedAreaPath(razorPagesOptions, path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pageNameIndex = areaEndIndex + normalizedPagesRootDirectory.Length;
|
|
||||||
pageName = path.Substring(pageNameIndex, path.Length - pageNameIndex - RazorViewEngine.ViewExtension.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
var builder = new InplaceStringBuilder(areaEndIndex + pageName.Length);
|
|
||||||
builder.Append(path, 0, areaEndIndex);
|
|
||||||
builder.Append(pageName);
|
|
||||||
var pageRoute = builder.ToString();
|
|
||||||
|
|
||||||
result = (areaName, pageName, pageRoute);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SelectorModel CreateSelectorModel(string prefix, string template)
|
|
||||||
{
|
|
||||||
return new SelectorModel
|
|
||||||
{
|
|
||||||
AttributeRouteModel = new AttributeRouteModel
|
|
||||||
{
|
|
||||||
Template = AttributeRouteModel.CombineTemplates(prefix, template),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// 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.Linq;
|
||||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
|
@ -14,7 +15,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
private readonly RazorProject _project;
|
private readonly RazorProject _project;
|
||||||
private readonly RazorPagesOptions _pagesOptions;
|
private readonly RazorPagesOptions _pagesOptions;
|
||||||
private readonly ILogger _logger;
|
private readonly PageRouteModelFactory _routeModelFactory;
|
||||||
|
private readonly ILogger<RazorProjectPageRouteModelProvider> _logger;
|
||||||
|
|
||||||
public RazorProjectPageRouteModelProvider(
|
public RazorProjectPageRouteModelProvider(
|
||||||
RazorProject razorProject,
|
RazorProject razorProject,
|
||||||
|
|
@ -24,12 +26,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
_project = razorProject;
|
_project = razorProject;
|
||||||
_pagesOptions = pagesOptionsAccessor.Value;
|
_pagesOptions = pagesOptionsAccessor.Value;
|
||||||
_logger = loggerFactory.CreateLogger<RazorProjectPageRouteModelProvider>();
|
_logger = loggerFactory.CreateLogger<RazorProjectPageRouteModelProvider>();
|
||||||
|
_routeModelFactory = new PageRouteModelFactory(_pagesOptions, _logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Ordered to execute after <see cref="CompiledPageRouteModelProvider"/>.
|
/// Ordered to execute after <see cref="CompiledPageRouteModelProvider"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public int Order => -1000 + 10;
|
public int Order => -1000 + 10;
|
||||||
|
|
||||||
public void OnProvidersExecuted(PageRouteModelProviderContext context)
|
public void OnProvidersExecuted(PageRouteModelProviderContext context)
|
||||||
{
|
{
|
||||||
|
|
@ -40,6 +43,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
// When RootDirectory and AreaRootDirectory overlap, e.g. RootDirectory = /, AreaRootDirectoryy = /Areas;
|
// When RootDirectory and AreaRootDirectory overlap, e.g. RootDirectory = /, AreaRootDirectoryy = /Areas;
|
||||||
// we need to ensure that the page is only route-able via the area route. By adding area routes first,
|
// we need to ensure that the page is only route-able via the area route. By adding area routes first,
|
||||||
// we'll ensure non area routes get skipped when it encounters an IsAlreadyRegistered check.
|
// we'll ensure non area routes get skipped when it encounters an IsAlreadyRegistered check.
|
||||||
|
|
||||||
if (_pagesOptions.AllowAreas)
|
if (_pagesOptions.AllowAreas)
|
||||||
{
|
{
|
||||||
AddAreaPageModels(context);
|
AddAreaPageModels(context);
|
||||||
|
|
@ -64,10 +68,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
var relativePath = item.CombinedPath;
|
var relativePath = item.CombinedPath;
|
||||||
if (IsAlreadyRegistered(context, relativePath))
|
if (context.RouteModels.Any(m => string.Equals(relativePath, m.RelativePath, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
// A route for this file was already registered either by the CompiledPageRouteModel or as an area route.
|
// A route for this file was already registered either by the CompiledPageRouteModel or as an area route.
|
||||||
// by this provider. Skip registering an additional entry.
|
// by this provider. Skip registering an additional entry.
|
||||||
|
|
||||||
|
// Note: We're comparing duplicates based on root-relative paths. This eliminates a page from being discovered
|
||||||
|
// by overlapping area and non-area routes where ViewEnginePath would be different.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,9 +92,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var routeModel = new PageRouteModel(relativePath, viewEnginePath: item.FilePathWithoutExtension);
|
var routeModel = _routeModelFactory.CreateRouteModel(relativePath, routeTemplate);
|
||||||
PageSelectorModel.PopulateDefaults(routeModel, routeModel.ViewEnginePath, routeTemplate);
|
if (routeModel != null)
|
||||||
context.RouteModels.Add(routeModel);
|
{
|
||||||
|
context.RouteModels.Add(routeModel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,7 +110,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
var relativePath = item.CombinedPath;
|
var relativePath = item.CombinedPath;
|
||||||
if (IsAlreadyRegistered(context, relativePath))
|
if (context.RouteModels.Any(m => string.Equals(relativePath, m.RelativePath, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
// A route for this file was already registered either by the CompiledPageRouteModel.
|
// A route for this file was already registered either by the CompiledPageRouteModel.
|
||||||
// Skip registering an additional entry.
|
// Skip registering an additional entry.
|
||||||
|
|
@ -114,36 +123,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PageSelectorModel.TryParseAreaPath(_pagesOptions, item.FilePath, _logger, out var areaResult))
|
var routeModel = _routeModelFactory.CreateAreaRouteModel(relativePath, routeTemplate);
|
||||||
|
if (routeModel != null)
|
||||||
{
|
{
|
||||||
continue;
|
context.RouteModels.Add(routeModel);
|
||||||
}
|
|
||||||
|
|
||||||
var routeModel = new PageRouteModel(relativePath, viewEnginePath: areaResult.viewEnginePath)
|
|
||||||
{
|
|
||||||
RouteValues =
|
|
||||||
{
|
|
||||||
["area"] = areaResult.areaName,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
PageSelectorModel.PopulateDefaults(routeModel, areaResult.pageRoute, routeTemplate);
|
|
||||||
context.RouteModels.Add(routeModel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsAlreadyRegistered(PageRouteModelProviderContext context, string relativePath)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < context.RouteModels.Count; i++)
|
|
||||||
{
|
|
||||||
var existingRouteModel = context.RouteModels[i];
|
|
||||||
if (string.Equals(relativePath, existingRouteModel.RelativePath, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsRouteable(RazorProjectItem item)
|
private static bool IsRouteable(RazorProjectItem item)
|
||||||
|
|
|
||||||
|
|
@ -24,42 +24,21 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
public class CompiledPageRouteModelProviderTest
|
public class CompiledPageRouteModelProviderTest
|
||||||
{
|
{
|
||||||
public CompiledPageRouteModelProviderTest()
|
|
||||||
{
|
|
||||||
FileProvider = new TestFileProvider();
|
|
||||||
Project = new FileProviderRazorProject(
|
|
||||||
Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == FileProvider),
|
|
||||||
Mock.Of<IHostingEnvironment>(e => e.ContentRootPath == "BasePath"));
|
|
||||||
TemplateEngine = new RazorTemplateEngine(RazorEngine.Create(), Project);
|
|
||||||
|
|
||||||
PagesOptions = new RazorPagesOptions();
|
|
||||||
Provider = new TestCompiledPageRouteModelProvider(new ApplicationPartManager(), Options.Create(PagesOptions), TemplateEngine, NullLoggerFactory.Instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestFileProvider FileProvider { get; }
|
|
||||||
|
|
||||||
public RazorProject Project { get; }
|
|
||||||
|
|
||||||
public RazorTemplateEngine TemplateEngine { get; }
|
|
||||||
|
|
||||||
public RazorPagesOptions PagesOptions { get; }
|
|
||||||
|
|
||||||
public TestCompiledPageRouteModelProvider Provider { get; }
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void OnProvidersExecuting_AddsModelsForCompiledViews()
|
public void OnProvidersExecuting_AddsModelsForCompiledViews()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Provider.Descriptors.AddRange(new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
CreateVersion_2_0_Descriptor("/Pages/About.cshtml"),
|
CreateVersion_2_0_Descriptor("/Pages/About.cshtml"),
|
||||||
CreateVersion_2_0_Descriptor("/Pages/Home.cshtml", "some-prefix"),
|
CreateVersion_2_0_Descriptor("/Pages/Home.cshtml", "some-prefix"),
|
||||||
});
|
};
|
||||||
|
|
||||||
|
var provider = CreateProvider(descriptors: descriptors);
|
||||||
var context = new PageRouteModelProviderContext();
|
var context = new PageRouteModelProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
Provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
|
|
@ -100,16 +79,17 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
public void OnProvidersExecuting_AddsModelsForCompiledViews_Version_2_1()
|
public void OnProvidersExecuting_AddsModelsForCompiledViews_Version_2_1()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Provider.Descriptors.AddRange(new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
CreateVersion_2_1_Descriptor("/Pages/About.cshtml"),
|
CreateVersion_2_1_Descriptor("/Pages/About.cshtml"),
|
||||||
CreateVersion_2_1_Descriptor("/Pages/Home.cshtml", "some-prefix"),
|
CreateVersion_2_1_Descriptor("/Pages/Home.cshtml", "some-prefix"),
|
||||||
});
|
};
|
||||||
|
|
||||||
|
var provider = CreateProvider(descriptors: descriptors);
|
||||||
var context = new PageRouteModelProviderContext();
|
var context = new PageRouteModelProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
Provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
|
|
@ -150,20 +130,22 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
public void OnProvidersExecuting_ValidatesChecksum_RejectsPageWhenContentDoesntMatch()
|
public void OnProvidersExecuting_ValidatesChecksum_RejectsPageWhenContentDoesntMatch()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Provider.Descriptors.AddRange(new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
CreateVersion_2_1_Descriptor("/Pages/About.cshtml", metadata: new object[]
|
CreateVersion_2_1_Descriptor("/Pages/About.cshtml", metadata: new object[]
|
||||||
{
|
{
|
||||||
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/About.cshtml"),
|
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/About.cshtml"),
|
||||||
}),
|
}),
|
||||||
});
|
};
|
||||||
|
|
||||||
FileProvider.AddFile("/Pages/About.cshtml", "some other content");
|
var fileProvider = new TestFileProvider();
|
||||||
|
fileProvider.AddFile("/Pages/About.cshtml", "some other content");
|
||||||
|
|
||||||
|
var provider = CreateProvider(descriptors: descriptors, fileProvider: fileProvider);
|
||||||
var context = new PageRouteModelProviderContext();
|
var context = new PageRouteModelProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
Provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(context.RouteModels);
|
Assert.Empty(context.RouteModels);
|
||||||
|
|
@ -173,22 +155,24 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
public void OnProvidersExecuting_ValidatesChecksum_AcceptsPageWhenContentMatches()
|
public void OnProvidersExecuting_ValidatesChecksum_AcceptsPageWhenContentMatches()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Provider.Descriptors.AddRange(new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
CreateVersion_2_1_Descriptor("/Pages/About.cshtml", metadata: new object[]
|
CreateVersion_2_1_Descriptor("/Pages/About.cshtml", metadata: new object[]
|
||||||
{
|
{
|
||||||
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/About.cshtml"),
|
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/About.cshtml"),
|
||||||
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), "/Pages/_ViewImports.cshtml"),
|
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), "/Pages/_ViewImports.cshtml"),
|
||||||
}),
|
}),
|
||||||
});
|
};
|
||||||
|
|
||||||
FileProvider.AddFile("/Pages/About.cshtml", "some content");
|
var fileProvider = new TestFileProvider();
|
||||||
FileProvider.AddFile("/Pages/_ViewImports.cshtml", "some import");
|
fileProvider.AddFile("/Pages/About.cshtml", "some content");
|
||||||
|
fileProvider.AddFile("/Pages/_ViewImports.cshtml", "some import");
|
||||||
|
|
||||||
|
var provider = CreateProvider(descriptors: descriptors, fileProvider: fileProvider);
|
||||||
var context = new PageRouteModelProviderContext();
|
var context = new PageRouteModelProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
Provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
|
|
@ -200,21 +184,23 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
public void OnProvidersExecuting_ValidatesChecksum_SkipsValidationWhenMainSourceMissing()
|
public void OnProvidersExecuting_ValidatesChecksum_SkipsValidationWhenMainSourceMissing()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Provider.Descriptors.AddRange(new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
CreateVersion_2_1_Descriptor("/Pages/About.cshtml", metadata: new object[]
|
CreateVersion_2_1_Descriptor("/Pages/About.cshtml", metadata: new object[]
|
||||||
{
|
{
|
||||||
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/About.cshtml"),
|
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/About.cshtml"),
|
||||||
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), "/Pages/_ViewImports.cshtml"),
|
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), "/Pages/_ViewImports.cshtml"),
|
||||||
}),
|
}),
|
||||||
});
|
};
|
||||||
|
|
||||||
FileProvider.AddFile("/Pages/_ViewImports.cshtml", "some other import");
|
var fileProvider = new TestFileProvider();
|
||||||
|
fileProvider.AddFile("/Pages/_ViewImports.cshtml", "some other import");
|
||||||
|
|
||||||
|
var provider = CreateProvider(descriptors: descriptors, fileProvider: fileProvider);
|
||||||
var context = new PageRouteModelProviderContext();
|
var context = new PageRouteModelProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
Provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
|
|
@ -226,21 +212,25 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
public void OnProvidersExecuting_AddsModelsForCompiledAreaPages()
|
public void OnProvidersExecuting_AddsModelsForCompiledAreaPages()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Provider.Descriptors.AddRange(new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
CreateVersion_2_0_Descriptor("/Features/Products/Files/About.cshtml"),
|
CreateVersion_2_0_Descriptor("/Features/Products/Files/About.cshtml"),
|
||||||
CreateVersion_2_0_Descriptor("/Features/Products/Files/Manage/Index.cshtml"),
|
CreateVersion_2_0_Descriptor("/Features/Products/Files/Manage/Index.cshtml"),
|
||||||
CreateVersion_2_0_Descriptor("/Features/Products/Files/Manage/Edit.cshtml", "{id}"),
|
CreateVersion_2_0_Descriptor("/Features/Products/Files/Manage/Edit.cshtml", "{id}"),
|
||||||
});
|
};
|
||||||
|
|
||||||
PagesOptions.AllowAreas = true;
|
var options = new RazorPagesOptions
|
||||||
PagesOptions.AreaRootDirectory = "/Features";
|
{
|
||||||
PagesOptions.RootDirectory = "/Files";
|
AllowAreas = true,
|
||||||
|
AreaRootDirectory = "/Features",
|
||||||
|
RootDirectory = "/Files",
|
||||||
|
};
|
||||||
|
|
||||||
|
var provider = CreateProvider(options: options, descriptors: descriptors);
|
||||||
var context = new PageRouteModelProviderContext();
|
var context = new PageRouteModelProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
Provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
|
|
@ -311,18 +301,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
public void OnProvidersExecuting_DoesNotAddsModelsForAreaPages_IfFeatureIsDisabled()
|
public void OnProvidersExecuting_DoesNotAddsModelsForAreaPages_IfFeatureIsDisabled()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Provider.Descriptors.AddRange(new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
CreateVersion_2_0_Descriptor("/Pages/About.cshtml"),
|
CreateVersion_2_0_Descriptor("/Pages/About.cshtml"),
|
||||||
CreateVersion_2_0_Descriptor("/Areas/Accounts/Pages/Home.cshtml"),
|
CreateVersion_2_0_Descriptor("/Areas/Accounts/Pages/Home.cshtml"),
|
||||||
});
|
};
|
||||||
|
|
||||||
PagesOptions.AllowAreas = false;
|
var options = new RazorPagesOptions { AllowAreas = false };
|
||||||
|
|
||||||
|
var provider = CreateProvider(options: options, descriptors: descriptors);
|
||||||
var context = new PageRouteModelProviderContext();
|
var context = new PageRouteModelProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
Provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
|
|
@ -348,21 +339,25 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
public void OnProvidersExecuting_DoesNotAddAreaAndNonAreaRoutesForAPage()
|
public void OnProvidersExecuting_DoesNotAddAreaAndNonAreaRoutesForAPage()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Provider.Descriptors.AddRange(new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
CreateVersion_2_0_Descriptor("/Areas/Accounts/Manage/Home.cshtml"),
|
CreateVersion_2_0_Descriptor("/Areas/Accounts/Manage/Home.cshtml"),
|
||||||
CreateVersion_2_0_Descriptor("/Areas/About.cshtml"),
|
CreateVersion_2_0_Descriptor("/Areas/About.cshtml"),
|
||||||
CreateVersion_2_0_Descriptor("/Contact.cshtml"),
|
CreateVersion_2_0_Descriptor("/Contact.cshtml"),
|
||||||
});
|
};
|
||||||
|
|
||||||
PagesOptions.AllowAreas = true;
|
var options = new RazorPagesOptions
|
||||||
PagesOptions.AreaRootDirectory = "/Areas";
|
{
|
||||||
PagesOptions.RootDirectory = "/";
|
AllowAreas = true,
|
||||||
|
AreaRootDirectory = "/Areas",
|
||||||
|
RootDirectory = "/",
|
||||||
|
};
|
||||||
|
|
||||||
|
var provider = CreateProvider(options: options, descriptors: descriptors);
|
||||||
var context = new PageRouteModelProviderContext();
|
var context = new PageRouteModelProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
Provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
|
|
@ -408,18 +403,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
public void OnProvidersExecuting_AddsMultipleSelectorsForIndexPage_WithIndexAtRoot()
|
public void OnProvidersExecuting_AddsMultipleSelectorsForIndexPage_WithIndexAtRoot()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Provider.Descriptors.AddRange(new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
CreateVersion_2_0_Descriptor("/Pages/Index.cshtml"),
|
CreateVersion_2_0_Descriptor("/Pages/Index.cshtml"),
|
||||||
CreateVersion_2_0_Descriptor("/Pages/Admin/Index.cshtml", "some-template"),
|
CreateVersion_2_0_Descriptor("/Pages/Admin/Index.cshtml", "some-template"),
|
||||||
});
|
};
|
||||||
|
var options = new RazorPagesOptions { RootDirectory = "/" };
|
||||||
PagesOptions.RootDirectory = "/";
|
|
||||||
|
|
||||||
|
var provider = CreateProvider(options: options, descriptors: descriptors);
|
||||||
var context = new PageRouteModelProviderContext();
|
var context = new PageRouteModelProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
Provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
|
|
@ -448,16 +443,17 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
public void OnProvidersExecuting_AddsMultipleSelectorsForIndexPage()
|
public void OnProvidersExecuting_AddsMultipleSelectorsForIndexPage()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Provider.Descriptors.AddRange(new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
CreateVersion_2_0_Descriptor("/Pages/Index.cshtml"),
|
CreateVersion_2_0_Descriptor("/Pages/Index.cshtml"),
|
||||||
CreateVersion_2_0_Descriptor("/Pages/Admin/Index.cshtml", "some-template"),
|
CreateVersion_2_0_Descriptor("/Pages/Admin/Index.cshtml", "some-template"),
|
||||||
});
|
};
|
||||||
|
|
||||||
|
var provider = CreateProvider(descriptors: descriptors);
|
||||||
var context = new PageRouteModelProviderContext();
|
var context = new PageRouteModelProviderContext();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
Provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
|
|
@ -486,21 +482,45 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
public void OnProvidersExecuting_ThrowsIfRouteTemplateHasOverridePattern()
|
public void OnProvidersExecuting_ThrowsIfRouteTemplateHasOverridePattern()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Provider.Descriptors.AddRange(new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
CreateVersion_2_0_Descriptor("/Pages/Index.cshtml"),
|
CreateVersion_2_0_Descriptor("/Pages/Index.cshtml"),
|
||||||
CreateVersion_2_0_Descriptor("/Pages/Home.cshtml", "/some-prefix"),
|
CreateVersion_2_0_Descriptor("/Pages/Home.cshtml", "/some-prefix"),
|
||||||
});
|
};
|
||||||
|
|
||||||
|
var provider = CreateProvider(descriptors: descriptors);
|
||||||
var context = new PageRouteModelProviderContext();
|
var context = new PageRouteModelProviderContext();
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var exception = Assert.Throws<InvalidOperationException>(() => Provider.OnProvidersExecuting(context));
|
var exception = Assert.Throws<InvalidOperationException>(() => provider.OnProvidersExecuting(context));
|
||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
"The route for the page at '/Pages/Home.cshtml' cannot start with / or ~/. Pages do not support overriding the file path of the page.",
|
"The route for the page at '/Pages/Home.cshtml' cannot start with / or ~/. Pages do not support overriding the file path of the page.",
|
||||||
exception.Message);
|
exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TestCompiledPageRouteModelProvider CreateProvider(
|
||||||
|
RazorPagesOptions options = null,
|
||||||
|
IList<CompiledViewDescriptor> descriptors = null,
|
||||||
|
TestFileProvider fileProvider = null)
|
||||||
|
{
|
||||||
|
options = options ?? new RazorPagesOptions();
|
||||||
|
fileProvider = fileProvider ?? new TestFileProvider();
|
||||||
|
var project = new FileProviderRazorProject(
|
||||||
|
Mock.Of<IRazorViewEngineFileProviderAccessor>(a => a.FileProvider == fileProvider),
|
||||||
|
Mock.Of<IHostingEnvironment>(e => e.ContentRootPath == "BasePath"));
|
||||||
|
var templateEngine = new RazorTemplateEngine(RazorEngine.Create(), project);
|
||||||
|
|
||||||
|
var provider = new TestCompiledPageRouteModelProvider(
|
||||||
|
new ApplicationPartManager(),
|
||||||
|
Options.Create(options),
|
||||||
|
templateEngine,
|
||||||
|
NullLogger<CompiledPageRouteModelProvider>.Instance);
|
||||||
|
|
||||||
|
provider.Descriptors.AddRange(descriptors ?? Array.Empty<CompiledViewDescriptor>());
|
||||||
|
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
|
||||||
private static CompiledViewDescriptor CreateVersion_2_0_Descriptor(string path, string routeTemplate = "")
|
private static CompiledViewDescriptor CreateVersion_2_0_Descriptor(string path, string routeTemplate = "")
|
||||||
{
|
{
|
||||||
return new CompiledViewDescriptor
|
return new CompiledViewDescriptor
|
||||||
|
|
@ -529,8 +549,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
ApplicationPartManager partManager,
|
ApplicationPartManager partManager,
|
||||||
IOptions<RazorPagesOptions> options,
|
IOptions<RazorPagesOptions> options,
|
||||||
RazorTemplateEngine templateEngine,
|
RazorTemplateEngine templateEngine,
|
||||||
ILoggerFactory loggerFactory)
|
ILogger<CompiledPageRouteModelProvider> logger)
|
||||||
: base(partManager, options, templateEngine, loggerFactory)
|
: base(partManager, options, templateEngine, logger)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -539,4 +559,4 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
protected override IEnumerable<CompiledViewDescriptor> GetViewDescriptors(ApplicationPartManager applicationManager) => Descriptors;
|
protected override IEnumerable<CompiledViewDescriptor> GetViewDescriptors(ApplicationPartManager applicationManager) => Descriptors;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,219 @@
|
||||||
|
// 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.Linq;
|
||||||
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
{
|
||||||
|
public class PageRouteModelFactoryTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CreateRouteModel_AddsSelector()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var relativePath = "/Pages/Users/Profile.cshtml";
|
||||||
|
var options = new RazorPagesOptions();
|
||||||
|
var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var routeModel = routeModelFactory.CreateRouteModel(relativePath, "{id?}");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(relativePath, routeModel.RelativePath);
|
||||||
|
Assert.Equal("/Users/Profile", routeModel.ViewEnginePath);
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
routeModel.Selectors,
|
||||||
|
selector => Assert.Equal("Users/Profile/{id?}", selector.AttributeRouteModel.Template));
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
routeModel.RouteValues,
|
||||||
|
kvp =>
|
||||||
|
{
|
||||||
|
Assert.Equal("page", kvp.Key);
|
||||||
|
Assert.Equal("/Users/Profile", kvp.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateRouteModel_AddsMultipleSelectorsForIndexPage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var relativePath = "/Pages/Users/Profile/Index.cshtml";
|
||||||
|
var options = new RazorPagesOptions();
|
||||||
|
var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var routeModel = routeModelFactory.CreateRouteModel(relativePath, "{id?}");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(relativePath, routeModel.RelativePath);
|
||||||
|
Assert.Equal("/Users/Profile/Index", routeModel.ViewEnginePath);
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
routeModel.Selectors,
|
||||||
|
selector => Assert.Equal("Users/Profile/Index/{id?}", selector.AttributeRouteModel.Template),
|
||||||
|
selector => Assert.Equal("Users/Profile/{id?}", selector.AttributeRouteModel.Template));
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
routeModel.RouteValues,
|
||||||
|
kvp =>
|
||||||
|
{
|
||||||
|
Assert.Equal("page", kvp.Key);
|
||||||
|
Assert.Equal("/Users/Profile/Index", kvp.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateAreaRouteModel_AddsSelector()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var relativePath = "/Areas/TestArea/Pages/Users/Profile.cshtml";
|
||||||
|
var options = new RazorPagesOptions();
|
||||||
|
var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var routeModel = routeModelFactory.CreateAreaRouteModel(relativePath, "{id?}");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(relativePath, routeModel.RelativePath);
|
||||||
|
Assert.Equal("/Users/Profile", routeModel.ViewEnginePath);
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
routeModel.Selectors,
|
||||||
|
selector => Assert.Equal("TestArea/Users/Profile/{id?}", selector.AttributeRouteModel.Template));
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
routeModel.RouteValues.OrderBy(kvp => kvp.Key),
|
||||||
|
kvp =>
|
||||||
|
{
|
||||||
|
Assert.Equal("area", kvp.Key);
|
||||||
|
Assert.Equal("TestArea", kvp.Value);
|
||||||
|
},
|
||||||
|
kvp =>
|
||||||
|
{
|
||||||
|
Assert.Equal("page", kvp.Key);
|
||||||
|
Assert.Equal("/Users/Profile", kvp.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateAreaRouteModel_AddsMultipleSelectorsForIndexPage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var relativePath = "/Areas/TestArea/Pages/Users/Profile/Index.cshtml";
|
||||||
|
var options = new RazorPagesOptions();
|
||||||
|
var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var routeModel = routeModelFactory.CreateAreaRouteModel(relativePath, "{id?}");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(relativePath, routeModel.RelativePath);
|
||||||
|
Assert.Equal("/Users/Profile/Index", routeModel.ViewEnginePath);
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
routeModel.Selectors,
|
||||||
|
selector => Assert.Equal("TestArea/Users/Profile/Index/{id?}", selector.AttributeRouteModel.Template),
|
||||||
|
selector => Assert.Equal("TestArea/Users/Profile/{id?}", selector.AttributeRouteModel.Template));
|
||||||
|
|
||||||
|
Assert.Collection(
|
||||||
|
routeModel.RouteValues.OrderBy(kvp => kvp.Key),
|
||||||
|
kvp =>
|
||||||
|
{
|
||||||
|
Assert.Equal("area", kvp.Key);
|
||||||
|
Assert.Equal("TestArea", kvp.Value);
|
||||||
|
},
|
||||||
|
kvp =>
|
||||||
|
{
|
||||||
|
Assert.Equal("page", kvp.Key);
|
||||||
|
Assert.Equal("/Users/Profile/Index", kvp.Value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalTheory]
|
||||||
|
[FrameworkSkipCondition(RuntimeFrameworks.CLR, SkipReason = "Fails due to dotnet/standard#567")]
|
||||||
|
[InlineData("/Areas/About.cshtml")]
|
||||||
|
[InlineData("/Areas/MyArea/Index.cshtml")]
|
||||||
|
public void TryParseAreaPath_ReturnsFalse_IfPathDoesNotConform(string path)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var options = new RazorPagesOptions();
|
||||||
|
var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var success = routeModelFactory.TryParseAreaPath(path, out _);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalTheory]
|
||||||
|
[FrameworkSkipCondition(RuntimeFrameworks.CLR, SkipReason = "Fails due to dotnet/standard#567")]
|
||||||
|
[InlineData("/Areas/MyArea/Views/About.cshtml")]
|
||||||
|
[InlineData("/Areas/MyArea/SubDir/Pages/Index.cshtml")]
|
||||||
|
[InlineData("/Areas/MyArea/NotPages/SubDir/About.cshtml")]
|
||||||
|
public void TryParseAreaPath_ReturnsFalse_IfPathDoesNotBelongToRootDirectory(string path)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var options = new RazorPagesOptions();
|
||||||
|
var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var success = routeModelFactory.TryParseAreaPath(path, out _);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalTheory]
|
||||||
|
[FrameworkSkipCondition(RuntimeFrameworks.CLR, SkipReason = "Fails due to dotnet/standard#567")]
|
||||||
|
[InlineData("/Areas/MyArea/Pages/Index.cshtml", "MyArea", "/Index")]
|
||||||
|
[InlineData("/Areas/Accounts/Pages/Manage/Edit.cshtml", "Accounts", "/Manage/Edit")]
|
||||||
|
public void TryParseAreaPath_ParsesAreaPath(
|
||||||
|
string path,
|
||||||
|
string expectedArea,
|
||||||
|
string expectedViewEnginePath)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var options = new RazorPagesOptions();
|
||||||
|
var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var success = routeModelFactory.TryParseAreaPath(path, out var result);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(success);
|
||||||
|
Assert.Equal(expectedArea, result.areaName);
|
||||||
|
Assert.Equal(expectedViewEnginePath, result.viewEnginePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalTheory]
|
||||||
|
[FrameworkSkipCondition(RuntimeFrameworks.CLR, SkipReason = "Fails due to dotnet/standard#567")]
|
||||||
|
[InlineData("/Areas/MyArea/Dir1/Dir2/Index.cshtml", "MyArea", "/Index")]
|
||||||
|
[InlineData("/Areas/Accounts/Dir1/Dir2/Manage/Edit.cshtml", "Accounts", "/Manage/Edit")]
|
||||||
|
public void TryParseAreaPath_ParsesAreaPath_WithMultiLevelRootDirectory(
|
||||||
|
string path,
|
||||||
|
string expectedArea,
|
||||||
|
string expectedViewEnginePath)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var options = new RazorPagesOptions
|
||||||
|
{
|
||||||
|
RootDirectory = "/Dir1/Dir2"
|
||||||
|
};
|
||||||
|
var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var success = routeModelFactory.TryParseAreaPath(path, out var result);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(success);
|
||||||
|
Assert.Equal(expectedArea, result.areaName);
|
||||||
|
Assert.Equal(expectedViewEnginePath, result.viewEnginePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
// 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 Microsoft.AspNetCore.Testing.xunit;
|
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|
||||||
{
|
|
||||||
public class PageSelectorModelTest
|
|
||||||
{
|
|
||||||
[ConditionalTheory]
|
|
||||||
[FrameworkSkipCondition(RuntimeFrameworks.CLR, SkipReason = "Fails due to dotnet/standard#567")]
|
|
||||||
[InlineData("/Areas/About.cshtml")]
|
|
||||||
[InlineData("/Areas/MyArea/Index.cshtml")]
|
|
||||||
public void TryParseAreaPath_ReturnsFalse_IfPathDoesNotConform(string path)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var options = new RazorPagesOptions();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var success = PageSelectorModel.TryParseAreaPath(options, path, NullLogger.Instance, out _);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.False(success);
|
|
||||||
}
|
|
||||||
|
|
||||||
[ConditionalTheory]
|
|
||||||
[FrameworkSkipCondition(RuntimeFrameworks.CLR, SkipReason = "Fails due to dotnet/standard#567")]
|
|
||||||
[InlineData("/MyArea/Views/About.cshtml")]
|
|
||||||
[InlineData("/MyArea/SubDir/Pages/Index.cshtml")]
|
|
||||||
[InlineData("/MyArea/NotPages/SubDir/About.cshtml")]
|
|
||||||
public void TryParseAreaPath_ReturnsFalse_IfPathDoesNotBelongToRootDirectory(string path)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var options = new RazorPagesOptions();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var success = PageSelectorModel.TryParseAreaPath(options, path, NullLogger.Instance, out _);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.False(success);
|
|
||||||
}
|
|
||||||
|
|
||||||
[ConditionalTheory]
|
|
||||||
[FrameworkSkipCondition(RuntimeFrameworks.CLR, SkipReason = "Fails due to dotnet/standard#567")]
|
|
||||||
[InlineData("/MyArea/Pages/Index.cshtml", "MyArea", "/Index", "/MyArea/Index")]
|
|
||||||
[InlineData("/Accounts/Pages/Manage/Edit.cshtml", "Accounts", "/Manage/Edit", "/Accounts/Manage/Edit")]
|
|
||||||
public void TryParseAreaPath_ParsesAreaPath(
|
|
||||||
string path,
|
|
||||||
string expectedArea,
|
|
||||||
string expectedViewEnginePath,
|
|
||||||
string expectedRoute)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var options = new RazorPagesOptions();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var success = PageSelectorModel.TryParseAreaPath(options, path, NullLogger.Instance, out var result);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.True(success);
|
|
||||||
Assert.Equal(expectedArea, result.areaName);
|
|
||||||
Assert.Equal(expectedViewEnginePath, result.viewEnginePath);
|
|
||||||
Assert.Equal(expectedRoute, result.pageRoute);
|
|
||||||
}
|
|
||||||
|
|
||||||
[ConditionalTheory]
|
|
||||||
[FrameworkSkipCondition(RuntimeFrameworks.CLR, SkipReason = "Fails due to dotnet/standard#567")]
|
|
||||||
[InlineData("/MyArea/Dir1/Dir2/Index.cshtml", "MyArea", "/Index", "/MyArea/Index")]
|
|
||||||
[InlineData("/Accounts/Dir1/Dir2/Manage/Edit.cshtml", "Accounts", "/Manage/Edit", "/Accounts/Manage/Edit")]
|
|
||||||
public void TryParseAreaPath_ParsesAreaPath_WithMultiLevelRootDirectory(
|
|
||||||
string path,
|
|
||||||
string expectedArea,
|
|
||||||
string expectedViewEnginePath,
|
|
||||||
string expectedRoute)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var options = new RazorPagesOptions
|
|
||||||
{
|
|
||||||
RootDirectory = "/Dir1/Dir2"
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var success = PageSelectorModel.TryParseAreaPath(options, path, NullLogger.Instance, out var result);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.True(success);
|
|
||||||
Assert.Equal(expectedArea, result.areaName);
|
|
||||||
Assert.Equal(expectedViewEnginePath, result.viewEnginePath);
|
|
||||||
Assert.Equal(expectedRoute, result.pageRoute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue