parent
5b2a4aecb6
commit
52ee9afc31
|
|
@ -99,6 +99,33 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Combines the prefix and route template for an attribute route.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefix">The prefix.</param>
|
||||||
|
/// <param name="template">The route template.</param>
|
||||||
|
/// <returns>The combined pattern.</returns>
|
||||||
|
public static string CombineTemplates(string prefix, string template)
|
||||||
|
{
|
||||||
|
var result = CombineCore(prefix, template);
|
||||||
|
return CleanTemplate(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if a template pattern can be used to override a prefix.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="template">The template.</param>
|
||||||
|
/// <returns><c>true</c> if this is an overriding template, <c>false</c> otherwise.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// Route templates starting with "~/" or "/" can be used to override the prefix.
|
||||||
|
/// </remarks>
|
||||||
|
public static bool IsOverridePattern(string template)
|
||||||
|
{
|
||||||
|
return template != null &&
|
||||||
|
(template.StartsWith("~/", StringComparison.Ordinal) ||
|
||||||
|
template.StartsWith("/", StringComparison.Ordinal));
|
||||||
|
}
|
||||||
|
|
||||||
private static string ChooseName(
|
private static string ChooseName(
|
||||||
AttributeRouteModel left,
|
AttributeRouteModel left,
|
||||||
AttributeRouteModel right)
|
AttributeRouteModel right)
|
||||||
|
|
@ -113,12 +140,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string CombineTemplates(string left, string right)
|
|
||||||
{
|
|
||||||
var result = CombineCore(left, right);
|
|
||||||
return CleanTemplate(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string CombineCore(string left, string right)
|
private static string CombineCore(string left, string right)
|
||||||
{
|
{
|
||||||
if (left == null && right == null)
|
if (left == null && right == null)
|
||||||
|
|
@ -143,13 +164,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
return left + "/" + right;
|
return left + "/" + right;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsOverridePattern(string template)
|
|
||||||
{
|
|
||||||
return template != null &&
|
|
||||||
(template.StartsWith("~/", StringComparison.Ordinal) ||
|
|
||||||
template.StartsWith("/", StringComparison.Ordinal));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsEmptyLeftSegment(string template)
|
private static bool IsEmptyLeftSegment(string template)
|
||||||
{
|
{
|
||||||
return template == null ||
|
return template == null ||
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
|
@ -14,7 +13,6 @@ using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
using Microsoft.AspNetCore.Mvc.Core;
|
using Microsoft.AspNetCore.Mvc.Core;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.Mvc.Routing;
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
using Microsoft.AspNetCore.Routing.Tree;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1306,22 +1306,6 @@ namespace Microsoft.AspNetCore.Mvc.Core
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("MiddlewareFilter_ServiceResolutionFail"), p0, p1, p2, p3);
|
return string.Format(CultureInfo.CurrentCulture, GetString("MiddlewareFilter_ServiceResolutionFail"), p0, p1, p2, p3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unable to create an instance of type '{0}'. The type specified in {1} must not be abstract and must have a parameterless constructor.
|
|
||||||
/// </summary>
|
|
||||||
internal static string MiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType
|
|
||||||
{
|
|
||||||
get { return GetString("MiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unable to create an instance of type '{0}'. The type specified in {1} must not be abstract and must have a parameterless constructor.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatMiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType(object p0, object p1)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("MiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType"), p0, p1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An {0} cannot be created without a valid instance of {1}.
|
/// An {0} cannot be created without a valid instance of {1}.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -1355,7 +1339,7 @@ namespace Microsoft.AspNetCore.Mvc.Core
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// VaryByQueryKeys requires the response cache middleware.
|
/// '{0}' requires the response cache middleware.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string VaryByQueryKeys_Requires_ResponseCachingMiddleware
|
internal static string VaryByQueryKeys_Requires_ResponseCachingMiddleware
|
||||||
{
|
{
|
||||||
|
|
@ -1363,7 +1347,7 @@ namespace Microsoft.AspNetCore.Mvc.Core
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// VaryByQueryKeys requires the response cache middleware.
|
/// '{0}' requires the response cache middleware.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatVaryByQueryKeys_Requires_ResponseCachingMiddleware(object p0)
|
internal static string FormatVaryByQueryKeys_Requires_ResponseCachingMiddleware(object p0)
|
||||||
{
|
{
|
||||||
|
|
@ -1386,6 +1370,22 @@ namespace Microsoft.AspNetCore.Mvc.Core
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("CandidateResolver_DifferentCasedReference"), p0);
|
return string.Format(CultureInfo.CurrentCulture, GetString("CandidateResolver_DifferentCasedReference"), p0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unable to create an instance of type '{0}'. The type specified in {1} must not be abstract and must have a parameterless constructor.
|
||||||
|
/// </summary>
|
||||||
|
internal static string MiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType
|
||||||
|
{
|
||||||
|
get { return GetString("MiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unable to create an instance of type '{0}'. The type specified in {1} must not be abstract and must have a parameterless constructor.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatMiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType(object p0, object p1)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("MiddlewareFilterConfigurationProvider_CreateConfigureDelegate_CannotCreateType"), p0, p1);
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Allows customization of the of the <see cref="PageModel"/>.
|
||||||
|
/// </summary>
|
||||||
|
public interface IPageModelConvention
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Called to apply the convention to the <see cref="PageModel"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The <see cref="PageModel"/>.</param>
|
||||||
|
void Apply(PageModel model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Application model component for RazorPages.
|
||||||
|
/// </summary>
|
||||||
|
public class PageModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="PageModel"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="relativePath">The application relative path of the page.</param>
|
||||||
|
/// <param name="viewEnginePath">The path relative to the base path for page discovery.</param>
|
||||||
|
public PageModel(string relativePath, string viewEnginePath)
|
||||||
|
{
|
||||||
|
if (relativePath == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(relativePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewEnginePath == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(viewEnginePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
RelativePath = relativePath;
|
||||||
|
ViewEnginePath = viewEnginePath;
|
||||||
|
|
||||||
|
Filters = new List<IFilterMetadata>();
|
||||||
|
Properties = new Dictionary<object, object>();
|
||||||
|
Selectors = new List<SelectorModel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A copy constructor for <see cref="PageModel"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">The <see cref="PageModel"/> to copy from.</param>
|
||||||
|
public PageModel(PageModel other)
|
||||||
|
{
|
||||||
|
if (other == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
RelativePath = other.RelativePath;
|
||||||
|
ViewEnginePath = other.ViewEnginePath;
|
||||||
|
|
||||||
|
Filters = new List<IFilterMetadata>(other.Filters);
|
||||||
|
Properties = new Dictionary<object, object>(other.Properties);
|
||||||
|
|
||||||
|
Selectors = new List<SelectorModel>(other.Selectors.Select(m => new SelectorModel(m)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the application root relative path for the page.
|
||||||
|
/// </summary>
|
||||||
|
public string RelativePath { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the path relative to the base path for page discovery.
|
||||||
|
/// </summary>
|
||||||
|
public string ViewEnginePath { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the applicable <see cref="IFilterMetadata"/> instances.
|
||||||
|
/// </summary>
|
||||||
|
public IList<IFilterMetadata> Filters { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores arbitrary metadata properties associated with the <see cref="PageModel"/>.
|
||||||
|
/// </summary>
|
||||||
|
public IDictionary<object, object> Properties { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="SelectorModel"/> instances.
|
||||||
|
/// </summary>
|
||||||
|
public IList<SelectorModel> Selectors { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
|
{
|
||||||
|
public class PageActionDescriptorProvider : IActionDescriptorProvider
|
||||||
|
{
|
||||||
|
private static readonly string IndexFileName = "Index.cshtml";
|
||||||
|
private readonly RazorProject _project;
|
||||||
|
private readonly MvcOptions _mvcOptions;
|
||||||
|
private readonly RazorPagesOptions _pagesOptions;
|
||||||
|
|
||||||
|
public PageActionDescriptorProvider(
|
||||||
|
RazorProject project,
|
||||||
|
IOptions<MvcOptions> mvcOptionsAccessor,
|
||||||
|
IOptions<RazorPagesOptions> pagesOptionsAccessor)
|
||||||
|
{
|
||||||
|
_project = project;
|
||||||
|
_mvcOptions = mvcOptionsAccessor.Value;
|
||||||
|
_pagesOptions = pagesOptionsAccessor.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Order { get; set; }
|
||||||
|
|
||||||
|
public void OnProvidersExecuting(ActionDescriptorProviderContext context)
|
||||||
|
{
|
||||||
|
foreach (var item in _project.EnumerateItems("/"))
|
||||||
|
{
|
||||||
|
if (item.Filename.StartsWith("_"))
|
||||||
|
{
|
||||||
|
// Pages like _PageImports should not be routable.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string template;
|
||||||
|
if (!PageDirectiveFeature.TryGetRouteTemplate(item, out template))
|
||||||
|
{
|
||||||
|
// .cshtml pages without @page are not RazorPages.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AttributeRouteModel.IsOverridePattern(template))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(string.Format(
|
||||||
|
Resources.PageActionDescriptorProvider_RouteTemplateCannotBeOverrideable,
|
||||||
|
item.Path));
|
||||||
|
}
|
||||||
|
|
||||||
|
AddActionDescriptors(context.Results, item, template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnProvidersExecuted(ActionDescriptorProviderContext context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddActionDescriptors(IList<ActionDescriptor> actions, RazorProjectItem item, string template)
|
||||||
|
{
|
||||||
|
var model = new PageModel(item.CombinedPath, item.PathWithoutExtension);
|
||||||
|
var routePrefix = item.BasePath == "/" ? item.PathWithoutExtension : item.BasePath + item.PathWithoutExtension;
|
||||||
|
model.Selectors.Add(CreateSelectorModel(routePrefix, template));
|
||||||
|
|
||||||
|
if (string.Equals(IndexFileName, item.Filename, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
model.Selectors.Add(CreateSelectorModel(item.BasePath, template));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < _pagesOptions.Conventions.Count; i++)
|
||||||
|
{
|
||||||
|
_pagesOptions.Conventions[i].Apply(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
var filters = new List<FilterDescriptor>(_mvcOptions.Filters.Count + model.Filters.Count);
|
||||||
|
for (var i = 0; i < _mvcOptions.Filters.Count; i++)
|
||||||
|
{
|
||||||
|
filters.Add(new FilterDescriptor(_mvcOptions.Filters[i], FilterScope.Global));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < model.Filters.Count; i++)
|
||||||
|
{
|
||||||
|
filters.Add(new FilterDescriptor(model.Filters[i], FilterScope.Action));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var selector in model.Selectors)
|
||||||
|
{
|
||||||
|
actions.Add(new PageActionDescriptor()
|
||||||
|
{
|
||||||
|
AttributeRouteInfo = new AttributeRouteInfo()
|
||||||
|
{
|
||||||
|
Name = selector.AttributeRouteModel.Name,
|
||||||
|
Order = selector.AttributeRouteModel.Order ?? 0,
|
||||||
|
Template = selector.AttributeRouteModel.Template,
|
||||||
|
},
|
||||||
|
DisplayName = $"Page: {item.Path}",
|
||||||
|
FilterDescriptors = filters,
|
||||||
|
Properties = new Dictionary<object, object>(model.Properties),
|
||||||
|
RelativePath = item.CombinedPath,
|
||||||
|
RouteValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{ "page", item.PathWithoutExtension },
|
||||||
|
},
|
||||||
|
ViewEnginePath = item.Path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SelectorModel CreateSelectorModel(string prefix, string template)
|
||||||
|
{
|
||||||
|
return new SelectorModel
|
||||||
|
{
|
||||||
|
AttributeRouteModel = new AttributeRouteModel
|
||||||
|
{
|
||||||
|
Template = AttributeRouteModel.CombineTemplates(prefix, template),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// 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.IO;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
|
{
|
||||||
|
public static class PageDirectiveFeature
|
||||||
|
{
|
||||||
|
public static bool TryGetRouteTemplate(RazorProjectItem projectItem, out string template)
|
||||||
|
{
|
||||||
|
const string PageDirective = "@page";
|
||||||
|
|
||||||
|
string content;
|
||||||
|
using (var streamReader = new StreamReader(projectItem.Read()))
|
||||||
|
{
|
||||||
|
content = streamReader.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.StartsWith(PageDirective, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var newLineIndex = content.IndexOf(Environment.NewLine, PageDirective.Length);
|
||||||
|
template = content.Substring(PageDirective.Length, newLineIndex - PageDirective.Length).Trim();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
{
|
||||||
|
public class DefaultRazorProject : RazorProject
|
||||||
|
{
|
||||||
|
private const string RazorFileExtension = ".cshtml";
|
||||||
|
private readonly IFileProvider _provider;
|
||||||
|
|
||||||
|
public DefaultRazorProject(IFileProvider provider)
|
||||||
|
{
|
||||||
|
_provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<RazorProjectItem> EnumerateItems(string path)
|
||||||
|
{
|
||||||
|
if (path == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.Length == 0 || path[0] != '/')
|
||||||
|
{
|
||||||
|
throw new ArgumentException(Resources.RazorProject_PathMustStartWithForwardSlash);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EnumerateFiles(_provider.GetDirectoryContents(path), path, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<RazorProjectItem> EnumerateFiles(IDirectoryContents directory, string basePath, string prefix)
|
||||||
|
{
|
||||||
|
if (directory.Exists)
|
||||||
|
{
|
||||||
|
foreach (var file in directory)
|
||||||
|
{
|
||||||
|
if (file.IsDirectory)
|
||||||
|
{
|
||||||
|
var children = EnumerateFiles(_provider.GetDirectoryContents(file.PhysicalPath), basePath, prefix + "/" + file.Name);
|
||||||
|
foreach (var child in children)
|
||||||
|
{
|
||||||
|
yield return child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (string.Equals(RazorFileExtension, Path.GetExtension(file.Name), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
yield return new DefaultRazorProjectItem(file, basePath, prefix + "/" + file.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
// 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.IO;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
{
|
||||||
|
public class DefaultRazorProjectItem : RazorProjectItem
|
||||||
|
{
|
||||||
|
private readonly IFileInfo _fileInfo;
|
||||||
|
|
||||||
|
public DefaultRazorProjectItem(IFileInfo fileInfo, string basePath, string path)
|
||||||
|
{
|
||||||
|
_fileInfo = fileInfo;
|
||||||
|
BasePath = basePath;
|
||||||
|
Path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string BasePath { get; }
|
||||||
|
|
||||||
|
public override string Path { get; }
|
||||||
|
|
||||||
|
public override string PhysicalPath => _fileInfo.PhysicalPath;
|
||||||
|
|
||||||
|
public override Stream Read()
|
||||||
|
{
|
||||||
|
return _fileInfo.CreateReadStream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
|
{
|
||||||
|
[DebuggerDisplay("{" + nameof(ViewEnginePath) + "}")]
|
||||||
|
public class PageActionDescriptor : ActionDescriptor
|
||||||
|
{
|
||||||
|
public string RelativePath { get; set; }
|
||||||
|
|
||||||
|
public string ViewEnginePath { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
// <auto-generated />
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||||
|
{
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Resources;
|
||||||
|
|
||||||
|
internal static class Resources
|
||||||
|
{
|
||||||
|
private static readonly ResourceManager _resourceManager
|
||||||
|
= new ResourceManager("Microsoft.AspNetCore.Mvc.RazorPages.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The route for the page at '{0}' cannot start with / or ~/. Pages do not support overriding the file path of the page.
|
||||||
|
/// </summary>
|
||||||
|
internal static string PageActionDescriptorProvider_RouteTemplateCannotBeOverrideable
|
||||||
|
{
|
||||||
|
get { return GetString("PageActionDescriptorProvider_RouteTemplateCannotBeOverrideable"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The route for the page at '{0}' cannot start with / or ~/. Pages do not support overriding the file path of the page.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatPageActionDescriptorProvider_RouteTemplateCannotBeOverrideable(object p0)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("PageActionDescriptorProvider_RouteTemplateCannotBeOverrideable"), p0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Path must begin with a forward slash '/'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string RazorProject_PathMustStartWithForwardSlash
|
||||||
|
{
|
||||||
|
get { return GetString("RazorProject_PathMustStartWithForwardSlash"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Path must begin with a forward slash '/'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatRazorProject_PathMustStartWithForwardSlash()
|
||||||
|
{
|
||||||
|
return GetString("RazorProject_PathMustStartWithForwardSlash");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
|
{
|
||||||
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.Assert(value != null);
|
||||||
|
|
||||||
|
if (formatterNames != null)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < formatterNames.Length; i++)
|
||||||
|
{
|
||||||
|
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides configuration for RazorPages.
|
||||||
|
/// </summary>
|
||||||
|
public class RazorPagesOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of <see cref="IPageModelConvention"/> instances that will be applied to
|
||||||
|
/// the <see cref="PageModel"/> when discovering Razor Pages.
|
||||||
|
/// </summary>
|
||||||
|
public IList<IPageModelConvention> Conventions { get; } = new List<IPageModelConvention>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="PageActionDescriptorProvider_RouteTemplateCannotBeOverrideable" xml:space="preserve">
|
||||||
|
<value>The route for the page at '{0}' cannot start with / or ~/. Pages do not support overriding the file path of the page.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RazorProject_PathMustStartWithForwardSlash" xml:space="preserve">
|
||||||
|
<value>Path must begin with a forward slash '/'.</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
"xmlDoc": true
|
"xmlDoc": true
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"Microsoft.AspNetCore.Mvc.ViewFeatures": { "target": "project" },
|
||||||
"Microsoft.AspNetCore.Razor.Evolution": "1.0.0-*",
|
"Microsoft.AspNetCore.Razor.Evolution": "1.0.0-*",
|
||||||
"NETStandard.Library": "1.6.1-*"
|
"NETStandard.Library": "1.6.1-*"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.
|
/// Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}', '{3}', or '{5}' attribute.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified
|
internal static string FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified
|
||||||
{
|
{
|
||||||
|
|
@ -171,7 +171,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cannot override the '{6}' attribute for <{0}>. <{0}> elements with a specified '{6}' must not have attributes starting with '{5}' or an '{1}', '{2}', '{3}', or '{4}' attribute.
|
/// Cannot override the '{7}' attribute for <{0}>. <{0}> elements with a specified '{7}' must not have attributes starting with '{6}' or an '{1}', '{2}', '{3}', '{4}', or '{5}' attribute.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormActionTagHelper_CannotOverrideFormAction
|
internal static string FormActionTagHelper_CannotOverrideFormAction
|
||||||
{
|
{
|
||||||
|
|
@ -179,7 +179,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cannot override the '{6}' attribute for <{0}>. <{0}> elements with a specified '{7}' must not have attributes starting with '{6}' or an '{1}', '{2}', '{3}', '{4}', or '{5}' attribute.
|
/// Cannot override the '{7}' attribute for <{0}>. <{0}> elements with a specified '{7}' must not have attributes starting with '{6}' or an '{1}', '{2}', '{3}', '{4}', or '{5}' attribute.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatFormActionTagHelper_CannotOverrideFormAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7)
|
internal static string FormatFormActionTagHelper_CannotOverrideFormAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7)
|
||||||
{
|
{
|
||||||
|
|
@ -187,7 +187,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cannot determine a '{4}' attribute for <{0}>. <{0}> elements with a specified '{1}' must not have an '{2}' or '{3}' attribute.
|
/// Cannot determine a '{4}' attribute for <{0}>. <{0}> elements with a specified '{1}' must not have an '{2}', '{3}', or '{5}' attribute.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified
|
internal static string FormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.Routing;
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
using Microsoft.AspNetCore.Routing.Tree;
|
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,304 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Razor;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
|
{
|
||||||
|
public class PageActionDescriptorProviderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void GetDescriptors_DoesNotAddDescriptorsForFilesWithoutDirectives()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var razorProject = new Mock<RazorProject>();
|
||||||
|
razorProject.Setup(p => p.EnumerateItems("/"))
|
||||||
|
.Returns(new[]
|
||||||
|
{
|
||||||
|
GetProjectItem("/", "/Index.cshtml", "<h1>Hello world</h1>"),
|
||||||
|
});
|
||||||
|
var provider = new PageActionDescriptorProvider(
|
||||||
|
razorProject.Object,
|
||||||
|
GetAccessor<MvcOptions>(),
|
||||||
|
GetAccessor<RazorPagesOptions>());
|
||||||
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(context.Results);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDescriptors_AddsDescriptorsForFileWithPageDirective()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var razorProject = new Mock<RazorProject>();
|
||||||
|
razorProject.Setup(p => p.EnumerateItems("/"))
|
||||||
|
.Returns(new[]
|
||||||
|
{
|
||||||
|
GetProjectItem("/", "/Test.cshtml", $"@page{Environment.NewLine}<h1>Hello world</h1>"),
|
||||||
|
});
|
||||||
|
var provider = new PageActionDescriptorProvider(
|
||||||
|
razorProject.Object,
|
||||||
|
GetAccessor<MvcOptions>(),
|
||||||
|
GetAccessor<RazorPagesOptions>());
|
||||||
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var result = Assert.Single(context.Results);
|
||||||
|
var descriptor = Assert.IsType<PageActionDescriptor>(result);
|
||||||
|
Assert.Equal("/Test.cshtml", descriptor.RelativePath);
|
||||||
|
Assert.Equal("/Test", descriptor.RouteValues["page"]);
|
||||||
|
Assert.Equal("Test", descriptor.AttributeRouteInfo.Template);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDescriptors_AddsDescriptorsForFileWithPageDirectiveAndRouteTemplate()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var razorProject = new Mock<RazorProject>();
|
||||||
|
razorProject.Setup(p => p.EnumerateItems("/"))
|
||||||
|
.Returns(new[]
|
||||||
|
{
|
||||||
|
GetProjectItem("/", "/Test.cshtml", $"@page Home {Environment.NewLine}<h1>Hello world</h1>"),
|
||||||
|
});
|
||||||
|
var provider = new PageActionDescriptorProvider(
|
||||||
|
razorProject.Object,
|
||||||
|
GetAccessor<MvcOptions>(),
|
||||||
|
GetAccessor<RazorPagesOptions>());
|
||||||
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var result = Assert.Single(context.Results);
|
||||||
|
var descriptor = Assert.IsType<PageActionDescriptor>(result);
|
||||||
|
Assert.Equal("/Test.cshtml", descriptor.RelativePath);
|
||||||
|
Assert.Equal("/Test", descriptor.RouteValues["page"]);
|
||||||
|
Assert.Equal("Test/Home", descriptor.AttributeRouteInfo.Template);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("/Path1")]
|
||||||
|
[InlineData("~/Path1")]
|
||||||
|
public void GetDescriptors_ThrowsIfRouteTemplatesAreOverriden(string template)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var razorProject = new Mock<RazorProject>();
|
||||||
|
razorProject.Setup(p => p.EnumerateItems("/"))
|
||||||
|
.Returns(new[]
|
||||||
|
{
|
||||||
|
GetProjectItem("/", "/Test.cshtml", $"@page {template} {Environment.NewLine}<h1>Hello world</h1>"),
|
||||||
|
});
|
||||||
|
var provider = new PageActionDescriptorProvider(
|
||||||
|
razorProject.Object,
|
||||||
|
GetAccessor<MvcOptions>(),
|
||||||
|
GetAccessor<RazorPagesOptions>());
|
||||||
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
|
// Act and Assert
|
||||||
|
var ex = Assert.Throws<InvalidOperationException>(() => provider.OnProvidersExecuting(context));
|
||||||
|
Assert.Equal(
|
||||||
|
"The route for the page at '/Test.cshtml' cannot start with / or ~/. " +
|
||||||
|
"Pages do not support overriding the file path of the page.",
|
||||||
|
ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDescriptors_WithEmptyPageDirective_MapsIndexToEmptySegment()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var razorProject = new Mock<RazorProject>();
|
||||||
|
razorProject.Setup(p => p.EnumerateItems("/"))
|
||||||
|
.Returns(new[]
|
||||||
|
{
|
||||||
|
GetProjectItem("/About", "/Index.cshtml", $"@page {Environment.NewLine}"),
|
||||||
|
});
|
||||||
|
var provider = new PageActionDescriptorProvider(
|
||||||
|
razorProject.Object,
|
||||||
|
GetAccessor<MvcOptions>(),
|
||||||
|
GetAccessor<RazorPagesOptions>());
|
||||||
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(context.Results,
|
||||||
|
result =>
|
||||||
|
{
|
||||||
|
var descriptor = Assert.IsType<PageActionDescriptor>(result);
|
||||||
|
Assert.Equal("/About/Index.cshtml", descriptor.RelativePath);
|
||||||
|
Assert.Equal("/Index", descriptor.RouteValues["page"]);
|
||||||
|
Assert.Equal("About/Index", descriptor.AttributeRouteInfo.Template);
|
||||||
|
},
|
||||||
|
result =>
|
||||||
|
{
|
||||||
|
var descriptor = Assert.IsType<PageActionDescriptor>(result);
|
||||||
|
Assert.Equal("/About/Index.cshtml", descriptor.RelativePath);
|
||||||
|
Assert.Equal("/Index", descriptor.RouteValues["page"]);
|
||||||
|
Assert.Equal("About", descriptor.AttributeRouteInfo.Template);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDescriptors_WithNonEmptyPageDirective_MapsIndexToEmptySegment()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var razorProject = new Mock<RazorProject>();
|
||||||
|
razorProject.Setup(p => p.EnumerateItems("/"))
|
||||||
|
.Returns(new[]
|
||||||
|
{
|
||||||
|
GetProjectItem("/Catalog/Details", "/Index.cshtml", $"@page {{id:int?}} {Environment.NewLine}"),
|
||||||
|
});
|
||||||
|
var provider = new PageActionDescriptorProvider(
|
||||||
|
razorProject.Object,
|
||||||
|
GetAccessor<MvcOptions>(),
|
||||||
|
GetAccessor<RazorPagesOptions>());
|
||||||
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(context.Results,
|
||||||
|
result =>
|
||||||
|
{
|
||||||
|
var descriptor = Assert.IsType<PageActionDescriptor>(result);
|
||||||
|
Assert.Equal("/Catalog/Details/Index.cshtml", descriptor.RelativePath);
|
||||||
|
Assert.Equal("/Index", descriptor.RouteValues["page"]);
|
||||||
|
Assert.Equal("Catalog/Details/Index/{id:int?}", descriptor.AttributeRouteInfo.Template);
|
||||||
|
},
|
||||||
|
result =>
|
||||||
|
{
|
||||||
|
var descriptor = Assert.IsType<PageActionDescriptor>(result);
|
||||||
|
Assert.Equal("/Catalog/Details/Index.cshtml", descriptor.RelativePath);
|
||||||
|
Assert.Equal("/Index", descriptor.RouteValues["page"]);
|
||||||
|
Assert.Equal("Catalog/Details/{id:int?}", descriptor.AttributeRouteInfo.Template);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDescriptors_AddsGlobalFilters()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var filter1 = Mock.Of<IFilterMetadata>();
|
||||||
|
var filter2 = Mock.Of<IFilterMetadata>();
|
||||||
|
var options = new MvcOptions();
|
||||||
|
options.Filters.Add(filter1);
|
||||||
|
options.Filters.Add(filter2);
|
||||||
|
var razorProject = new Mock<RazorProject>();
|
||||||
|
razorProject.Setup(p => p.EnumerateItems("/"))
|
||||||
|
.Returns(new[]
|
||||||
|
{
|
||||||
|
GetProjectItem("/", "/Home.cshtml", $"@page {Environment.NewLine}"),
|
||||||
|
});
|
||||||
|
var provider = new PageActionDescriptorProvider(
|
||||||
|
razorProject.Object,
|
||||||
|
GetAccessor(options),
|
||||||
|
GetAccessor<RazorPagesOptions>());
|
||||||
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var result = Assert.Single(context.Results);
|
||||||
|
var descriptor = Assert.IsType<PageActionDescriptor>(result);
|
||||||
|
Assert.Collection(descriptor.FilterDescriptors,
|
||||||
|
filterDescriptor =>
|
||||||
|
{
|
||||||
|
Assert.Equal(FilterScope.Global, filterDescriptor.Scope);
|
||||||
|
Assert.Same(filter1, filterDescriptor.Filter);
|
||||||
|
},
|
||||||
|
filterDescriptor =>
|
||||||
|
{
|
||||||
|
Assert.Equal(FilterScope.Global, filterDescriptor.Scope);
|
||||||
|
Assert.Same(filter2, filterDescriptor.Filter);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetDescriptors_AddsFiltersAddedByConvention()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var globalFilter = Mock.Of<IFilterMetadata>();
|
||||||
|
var localFilter = Mock.Of<IFilterMetadata>();
|
||||||
|
var options = new MvcOptions();
|
||||||
|
options.Filters.Add(globalFilter);
|
||||||
|
var convention = new Mock<IPageModelConvention>();
|
||||||
|
convention.Setup(c => c.Apply(It.IsAny<PageModel>()))
|
||||||
|
.Callback((PageModel model) =>
|
||||||
|
{
|
||||||
|
model.Filters.Add(localFilter);
|
||||||
|
});
|
||||||
|
var razorOptions = new RazorPagesOptions();
|
||||||
|
razorOptions.Conventions.Add(convention.Object);
|
||||||
|
|
||||||
|
var razorProject = new Mock<RazorProject>();
|
||||||
|
razorProject.Setup(p => p.EnumerateItems("/"))
|
||||||
|
.Returns(new[]
|
||||||
|
{
|
||||||
|
GetProjectItem("/", "/Home.cshtml", $"@page {Environment.NewLine}"),
|
||||||
|
});
|
||||||
|
var provider = new PageActionDescriptorProvider(
|
||||||
|
razorProject.Object,
|
||||||
|
GetAccessor(options),
|
||||||
|
GetAccessor(razorOptions));
|
||||||
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var result = Assert.Single(context.Results);
|
||||||
|
var descriptor = Assert.IsType<PageActionDescriptor>(result);
|
||||||
|
Assert.Collection(descriptor.FilterDescriptors,
|
||||||
|
filterDescriptor =>
|
||||||
|
{
|
||||||
|
Assert.Equal(FilterScope.Global, filterDescriptor.Scope);
|
||||||
|
Assert.Same(globalFilter, filterDescriptor.Filter);
|
||||||
|
},
|
||||||
|
filterDescriptor =>
|
||||||
|
{
|
||||||
|
Assert.Equal(FilterScope.Action, filterDescriptor.Scope);
|
||||||
|
Assert.Same(localFilter, filterDescriptor.Filter);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IOptions<TOptions> GetAccessor<TOptions>(TOptions options = null)
|
||||||
|
where TOptions : class, new()
|
||||||
|
{
|
||||||
|
var accessor = new Mock<IOptions<TOptions>>();
|
||||||
|
accessor.SetupGet(a => a.Value).Returns(options ?? new TOptions());
|
||||||
|
return accessor.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RazorProjectItem GetProjectItem(string basePath, string path, string content)
|
||||||
|
{
|
||||||
|
var testFileInfo = new TestFileInfo
|
||||||
|
{
|
||||||
|
Content = content,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new DefaultRazorProjectItem(testFileInfo, basePath, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<ProjectGuid>0ab46520-f441-4e01-b444-08f4d23f8b1b</ProjectGuid>
|
<ProjectGuid>0ab46520-f441-4e01-b444-08f4d23f8b1b</ProjectGuid>
|
||||||
|
|
@ -13,9 +12,11 @@
|
||||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -4,11 +4,15 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotnet-test-xunit": "2.2.0-*",
|
"dotnet-test-xunit": "2.2.0-*",
|
||||||
"Microsoft.AspNetCore.Testing": "1.2.0-*",
|
"Microsoft.AspNetCore.Mvc.RazorPages": "1.0.0-*",
|
||||||
|
"Microsoft.AspNetCore.Mvc.TestCommon": {
|
||||||
|
"target": "project"
|
||||||
|
},
|
||||||
"Microsoft.DotNet.InternalAbstractions": "1.0.0",
|
"Microsoft.DotNet.InternalAbstractions": "1.0.0",
|
||||||
"Moq": "4.6.36-*",
|
"Moq": "4.6.36-*",
|
||||||
"xunit": "2.2.0-*"
|
"xunit": "2.2.0-*"
|
||||||
},
|
},
|
||||||
|
"testRunner": "xunit",
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"netcoreapp1.1": {
|
"netcoreapp1.1": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue