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(
|
||||
AttributeRouteModel left,
|
||||
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)
|
||||
{
|
||||
if (left == null && right == null)
|
||||
|
|
@ -143,13 +164,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
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)
|
||||
{
|
||||
return template == null ||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
|
|
@ -14,7 +13,6 @@ using Microsoft.AspNetCore.Mvc.Controllers;
|
|||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Routing.Tree;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// An {0} cannot be created without a valid instance of {1}.
|
||||
/// </summary>
|
||||
|
|
@ -1355,7 +1339,7 @@ namespace Microsoft.AspNetCore.Mvc.Core
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// VaryByQueryKeys requires the response cache middleware.
|
||||
/// '{0}' requires the response cache middleware.
|
||||
/// </summary>
|
||||
internal static string VaryByQueryKeys_Requires_ResponseCachingMiddleware
|
||||
{
|
||||
|
|
@ -1363,7 +1347,7 @@ namespace Microsoft.AspNetCore.Mvc.Core
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// VaryByQueryKeys requires the response cache middleware.
|
||||
/// '{0}' requires the response cache middleware.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
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
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.Mvc.ViewFeatures": { "target": "project" },
|
||||
"Microsoft.AspNetCore.Razor.Evolution": "1.0.0-*",
|
||||
"NETStandard.Library": "1.6.1-*"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
}
|
||||
|
||||
/// <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>
|
||||
internal static string FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified
|
||||
{
|
||||
|
|
@ -171,7 +171,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
}
|
||||
|
||||
/// <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>
|
||||
internal static string FormActionTagHelper_CannotOverrideFormAction
|
||||
{
|
||||
|
|
@ -179,7 +179,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
}
|
||||
|
||||
/// <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>
|
||||
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>
|
||||
/// 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>
|
||||
internal static string FormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ using Microsoft.AspNetCore.Mvc.Controllers;
|
|||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Routing.Tree;
|
||||
using Moq;
|
||||
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>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>0ab46520-f441-4e01-b444-08f4d23f8b1b</ProjectGuid>
|
||||
|
|
@ -13,9 +12,11 @@
|
|||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
@ -4,11 +4,15 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"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",
|
||||
"Moq": "4.6.36-*",
|
||||
"xunit": "2.2.0-*"
|
||||
},
|
||||
"testRunner": "xunit",
|
||||
"frameworks": {
|
||||
"netcoreapp1.1": {
|
||||
"dependencies": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue