parent
6751e3b7ca
commit
f0ae0ce528
|
|
@ -72,9 +72,22 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
|
||||
/// <summary>
|
||||
/// Gets the path relative to the base path for page discovery.
|
||||
/// <para>
|
||||
/// This value is the path of the file without extension, relative to the pages root directory.
|
||||
/// e.g. the <see cref="ViewEnginePath"/> for the file /Pages/Catalog/Antiques.cshtml is <c>/Catalog/Antiques</c>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// In an area, this value is the path of the file without extension, relative to the pages root directory for the specified area.
|
||||
/// e.g. the <see cref="ViewEnginePath"/> for the file Areas/Identity/Pages/Manage/Accounts.cshtml, is <c>/Manage/Accounts</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public string ViewEnginePath => ActionDescriptor.ViewEnginePath;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the area name.
|
||||
/// </summary>
|
||||
public string AreaName => ActionDescriptor.AreaName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the route template for the page.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -48,6 +48,40 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
return Add(new PageApplicationModelConvention(pageName, action));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and adds an <see cref="IPageApplicationModelConvention"/> that invokes an action on the
|
||||
/// <see cref="PageApplicationModel"/> for the page with the specified name located in the specified area.
|
||||
/// </summary>
|
||||
/// <param name="areaName">The name of area.</param>
|
||||
/// <param name="pageName">
|
||||
/// The page name e.g. <c>/Users/List</c>
|
||||
/// <para>
|
||||
/// The page name is the path of the file without extension, relative to the pages root directory for the specified area.
|
||||
/// e.g. the page name for the file Areas/Identity/Pages/Manage/Accounts.cshtml, is <c>/Manage/Accounts</c>.
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <param name="action">The <see cref="Action"/>.</param>
|
||||
/// <returns>The added <see cref="IPageApplicationModelConvention"/>.</returns>
|
||||
public IPageApplicationModelConvention AddAreaPageApplicationModelConvention(
|
||||
string areaName,
|
||||
string pageName,
|
||||
Action<PageApplicationModel> action)
|
||||
{
|
||||
if (string.IsNullOrEmpty(areaName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(areaName));
|
||||
}
|
||||
|
||||
EnsureValidPageName(pageName);
|
||||
|
||||
if (action == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(action));
|
||||
}
|
||||
|
||||
return Add(new PageApplicationModelConvention(areaName, pageName, action));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and adds an <see cref="IPageApplicationModelConvention"/> that invokes an action on
|
||||
/// <see cref="PageApplicationModel"/> instances for all page under the specified folder.
|
||||
|
|
@ -67,6 +101,40 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
return Add(new FolderApplicationModelConvention(folderPath, action));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and adds an <see cref="IPageApplicationModelConvention"/> that invokes an action on
|
||||
/// <see cref="PageApplicationModel"/> instances for all pages under the specified area folder.
|
||||
/// </summary>
|
||||
/// <param name="areaName">The name of area.</param>
|
||||
/// <param name="folderPath">
|
||||
/// The folder path e.g. <c>/Manage/</c>
|
||||
/// <para>
|
||||
/// The folder path is the path of the folder, relative to the pages root directory for the specified area.
|
||||
/// e.g. the folder path for the file Areas/Identity/Pages/Manage/Accounts.cshtml, is <c>/Manage</c>.
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <param name="action">The <see cref="Action"/>.</param>
|
||||
/// <returns>The added <see cref="IPageApplicationModelConvention"/>.</returns>
|
||||
public IPageApplicationModelConvention AddAreaFolderApplicationModelConvention(
|
||||
string areaName,
|
||||
string folderPath,
|
||||
Action<PageApplicationModel> action)
|
||||
{
|
||||
if (string.IsNullOrEmpty(areaName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(areaName));
|
||||
}
|
||||
|
||||
EnsureValidFolderPath(folderPath);
|
||||
|
||||
if (action == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(action));
|
||||
}
|
||||
|
||||
return Add(new FolderApplicationModelConvention(areaName, folderPath, action));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and adds an <see cref="IPageRouteModelConvention"/> that invokes an action on the
|
||||
/// <see cref="PageRouteModel"/> for the page with the specified name.
|
||||
|
|
@ -86,6 +154,37 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
return Add(new PageRouteModelConvention(pageName, action));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and adds an <see cref="IPageRouteModelConvention"/> that invokes an action on the
|
||||
/// <see cref="PageRouteModel"/> for the page with the specified name located in the specified area.
|
||||
/// </summary>
|
||||
/// <param name="areaName">The area name.</param>
|
||||
/// <param name="pageName">
|
||||
/// The page name e.g. <c>/Users/List</c>
|
||||
/// <para>
|
||||
/// The page name is the path of the file without extension, relative to the pages root directory for the specified area.
|
||||
/// e.g. the page name for the file Areas/Identity/Pages/Manage/Accounts.cshtml, is <c>/Manage/Accounts</c>.
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <param name="action">The <see cref="Action"/>.</param>
|
||||
/// <returns>The added <see cref="IPageRouteModelConvention"/>.</returns>
|
||||
public IPageRouteModelConvention AddAreaPageRouteModelConvention(string areaName, string pageName, Action<PageRouteModel> action)
|
||||
{
|
||||
if (string.IsNullOrEmpty(areaName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(areaName));
|
||||
}
|
||||
|
||||
EnsureValidPageName(pageName);
|
||||
|
||||
if (action == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(action));
|
||||
}
|
||||
|
||||
return Add(new PageRouteModelConvention(areaName, pageName, action));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and adds an <see cref="IPageRouteModelConvention"/> that invokes an action on
|
||||
/// <see cref="PageRouteModel"/> instances for all page under the specified folder.
|
||||
|
|
@ -105,6 +204,37 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
return Add(new FolderRouteModelConvention(folderPath, action));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and adds an <see cref="IPageRouteModelConvention"/> that invokes an action on
|
||||
/// <see cref="PageRouteModel"/> instances for all page under the specified area folder.
|
||||
/// </summary>
|
||||
/// <param name="areaName">The area name.</param>
|
||||
/// <param name="folderPath">
|
||||
/// The folder path e.g. <c>/Manage/</c>
|
||||
/// <para>
|
||||
/// The folder path is the path of the folder, relative to the pages root directory for the specified area.
|
||||
/// e.g. the folder path for the file Areas/Identity/Pages/Manage/Accounts.cshtml, is <c>/Manage</c>.
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <param name="action">The <see cref="Action"/>.</param>
|
||||
/// <returns>The added <see cref="IPageApplicationModelConvention"/>.</returns>
|
||||
public IPageRouteModelConvention AddAreaFolderRouteModelConvention(string areaName, string folderPath, Action<PageRouteModel> action)
|
||||
{
|
||||
if (string.IsNullOrEmpty(areaName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(areaName));
|
||||
}
|
||||
|
||||
EnsureValidFolderPath(folderPath);
|
||||
|
||||
if (action == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(action));
|
||||
}
|
||||
|
||||
return Add(new FolderRouteModelConvention(areaName, folderPath, action));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all <see cref="IPageConvention"/> instances of the specified type.
|
||||
/// </summary>
|
||||
|
|
@ -158,7 +288,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
}
|
||||
}
|
||||
|
||||
private TConvention Add<TConvention>(TConvention convention) where TConvention: IPageConvention
|
||||
private TConvention Add<TConvention>(TConvention convention) where TConvention : IPageConvention
|
||||
{
|
||||
base.Add(convention);
|
||||
return convention;
|
||||
|
|
@ -166,18 +296,26 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
|
||||
private class PageRouteModelConvention : IPageRouteModelConvention
|
||||
{
|
||||
private readonly string _areaName;
|
||||
private readonly string _path;
|
||||
private readonly Action<PageRouteModel> _action;
|
||||
|
||||
public PageRouteModelConvention(string path, Action<PageRouteModel> action)
|
||||
: this(null, path, action)
|
||||
{
|
||||
}
|
||||
|
||||
public PageRouteModelConvention(string areaName, string path, Action<PageRouteModel> action)
|
||||
{
|
||||
_areaName = areaName;
|
||||
_path = path;
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public void Apply(PageRouteModel model)
|
||||
{
|
||||
if (string.Equals(model.ViewEnginePath, _path, StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(_areaName, model.AreaName, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(model.ViewEnginePath, _path, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_action(model);
|
||||
}
|
||||
|
|
@ -186,20 +324,26 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
|
||||
private class FolderRouteModelConvention : IPageRouteModelConvention
|
||||
{
|
||||
private readonly string _areaName;
|
||||
private readonly string _folderPath;
|
||||
private readonly Action<PageRouteModel> _action;
|
||||
|
||||
public FolderRouteModelConvention(string folderPath, Action<PageRouteModel> action)
|
||||
: this(null, folderPath, action)
|
||||
{
|
||||
}
|
||||
|
||||
public FolderRouteModelConvention(string areaName, string folderPath, Action<PageRouteModel> action)
|
||||
{
|
||||
_areaName = areaName;
|
||||
_folderPath = folderPath.TrimEnd('/');
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public void Apply(PageRouteModel model)
|
||||
{
|
||||
var viewEnginePath = model.ViewEnginePath;
|
||||
|
||||
if (PathBelongsToFolder(_folderPath, viewEnginePath))
|
||||
if (string.Equals(_areaName, model.AreaName, StringComparison.OrdinalIgnoreCase) &&
|
||||
PathBelongsToFolder(_folderPath, model.ViewEnginePath))
|
||||
{
|
||||
_action(model);
|
||||
}
|
||||
|
|
@ -208,18 +352,26 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
|
||||
private class PageApplicationModelConvention : IPageApplicationModelConvention
|
||||
{
|
||||
private readonly string _areaName;
|
||||
private readonly string _path;
|
||||
private readonly Action<PageApplicationModel> _action;
|
||||
|
||||
public PageApplicationModelConvention(string path, Action<PageApplicationModel> action)
|
||||
: this(null, path, action)
|
||||
{
|
||||
}
|
||||
|
||||
public PageApplicationModelConvention(string areaName, string path, Action<PageApplicationModel> action)
|
||||
{
|
||||
_areaName = areaName;
|
||||
_path = path;
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public void Apply(PageApplicationModel model)
|
||||
{
|
||||
if (string.Equals(model.ViewEnginePath, _path, StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(model.ViewEnginePath, _path, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(model.AreaName, _areaName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_action(model);
|
||||
}
|
||||
|
|
@ -228,20 +380,26 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
|
||||
private class FolderApplicationModelConvention : IPageApplicationModelConvention
|
||||
{
|
||||
private readonly string _areaName;
|
||||
private readonly string _folderPath;
|
||||
private readonly Action<PageApplicationModel> _action;
|
||||
|
||||
public FolderApplicationModelConvention(string folderPath, Action<PageApplicationModel> action)
|
||||
: this(null, folderPath, action)
|
||||
{
|
||||
}
|
||||
|
||||
public FolderApplicationModelConvention(string areaName, string folderPath, Action<PageApplicationModel> action)
|
||||
{
|
||||
_areaName = areaName;
|
||||
_folderPath = folderPath.TrimEnd('/');
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public void Apply(PageApplicationModel model)
|
||||
{
|
||||
var viewEnginePath = model.ViewEnginePath;
|
||||
|
||||
if (PathBelongsToFolder(_folderPath, viewEnginePath))
|
||||
if (string.Equals(_areaName, model.AreaName, StringComparison.OrdinalIgnoreCase) &&
|
||||
PathBelongsToFolder(_folderPath, model.ViewEnginePath))
|
||||
{
|
||||
_action(model);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,21 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
/// <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 PageRouteModel(string relativePath, string viewEnginePath)
|
||||
: this(relativePath, viewEnginePath, areaName: null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="PageRouteModel"/>.
|
||||
/// </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>
|
||||
/// <param name="areaName">The area name.</param>
|
||||
public PageRouteModel(string relativePath, string viewEnginePath, string areaName)
|
||||
{
|
||||
RelativePath = relativePath ?? throw new ArgumentNullException(nameof(relativePath));
|
||||
ViewEnginePath = viewEnginePath ?? throw new ArgumentNullException(nameof(viewEnginePath));
|
||||
AreaName = areaName;
|
||||
|
||||
Properties = new Dictionary<object, object>();
|
||||
Selectors = new List<SelectorModel>();
|
||||
|
|
@ -43,6 +55,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
|
||||
RelativePath = other.RelativePath;
|
||||
ViewEnginePath = other.ViewEnginePath;
|
||||
AreaName = other.AreaName;
|
||||
|
||||
Properties = new Dictionary<object, object>(other.Properties);
|
||||
Selectors = new List<SelectorModel>(other.Selectors.Select(m => new SelectorModel(m)));
|
||||
|
|
@ -56,12 +69,22 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
|
||||
/// <summary>
|
||||
/// Gets the path relative to the base path for page discovery.
|
||||
/// <para>
|
||||
/// This value is the path of the file without extension, relative to the pages root directory.
|
||||
/// e.g. the <see cref="ViewEnginePath"/> for the file /Pages/Catalog/Antiques.cshtml is <c>/Catalog/Antiques</c>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// In an area, this value is the path of the file without extension, relative to the pages root directory for the specified area.
|
||||
/// e.g. the <see cref="ViewEnginePath"/> for the file Areas/Identity/Pages/Manage/Accounts.cshtml, is <c>/Manage/Accounts</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For area pages, this path is calculated relative to the <see cref="RazorPagesOptions.RootDirectory"/> of the specific area.
|
||||
/// </remarks>
|
||||
public string ViewEnginePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the area name. Will be <c>null</c> for non-area pages.
|
||||
/// </summary>
|
||||
public string AreaName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Stores arbitrary metadata properties associated with the <see cref="PageRouteModel"/>.
|
||||
/// </summary>
|
||||
|
|
@ -79,7 +102,14 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
/// <remarks>
|
||||
/// <para>
|
||||
/// The value of <see cref="ViewEnginePath"/> is considered an implicit route value corresponding
|
||||
/// to the key <c>page</c>. These entries will be implicitly added to <see cref="ActionDescriptor.RouteValues"/>
|
||||
/// to the key <c>page</c>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The value of <see cref="AreaName"/> is considered an implicit route value corresponding
|
||||
/// to the key <c>area</c> when <see cref="AreaName"/> is not <c>null</c>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// These entries will be implicitly added to <see cref="ActionDescriptor.RouteValues"/>
|
||||
/// when the action descriptor is created, but will not be visible in <see cref="RouteValues"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
|
|
|
|||
|
|
@ -82,6 +82,44 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
return conventions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="AllowAnonymousFilter"/> to the page with the specified name located in the specified area.
|
||||
/// </summary>
|
||||
/// <param name="conventions">The <see cref="PageConventionCollection"/> to configure.</param>
|
||||
/// <param name="areaName">The area name.</param>
|
||||
/// <param name="pageName">
|
||||
/// The page name e.g. <c>/Users/List</c>
|
||||
/// <para>
|
||||
/// The page name is the path of the file without extension, relative to the pages root directory for the specified area.
|
||||
/// e.g. the page name for the file Areas/Identity/Pages/Manage/Accounts.cshtml, is <c>/Manage/Accounts</c>.
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <returns>The <see cref="PageConventionCollection"/>.</returns>
|
||||
public static PageConventionCollection AllowAnonymousToAreaPage(
|
||||
this PageConventionCollection conventions,
|
||||
string areaName,
|
||||
string pageName)
|
||||
{
|
||||
if (conventions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(conventions));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(areaName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(areaName));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(pageName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
|
||||
}
|
||||
|
||||
var anonymousFilter = new AllowAnonymousFilter();
|
||||
conventions.AddAreaPageApplicationModelConvention(areaName, pageName, model => model.Filters.Add(anonymousFilter));
|
||||
return conventions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="AllowAnonymousFilter"/> to all pages under the specified folder.
|
||||
/// </summary>
|
||||
|
|
@ -105,6 +143,44 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
return conventions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="AllowAnonymousFilter"/> to all pages under the specified area folder.
|
||||
/// </summary>
|
||||
/// <param name="conventions">The <see cref="PageConventionCollection"/> to configure.</param>
|
||||
/// <param name="areaName">The area name.</param>
|
||||
/// <param name="folderPath">
|
||||
/// The folder path e.g. <c>/Manage/</c>
|
||||
/// <para>
|
||||
/// The folder path is the path of the folder, relative to the pages root directory for the specified area.
|
||||
/// e.g. the folder path for the file Areas/Identity/Pages/Manage/Accounts.cshtml, is <c>/Manage</c>.
|
||||
/// </para>
|
||||
///.</param>
|
||||
/// <returns>The <see cref="PageConventionCollection"/>.</returns>
|
||||
public static PageConventionCollection AllowAnonymousToAreaFolder(
|
||||
this PageConventionCollection conventions,
|
||||
string areaName,
|
||||
string folderPath)
|
||||
{
|
||||
if (conventions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(conventions));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(areaName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(areaName));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(folderPath))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(folderPath));
|
||||
}
|
||||
|
||||
var anonymousFilter = new AllowAnonymousFilter();
|
||||
conventions.AddAreaFolderApplicationModelConvention(areaName, folderPath, model => model.Filters.Add(anonymousFilter));
|
||||
return conventions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="AuthorizeFilter"/> with the specified policy to the page with the specified name.
|
||||
/// </summary>
|
||||
|
|
@ -138,6 +214,62 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
public static PageConventionCollection AuthorizePage(this PageConventionCollection conventions, string pageName) =>
|
||||
AuthorizePage(conventions, pageName, policy: string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="AuthorizeFilter"/> with default policy to the page with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="conventions">The <see cref="PageConventionCollection"/> to configure.</param>
|
||||
/// <param name="areaName">The area name.</param>
|
||||
/// <param name="pageName">
|
||||
/// The page name e.g. <c>/Users/List</c>
|
||||
/// <para>
|
||||
/// The page name is the path of the file without extension, relative to the pages root directory for the specified area.
|
||||
/// e.g. the page name for the file Areas/Identity/Pages/Manage/Accounts.cshtml, is <c>/Manage/Accounts</c>.
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <returns>The <see cref="PageConventionCollection"/>.</returns>
|
||||
public static PageConventionCollection AuthorizeAreaPage(this PageConventionCollection conventions, string areaName, string pageName)
|
||||
=> AuthorizeAreaPage(conventions, areaName, pageName, policy: string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="AuthorizeFilter"/> with the specified policy to the page with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="conventions">The <see cref="PageConventionCollection"/> to configure.</param>
|
||||
/// <param name="areaName">The area name.</param>
|
||||
/// <param name="pageName">
|
||||
/// The page name e.g. <c>/Users/List</c>
|
||||
/// <para>
|
||||
/// The page name is the path of the file without extension, relative to the pages root directory for the specified area.
|
||||
/// e.g. the page name for the file Areas/Identity/Pages/Manage/Accounts.cshtml, is <c>/Manage/Accounts</c>.
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <param name="policy">The authorization policy.</param>
|
||||
/// <returns>The <see cref="PageConventionCollection"/>.</returns>
|
||||
public static PageConventionCollection AuthorizeAreaPage(
|
||||
this PageConventionCollection conventions,
|
||||
string areaName,
|
||||
string pageName,
|
||||
string policy)
|
||||
{
|
||||
if (conventions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(conventions));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(areaName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(areaName));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(pageName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
|
||||
}
|
||||
|
||||
var authorizeFilter = new AuthorizeFilter(policy);
|
||||
conventions.AddAreaPageApplicationModelConvention(areaName, pageName, model => model.Filters.Add(authorizeFilter));
|
||||
return conventions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="AuthorizeFilter"/> with the specified policy to all pages under the specified folder.
|
||||
/// </summary>
|
||||
|
|
@ -171,6 +303,62 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
public static PageConventionCollection AuthorizeFolder(this PageConventionCollection conventions, string folderPath) =>
|
||||
AuthorizeFolder(conventions, folderPath, policy: string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="AuthorizeFilter"/> with the default policy to all pages under the specified folder.
|
||||
/// </summary>
|
||||
/// <param name="conventions">The <see cref="PageConventionCollection"/> to configure.</param>
|
||||
/// <param name="areaName">The area name.</param>
|
||||
/// <param name="folderPath">
|
||||
/// The folder path e.g. <c>/Manage/</c>
|
||||
/// <para>
|
||||
/// The folder path is the path of the folder, relative to the pages root directory for the specified area.
|
||||
/// e.g. the folder path for the file Areas/Identity/Pages/Manage/Accounts.cshtml, is <c>/Manage</c>.
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <returns>The <see cref="PageConventionCollection"/>.</returns>
|
||||
public static PageConventionCollection AuthorizeAreaFolder(this PageConventionCollection conventions, string areaName, string folderPath)
|
||||
=> AuthorizeAreaFolder(conventions, areaName, folderPath, policy: string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="AuthorizeFilter"/> with the specified policy to all pages under the specified folder.
|
||||
/// </summary>
|
||||
/// <param name="conventions">The <see cref="PageConventionCollection"/> to configure.</param>
|
||||
/// <param name="areaName">The area name.</param>
|
||||
/// <param name="folderPath">
|
||||
/// The folder path e.g. <c>/Manage/</c>
|
||||
/// <para>
|
||||
/// The folder path is the path of the folder, relative to the pages root directory for the specified area.
|
||||
/// e.g. the folder path for the file Areas/Identity/Pages/Manage/Accounts.cshtml, is <c>/Manage</c>.
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <param name="policy">The authorization policy.</param>
|
||||
/// <returns>The <see cref="PageConventionCollection"/>.</returns>
|
||||
public static PageConventionCollection AuthorizeAreaFolder(
|
||||
this PageConventionCollection conventions,
|
||||
string areaName,
|
||||
string folderPath,
|
||||
string policy)
|
||||
{
|
||||
if (conventions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(conventions));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(areaName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(areaName));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(folderPath))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(folderPath));
|
||||
}
|
||||
|
||||
var authorizeFilter = new AuthorizeFilter(policy);
|
||||
conventions.AddAreaFolderApplicationModelConvention(areaName, folderPath, model => model.Filters.Add(authorizeFilter));
|
||||
return conventions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified <paramref name="route"/> to the page at the specified <paramref name="pageName"/>.
|
||||
/// <para>
|
||||
|
|
@ -199,7 +387,64 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
throw new ArgumentNullException(nameof(route));
|
||||
}
|
||||
|
||||
conventions.AddPageRouteModelConvention(pageName, model =>
|
||||
conventions.AddPageRouteModelConvention(pageName, AddPageRouteThunk(route));
|
||||
|
||||
return conventions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified <paramref name="route"/> to the page at the specified <paramref name="pageName"/> located in the specified
|
||||
/// area.
|
||||
/// <para>
|
||||
/// The page can be routed via <paramref name="route"/> in addition to the default set of path based routes.
|
||||
/// All links generated for this page will use the specified route.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="conventions">The <see cref="PageConventionCollection"/>.</param>
|
||||
/// <param name="areaName">The area name.</param>
|
||||
/// <param name="pageName">
|
||||
/// The page name e.g. <c>/Users/List</c>
|
||||
/// <para>
|
||||
/// The page name is the path of the file without extension, relative to the pages root directory for the specified area.
|
||||
/// e.g. the page name for the file Areas/Identity/Pages/Manage/Accounts.cshtml, is <c>/Manage/Accounts</c>.
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <param name="route">The route to associate with the page.</param>
|
||||
/// <returns>The <see cref="PageConventionCollection"/>.</returns>
|
||||
public static PageConventionCollection AddAreaPageRoute(
|
||||
this PageConventionCollection conventions,
|
||||
string areaName,
|
||||
string pageName,
|
||||
string route)
|
||||
{
|
||||
if (conventions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(conventions));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(areaName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(areaName));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(pageName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
|
||||
}
|
||||
|
||||
if (route == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(route));
|
||||
}
|
||||
|
||||
conventions.AddAreaPageRouteModelConvention(areaName, pageName, AddPageRouteThunk(route));
|
||||
|
||||
return conventions;
|
||||
}
|
||||
|
||||
private static Action<PageRouteModel> AddPageRouteThunk(string route)
|
||||
{
|
||||
return model =>
|
||||
{
|
||||
// Use the route specified in MapPageRoute for outbound routing.
|
||||
foreach (var selector in model.Selectors)
|
||||
|
|
@ -214,9 +459,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
Template = route,
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return conventions;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,9 +73,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
|
||||
foreach (var selector in model.Selectors)
|
||||
{
|
||||
var descriptor = new PageActionDescriptor()
|
||||
var descriptor = new PageActionDescriptor
|
||||
{
|
||||
AttributeRouteInfo = new AttributeRouteInfo()
|
||||
AttributeRouteInfo = new AttributeRouteInfo
|
||||
{
|
||||
Name = selector.AttributeRouteModel.Name,
|
||||
Order = selector.AttributeRouteModel.Order ?? 0,
|
||||
|
|
@ -88,6 +88,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
Properties = new Dictionary<object, object>(model.Properties),
|
||||
RelativePath = model.RelativePath,
|
||||
ViewEnginePath = model.ViewEnginePath,
|
||||
AreaName = model.AreaName,
|
||||
};
|
||||
|
||||
foreach (var kvp in model.RouteValues)
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
return null;
|
||||
}
|
||||
|
||||
var routeModel = new PageRouteModel(relativePath, areaResult.viewEnginePath);
|
||||
var routeModel = new PageRouteModel(relativePath, areaResult.viewEnginePath, areaResult.areaName);
|
||||
|
||||
var routePrefix = CreateAreaRoute(areaResult.areaName, areaResult.viewEnginePath);
|
||||
PopulateRouteModel(routeModel, routePrefix, routeTemplate);
|
||||
|
|
|
|||
|
|
@ -23,8 +23,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
/// <param name="other">The <see cref="PageActionDescriptor"/> to copy from.</param>
|
||||
public PageActionDescriptor(PageActionDescriptor other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
RelativePath = other.RelativePath;
|
||||
ViewEnginePath = other.ViewEnginePath;
|
||||
AreaName = other.AreaName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -34,9 +40,23 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
|
||||
/// <summary>
|
||||
/// Gets or sets the path relative to the base path for page discovery.
|
||||
/// <para>
|
||||
/// This value is the path of the file without extension, relative to the pages root directory.
|
||||
/// e.g. the <see cref="ViewEnginePath"/> for the file /Pages/Catalog/Antiques.cshtml is <c>/Catalog/Antiques</c>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// In an area, this value is the path of the file without extension, relative to the pages root directory for the specified area.
|
||||
/// e.g. the <see cref="ViewEnginePath"/> for the file Areas/Identity/Pages/Manage/Accounts.cshtml, is <c>/Manage/Accounts</c>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public string ViewEnginePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the area name for this page.
|
||||
/// This value will be <c>null</c> for non-area pages.
|
||||
/// </summary>
|
||||
public string AreaName { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DisplayName
|
||||
{
|
||||
|
|
|
|||
|
|
@ -370,5 +370,26 @@ Hello from /Pages/Shared/";
|
|||
// Assert
|
||||
Assert.Equal(expected, response.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AuthorizeFolderConvention_CanBeAppliedToAreaPages()
|
||||
{
|
||||
// Act
|
||||
var response = await Client.GetAsync("/Accounts/RequiresAuth");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||
Assert.Equal("/Login?ReturnUrl=%2FAccounts%2FRequiresAuth", response.Headers.Location.PathAndQuery);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AllowAnonymouseToPageConvention_CanBeAppliedToAreaPages()
|
||||
{
|
||||
// Act
|
||||
var response = await Client.GetStringAsync("/Accounts/RequiresAuth/AllowAnonymous");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Hello from AllowAnonymous", response.Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,31 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AllowAnonymousToAreaPage_AddsAllowAnonymousFilterToSpecificPage()
|
||||
{
|
||||
// Arrange
|
||||
var conventions = new PageConventionCollection();
|
||||
var models = new[]
|
||||
{
|
||||
CreateApplicationModel("/Profile.cshtml", "/Profile"),
|
||||
CreateApplicationModel("/Areas/Accounts/Pages/Profile.cshtml", "/Profile", "Accounts"),
|
||||
};
|
||||
|
||||
// Act
|
||||
conventions.AllowAnonymousToAreaPage("Accounts", "/Profile");
|
||||
ApplyConventions(conventions, models);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(models,
|
||||
model => Assert.Empty(model.Filters),
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Areas/Accounts/Pages/Profile.cshtml", model.RelativePath);
|
||||
Assert.IsType<AllowAnonymousFilter>(Assert.Single(model.Filters));
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/Users")]
|
||||
[InlineData("/Users/")]
|
||||
|
|
@ -112,6 +137,41 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AllowAnonymousToAreaFolder_AddsAllowAnonymousFilterToFolderInArea()
|
||||
{
|
||||
// Arrange
|
||||
var conventions = new PageConventionCollection();
|
||||
var models = new[]
|
||||
{
|
||||
CreateApplicationModel("/Profile.cshtml", "/Profile"),
|
||||
CreateApplicationModel("/Mange/Profile.cshtml", "/Manage/Profile"),
|
||||
CreateApplicationModel("/Areas/Accounts/Pages/Manage/Profile.cshtml", "/Manage/Profile", "Accounts"),
|
||||
CreateApplicationModel("/Areas/Accounts/Pages/Manage/2FA.cshtml", "/Manage/2FA", "Accounts"),
|
||||
CreateApplicationModel("/Areas/Accounts/Pages/View/OrderHistory.cshtml", "/View/OrderHistory", "Accounts"),
|
||||
};
|
||||
|
||||
// Act
|
||||
conventions.AllowAnonymousToAreaFolder("Accounts", "/Manage");
|
||||
ApplyConventions(conventions, models);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(models,
|
||||
model => Assert.Empty(model.Filters),
|
||||
model => Assert.Empty(model.Filters),
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Areas/Accounts/Pages/Manage/Profile.cshtml", model.RelativePath);
|
||||
Assert.IsType<AllowAnonymousFilter>(Assert.Single(model.Filters));
|
||||
},
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Areas/Accounts/Pages/Manage/2FA.cshtml", model.RelativePath);
|
||||
Assert.IsType<AllowAnonymousFilter>(Assert.Single(model.Filters));
|
||||
},
|
||||
model => Assert.Empty(model.Filters));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AuthorizePage_AddsAuthorizeFilterWithPolicyToSpecificPage()
|
||||
{
|
||||
|
|
@ -141,6 +201,60 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
model => Assert.Empty(model.Filters));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AuthorizeAreaPage_AddsAuthorizeFilterWithDefaultPolicyToAreaPage()
|
||||
{
|
||||
// Arrange
|
||||
var conventions = new PageConventionCollection();
|
||||
var models = new[]
|
||||
{
|
||||
CreateApplicationModel("/Profile.cshtml", "/Profile"),
|
||||
CreateApplicationModel("/Areas/Accounts/Pages/Profile.cshtml", "/Profile", "Accounts"),
|
||||
};
|
||||
|
||||
// Act
|
||||
conventions.AuthorizeAreaPage("Accounts", "/Profile");
|
||||
ApplyConventions(conventions, models);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(models,
|
||||
model => Assert.Empty(model.Filters),
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Areas/Accounts/Pages/Profile.cshtml", model.RelativePath);
|
||||
var authFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(model.Filters));
|
||||
var authorizeAttribute = Assert.Single(authFilter.AuthorizeData);
|
||||
Assert.Empty(authorizeAttribute.Policy);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AuthorizeAreaPage_AddsAuthorizeFilterWithCustomPolicyToAreaPage()
|
||||
{
|
||||
// Arrange
|
||||
var conventions = new PageConventionCollection();
|
||||
var models = new[]
|
||||
{
|
||||
CreateApplicationModel("/Profile.cshtml", "/Profile"),
|
||||
CreateApplicationModel("/Areas/Accounts/Pages/Profile.cshtml", "/Profile", "Accounts"),
|
||||
};
|
||||
|
||||
// Act
|
||||
conventions.AuthorizeAreaPage("Accounts", "/Profile", "custom");
|
||||
ApplyConventions(conventions, models);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(models,
|
||||
model => Assert.Empty(model.Filters),
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Areas/Accounts/Pages/Profile.cshtml", model.RelativePath);
|
||||
var authFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(model.Filters));
|
||||
var authorizeAttribute = Assert.Single(authFilter.AuthorizeData);
|
||||
Assert.Equal("custom", authorizeAttribute.Policy);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AuthorizePage_AddsAuthorizeFilterWithoutPolicyToSpecificPage()
|
||||
{
|
||||
|
|
@ -244,6 +358,84 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AuthorizeAreaFolder_AddsAuthorizeFilterWithDefaultPolicyToAreaPagesInFolder()
|
||||
{
|
||||
// Arrange
|
||||
var conventions = new PageConventionCollection();
|
||||
var models = new[]
|
||||
{
|
||||
CreateApplicationModel("/Profile.cshtml", "/Profile"),
|
||||
CreateApplicationModel("/Mange/Profile.cshtml", "/Manage/Profile"),
|
||||
CreateApplicationModel("/Areas/Accounts/Pages/Manage/Profile.cshtml", "/Manage/Profile", "Accounts"),
|
||||
CreateApplicationModel("/Areas/Accounts/Pages/Manage/2FA.cshtml", "/Manage/2FA", "Accounts"),
|
||||
CreateApplicationModel("/Areas/Accounts/Pages/View/OrderHistory.cshtml", "/View/OrderHistory", "Accounts"),
|
||||
};
|
||||
|
||||
// Act
|
||||
conventions.AuthorizeAreaFolder("Accounts", "/Manage");
|
||||
ApplyConventions(conventions, models);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(models,
|
||||
model => Assert.Empty(model.Filters),
|
||||
model => Assert.Empty(model.Filters),
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Areas/Accounts/Pages/Manage/Profile.cshtml", model.RelativePath);
|
||||
var authorizeFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(model.Filters));
|
||||
var authorizeData = Assert.IsType<AuthorizeAttribute>(Assert.Single(authorizeFilter.AuthorizeData));
|
||||
Assert.Empty(authorizeData.Policy);
|
||||
},
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Areas/Accounts/Pages/Manage/2FA.cshtml", model.RelativePath);
|
||||
var authorizeFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(model.Filters));
|
||||
var authorizeData = Assert.IsType<AuthorizeAttribute>(Assert.Single(authorizeFilter.AuthorizeData));
|
||||
Assert.Empty(authorizeData.Policy);
|
||||
},
|
||||
model => Assert.Empty(model.Filters));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AuthorizeAreaFolder_AddsAuthorizeFilterWithCustomPolicyToAreaPagesInFolder()
|
||||
{
|
||||
// Arrange
|
||||
var conventions = new PageConventionCollection();
|
||||
var models = new[]
|
||||
{
|
||||
CreateApplicationModel("/Profile.cshtml", "/Profile"),
|
||||
CreateApplicationModel("/Mange/Profile.cshtml", "/Manage/Profile"),
|
||||
CreateApplicationModel("/Areas/Accounts/Pages/Manage/Profile.cshtml", "/Manage/Profile", "Accounts"),
|
||||
CreateApplicationModel("/Areas/Accounts/Pages/Manage/2FA.cshtml", "/Manage/2FA", "Accounts"),
|
||||
CreateApplicationModel("/Areas/Accounts/Pages/View/OrderHistory.cshtml", "/View/OrderHistory", "Accounts"),
|
||||
};
|
||||
|
||||
// Act
|
||||
conventions.AuthorizeAreaFolder("Accounts", "/Manage", "custom");
|
||||
ApplyConventions(conventions, models);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(models,
|
||||
model => Assert.Empty(model.Filters),
|
||||
model => Assert.Empty(model.Filters),
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Areas/Accounts/Pages/Manage/Profile.cshtml", model.RelativePath);
|
||||
var authorizeFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(model.Filters));
|
||||
var authorizeData = Assert.IsType<AuthorizeAttribute>(Assert.Single(authorizeFilter.AuthorizeData));
|
||||
Assert.Equal("custom", authorizeData.Policy);
|
||||
},
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Areas/Accounts/Pages/Manage/2FA.cshtml", model.RelativePath);
|
||||
var authorizeFilter = Assert.IsType<AuthorizeFilter>(Assert.Single(model.Filters));
|
||||
var authorizeData = Assert.IsType<AuthorizeAttribute>(Assert.Single(authorizeFilter.AuthorizeData));
|
||||
Assert.Equal("custom", authorizeData.Policy);
|
||||
},
|
||||
model => Assert.Empty(model.Filters));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddPageRoute_AddsRouteToSelector()
|
||||
{
|
||||
|
|
@ -306,6 +498,62 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddAreaPageRoute_AddsRouteToSelector()
|
||||
{
|
||||
// Arrange
|
||||
var conventions = new PageConventionCollection();
|
||||
var models = new[]
|
||||
{
|
||||
new PageRouteModel("/Pages/Profile.cshtml", "/Profile")
|
||||
{
|
||||
Selectors =
|
||||
{
|
||||
CreateSelectorModel("Profile"),
|
||||
}
|
||||
},
|
||||
new PageRouteModel("/Areas/Accounts/Pages/Profile.cshtml", "/Profile", "Accounts")
|
||||
{
|
||||
Selectors =
|
||||
{
|
||||
CreateSelectorModel("Accounts/Profile"),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
conventions.AddAreaPageRoute("Accounts", "/Profile", "Different-Route");
|
||||
ApplyConventions(conventions, models);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(models,
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Pages/Profile.cshtml", model.RelativePath);
|
||||
Assert.Collection(model.Selectors,
|
||||
selector =>
|
||||
{
|
||||
Assert.Equal("Profile", selector.AttributeRouteModel.Template);
|
||||
Assert.False(selector.AttributeRouteModel.SuppressLinkGeneration);
|
||||
});
|
||||
},
|
||||
model =>
|
||||
{
|
||||
Assert.Equal("/Areas/Accounts/Pages/Profile.cshtml", model.RelativePath);
|
||||
Assert.Collection(model.Selectors,
|
||||
selector =>
|
||||
{
|
||||
Assert.Equal("Accounts/Profile", selector.AttributeRouteModel.Template);
|
||||
Assert.True (selector.AttributeRouteModel.SuppressLinkGeneration);
|
||||
},
|
||||
selector =>
|
||||
{
|
||||
Assert.Equal("Different-Route", selector.AttributeRouteModel.Template);
|
||||
Assert.False(selector.AttributeRouteModel.SuppressLinkGeneration);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static SelectorModel CreateSelectorModel(string template, bool suppressLinkGeneration = false)
|
||||
{
|
||||
return new SelectorModel
|
||||
|
|
@ -339,12 +587,13 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
}
|
||||
|
||||
private PageApplicationModel CreateApplicationModel(string relativePath, string viewEnginePath)
|
||||
private PageApplicationModel CreateApplicationModel(string relativePath, string viewEnginePath, string areaName = null)
|
||||
{
|
||||
var descriptor = new PageActionDescriptor
|
||||
{
|
||||
ViewEnginePath = viewEnginePath,
|
||||
RelativePath = relativePath,
|
||||
AreaName = areaName,
|
||||
};
|
||||
|
||||
return new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), new object[0]);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -65,6 +66,107 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
Assert.Equal("/Test/{id:int?}", descriptor.AttributeRouteInfo.Template);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_AddsDescriptorsForAreaPages()
|
||||
{
|
||||
// Arrange
|
||||
var model = new PageRouteModel("/Test.cshtml", "/Test")
|
||||
{
|
||||
RouteValues =
|
||||
{
|
||||
{ "custom-key", "custom-value" },
|
||||
},
|
||||
Selectors =
|
||||
{
|
||||
new SelectorModel
|
||||
{
|
||||
AttributeRouteModel = new AttributeRouteModel
|
||||
{
|
||||
Template = "/Test/{id:int?}",
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
var applicationModelProvider = new TestPageRouteModelProvider(model);
|
||||
var provider = new PageActionDescriptorProvider(
|
||||
new[] { applicationModelProvider },
|
||||
GetAccessor<MvcOptions>(),
|
||||
GetRazorPagesOptions());
|
||||
var context = new ActionDescriptorProviderContext();
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var result = Assert.Single(context.Results);
|
||||
var descriptor = Assert.IsType<PageActionDescriptor>(result);
|
||||
Assert.Equal(model.RelativePath, descriptor.RelativePath);
|
||||
Assert.Collection(
|
||||
descriptor.RouteValues.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("custom-key", kvp.Key);
|
||||
Assert.Equal("custom-value", kvp.Value);
|
||||
},
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("page", kvp.Key);
|
||||
Assert.Equal("/Test", kvp.Value);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_CopiesRouteValuesFromModel()
|
||||
{
|
||||
// Arrange
|
||||
var model = new PageRouteModel("/Areas/Accounts/Pages/Test.cshtml", "/Test", "Accounts")
|
||||
{
|
||||
RouteValues =
|
||||
{
|
||||
{ "page", "/Test" },
|
||||
{ "area", "Accounts" },
|
||||
},
|
||||
Selectors =
|
||||
{
|
||||
new SelectorModel
|
||||
{
|
||||
AttributeRouteModel = new AttributeRouteModel
|
||||
{
|
||||
Template = "Accounts/Test/{id:int?}",
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
var applicationModelProvider = new TestPageRouteModelProvider(model);
|
||||
var provider = new PageActionDescriptorProvider(
|
||||
new[] { applicationModelProvider },
|
||||
GetAccessor<MvcOptions>(),
|
||||
GetRazorPagesOptions());
|
||||
var context = new ActionDescriptorProviderContext();
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var result = Assert.Single(context.Results);
|
||||
var descriptor = Assert.IsType<PageActionDescriptor>(result);
|
||||
Assert.Equal(model.RelativePath, descriptor.RelativePath);
|
||||
Assert.Collection(
|
||||
descriptor.RouteValues.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("area", kvp.Key);
|
||||
Assert.Equal("Accounts", kvp.Value);
|
||||
},
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("page", kvp.Key);
|
||||
Assert.Equal("/Test", kvp.Value);
|
||||
});
|
||||
Assert.Equal("Accounts", descriptor.AreaName);
|
||||
Assert.Equal("Accounts/Test/{id:int?}", descriptor.AttributeRouteInfo.Template);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_AddsActionDescriptorForEachSelector()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
// Assert
|
||||
Assert.Equal(relativePath, routeModel.RelativePath);
|
||||
Assert.Equal("/Users/Profile", routeModel.ViewEnginePath);
|
||||
Assert.Null(routeModel.AreaName);
|
||||
|
||||
Assert.Collection(
|
||||
routeModel.Selectors,
|
||||
|
|
@ -109,6 +110,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
// Assert
|
||||
Assert.Equal(relativePath, routeModel.RelativePath);
|
||||
Assert.Equal("/Users/Profile", routeModel.ViewEnginePath);
|
||||
Assert.Equal("TestArea", routeModel.AreaName);
|
||||
|
||||
Assert.Collection(
|
||||
routeModel.Selectors,
|
||||
|
|
@ -142,6 +144,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
// Assert
|
||||
Assert.Equal(relativePath, routeModel.RelativePath);
|
||||
Assert.Equal("/Users/Profile/Index", routeModel.ViewEnginePath);
|
||||
Assert.Equal("TestArea", routeModel.AreaName);
|
||||
|
||||
Assert.Collection(
|
||||
routeModel.Selectors,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
@page
|
||||
Hello from AllowAnonymous
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
@page
|
||||
@{ throw new Exception("This should not be rendered."); }
|
||||
|
|
@ -20,6 +20,8 @@ namespace RazorPagesWebSite
|
|||
options.AllowAreas = true;
|
||||
options.Conventions.AuthorizePage("/Conventions/Auth");
|
||||
options.Conventions.AuthorizeFolder("/Conventions/AuthFolder");
|
||||
options.Conventions.AuthorizeAreaFolder("Accounts", "/RequiresAuth");
|
||||
options.Conventions.AllowAnonymousToAreaPage("Accounts", "/RequiresAuth/AllowAnonymous");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue