diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageApplicationModelConvention.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageApplicationModelConvention.cs
index 2ffe77c41b..deb53ee82d 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageApplicationModelConvention.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageApplicationModelConvention.cs
@@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
///
/// Allows customization of the of the .
///
- public interface IPageApplicationModelConvention
+ public interface IPageApplicationModelConvention : IPageConvention
{
///
/// Called to apply the convention to the .
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageConvention.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageConvention.cs
new file mode 100644
index 0000000000..3b23349536
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageConvention.cs
@@ -0,0 +1,12 @@
+// 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
+{
+ ///
+ /// Common interface for route and application model conventions that apply to Razor Pages.
+ ///
+ public interface IPageConvention
+ {
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageRouteModelConvention.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageRouteModelConvention.cs
index cfa332a465..7d54b8bcd7 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageRouteModelConvention.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageRouteModelConvention.cs
@@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
///
/// Allows customization of the of the .
///
- public interface IPageRouteModelConvention
+ public interface IPageRouteModelConvention : IPageConvention
{
///
/// Called to apply the convention to the .
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/PageConventionCollection.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/PageConventionCollection.cs
new file mode 100644
index 0000000000..51c36fcad0
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/PageConventionCollection.cs
@@ -0,0 +1,222 @@
+// 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.ObjectModel;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Microsoft.AspNetCore.Mvc.ApplicationModels
+{
+ public class PageConventionCollection : Collection
+ {
+ ///
+ /// Creates and adds an that invokes an action on the
+ /// for the page with the speciifed name.
+ ///
+ /// The name of the page e.g. /Users/List
+ /// The .
+ /// The added .
+ public IPageApplicationModelConvention AddPageApplicationModelConvention(
+ string pageName,
+ Action action)
+ {
+ EnsureValidPageName(pageName);
+
+ if (action == null)
+ {
+ throw new ArgumentNullException(nameof(action));
+ }
+
+ return Add(new PageApplicationModelConvention(pageName, action));
+ }
+
+ ///
+ /// Creates and adds an that invokes an action on
+ /// instances for all page under the specified folder.
+ ///
+ /// The path of the folder relative to the Razor Pages root. e.g. /Users/
+ /// The .
+ /// The added .
+ public IPageApplicationModelConvention AddFolderApplicationModelConvention(string folderPath, Action action)
+ {
+ EnsureValidFolderPath(folderPath);
+
+ if (action == null)
+ {
+ throw new ArgumentNullException(nameof(action));
+ }
+
+ return Add(new FolderApplicationModelConvention(folderPath, action));
+ }
+
+ ///
+ /// Creates and adds an that invokes an action on the
+ /// for the page with the speciifed name.
+ ///
+ /// The name of the page e.g. /Users/List
+ /// The .
+ /// The added .
+ public IPageRouteModelConvention AddPageRouteModelConvention(string pageName, Action action)
+ {
+ EnsureValidPageName(pageName);
+
+ if (action == null)
+ {
+ throw new ArgumentNullException(nameof(action));
+ }
+
+ return Add(new PageRouteModelConvention(pageName, action));
+ }
+
+ ///
+ /// Creates and adds an that invokes an action on
+ /// instances for all page under the specified folder.
+ ///
+ /// The path of the folder relative to the Razor Pages root. e.g. /Users/
+ /// The .
+ /// The added .
+ public IPageRouteModelConvention AddFolderRouteModelConvention(string folderPath, Action action)
+ {
+ EnsureValidFolderPath(folderPath);
+
+ if (action == null)
+ {
+ throw new ArgumentNullException(nameof(action));
+ }
+
+ return Add(new FolderRouteModelConvention(folderPath, action));
+ }
+
+ // Internal for unit testing
+ internal static void EnsureValidPageName(string pageName)
+ {
+ if (string.IsNullOrEmpty(pageName))
+ {
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
+ }
+
+ if (pageName[0] != '/' || pageName.EndsWith(".cshtml", StringComparison.OrdinalIgnoreCase))
+ {
+ throw new ArgumentException(Resources.FormatInvalidValidPageName(pageName), nameof(pageName));
+ }
+ }
+
+ // Internal for unit testing
+ internal static void EnsureValidFolderPath(string folderPath)
+ {
+ if (string.IsNullOrEmpty(folderPath))
+ {
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(folderPath));
+ }
+
+ if (folderPath[0] != '/')
+ {
+ throw new ArgumentException(Resources.PathMustBeRootRelativePath, nameof(folderPath));
+ }
+ }
+
+ private TConvention Add(TConvention convention) where TConvention: IPageConvention
+ {
+ base.Add(convention);
+ return convention;
+ }
+
+ private class PageRouteModelConvention : IPageRouteModelConvention
+ {
+ private readonly string _path;
+ private readonly Action _action;
+
+ public PageRouteModelConvention(string path, Action action)
+ {
+ _path = path;
+ _action = action;
+ }
+
+ public void Apply(PageRouteModel model)
+ {
+ if (string.Equals(model.ViewEnginePath, _path, StringComparison.OrdinalIgnoreCase))
+ {
+ _action(model);
+ }
+ }
+ }
+
+ private class FolderRouteModelConvention : IPageRouteModelConvention
+ {
+ private readonly string _folderPath;
+ private readonly Action _action;
+
+ public FolderRouteModelConvention(string folderPath, Action action)
+ {
+ _folderPath = folderPath.TrimEnd('/');
+ _action = action;
+ }
+
+ public void Apply(PageRouteModel model)
+ {
+ var viewEnginePath = model.ViewEnginePath;
+
+ if (PathBelongsToFolder(_folderPath, viewEnginePath))
+ {
+ _action(model);
+ }
+ }
+ }
+
+ private class PageApplicationModelConvention : IPageApplicationModelConvention
+ {
+ private readonly string _path;
+ private readonly Action _action;
+
+ public PageApplicationModelConvention(string path, Action action)
+ {
+ _path = path;
+ _action = action;
+ }
+
+ public void Apply(PageApplicationModel model)
+ {
+ if (string.Equals(model.ViewEnginePath, _path, StringComparison.OrdinalIgnoreCase))
+ {
+ _action(model);
+ }
+ }
+ }
+
+ private class FolderApplicationModelConvention : IPageApplicationModelConvention
+ {
+ private readonly string _folderPath;
+ private readonly Action _action;
+
+ public FolderApplicationModelConvention(string folderPath, Action action)
+ {
+ _folderPath = folderPath.TrimEnd('/');
+ _action = action;
+ }
+
+ public void Apply(PageApplicationModel model)
+ {
+ var viewEnginePath = model.ViewEnginePath;
+
+ if (PathBelongsToFolder(_folderPath, viewEnginePath))
+ {
+ _action(model);
+ }
+ }
+ }
+
+ // Internal for unit testing
+ internal static bool PathBelongsToFolder(string folderPath, string viewEnginePath)
+ {
+ if (folderPath == "/")
+ {
+ // Root directory covers everything.
+ return true;
+ }
+
+ return viewEnginePath.Length > folderPath.Length &&
+ viewEnginePath.StartsWith(folderPath, StringComparison.OrdinalIgnoreCase) &&
+ viewEnginePath[folderPath.Length] == '/';
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcBuilderExtensions.cs
index 0655c3bb7d..6340d87005 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcBuilderExtensions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/MvcRazorPagesMvcBuilderExtensions.cs
@@ -57,7 +57,7 @@ namespace Microsoft.Extensions.DependencyInjection
if (rootDirectory[0] != '/')
{
- throw new ArgumentException(Resources.PathMustBeAnAppRelativePath, nameof(rootDirectory));
+ throw new ArgumentException(Resources.PathMustBeRootRelativePath, nameof(rootDirectory));
}
builder.Services.Configure(options => options.RootDirectory = rootDirectory);
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/PageConventionCollectionExtensions.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/PageConventionCollectionExtensions.cs
new file mode 100644
index 0000000000..b04072f217
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/PageConventionCollectionExtensions.cs
@@ -0,0 +1,222 @@
+// 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.ApplicationModels;
+using Microsoft.AspNetCore.Mvc.Authorization;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ ///
+ /// Extensions for .
+ ///
+ public static class PageConventionCollectionExtensions
+ {
+ ///
+ /// Configures the specified to apply filters to all Razor Pages.
+ ///
+ /// The to configure.
+ /// The factory to create filters.
+ ///
+ public static IPageApplicationModelConvention ConfigureFilter(
+ this PageConventionCollection conventions,
+ Func factory)
+ {
+ if (conventions == null)
+ {
+ throw new ArgumentNullException(nameof(conventions));
+ }
+
+ if (factory == null)
+ {
+ throw new ArgumentNullException(nameof(factory));
+ }
+
+ return conventions.AddFolderApplicationModelConvention("/", model => model.Filters.Add(factory(model)));
+ }
+
+ ///
+ /// Configures the specified to apply to all Razor Pages.
+ ///
+ /// The to configure.
+ /// The to add.
+ /// The .
+ public static PageConventionCollection ConfigureFilter(this PageConventionCollection conventions, IFilterMetadata filter)
+ {
+ if (conventions == null)
+ {
+ throw new ArgumentNullException(nameof(conventions));
+ }
+
+ if (filter == null)
+ {
+ throw new ArgumentNullException(nameof(filter));
+ }
+
+ conventions.AddFolderApplicationModelConvention("/", model => model.Filters.Add(filter));
+ return conventions;
+ }
+
+ ///
+ /// Adds a to the page with the specified name.
+ ///
+ /// The to configure.
+ /// The page name.
+ /// The .
+ public static PageConventionCollection AllowAnonymousToPage(this PageConventionCollection conventions, string pageName)
+ {
+ if (conventions == null)
+ {
+ throw new ArgumentNullException(nameof(conventions));
+ }
+
+ if (string.IsNullOrEmpty(pageName))
+ {
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
+ }
+
+ var anonymousFilter = new AllowAnonymousFilter();
+ conventions.AddPageApplicationModelConvention(pageName, model => model.Filters.Add(anonymousFilter));
+ return conventions;
+ }
+
+ ///
+ /// Adds a to all pages under the specified folder.
+ ///
+ /// The to configure.
+ /// The folder path.
+ /// The .
+ public static PageConventionCollection AllowAnonymousToFolder(this PageConventionCollection conventions, string folderPath)
+ {
+ if (conventions == null)
+ {
+ throw new ArgumentNullException(nameof(conventions));
+ }
+
+ if (string.IsNullOrEmpty(folderPath))
+ {
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(folderPath));
+ }
+
+ var anonymousFilter = new AllowAnonymousFilter();
+ conventions.AddFolderApplicationModelConvention(folderPath, model => model.Filters.Add(anonymousFilter));
+ return conventions;
+ }
+
+ ///
+ /// Adds a with the specified policy to the page with the specified name.
+ ///
+ /// The to configure.
+ /// The page name.
+ /// The authorization policy.
+ /// The .
+ public static PageConventionCollection AuthorizePage(this PageConventionCollection conventions, string pageName, string policy)
+ {
+ if (conventions == null)
+ {
+ throw new ArgumentNullException(nameof(conventions));
+ }
+
+ if (string.IsNullOrEmpty(pageName))
+ {
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
+ }
+
+ var authorizeFilter = new AuthorizeFilter(policy);
+ conventions.AddPageApplicationModelConvention(pageName, model => model.Filters.Add(authorizeFilter));
+ return conventions;
+ }
+
+ ///
+ /// Adds a to the page with the specified name.
+ ///
+ /// The to configure.
+ /// The page name.
+ /// The .
+ public static PageConventionCollection AuthorizePage(this PageConventionCollection conventions, string pageName) =>
+ AuthorizePage(conventions, pageName, policy: string.Empty);
+
+ ///
+ /// Adds a with the specified policy to all pages under the specified folder.
+ ///
+ /// The to configure.
+ /// The folder path.
+ /// The authorization policy.
+ /// The .
+ public static PageConventionCollection AuthorizeFolder(this PageConventionCollection conventions, string folderPath, string policy)
+ {
+ if (conventions == null)
+ {
+ throw new ArgumentNullException(nameof(conventions));
+ }
+
+ if (string.IsNullOrEmpty(folderPath))
+ {
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(folderPath));
+ }
+
+ var authorizeFilter = new AuthorizeFilter(policy);
+ conventions.AddFolderApplicationModelConvention(folderPath, model => model.Filters.Add(authorizeFilter));
+ return conventions;
+ }
+
+ ///
+ /// Adds a to all pages under the specified folder.
+ ///
+ /// The to configure.
+ /// The folder path.
+ /// The .
+ public static PageConventionCollection AuthorizeFolder(this PageConventionCollection conventions, string folderPath) =>
+ AuthorizeFolder(conventions, folderPath, policy: string.Empty);
+
+ ///
+ /// Adds the specified to the page at the specified .
+ ///
+ /// The page can be routed via in addition to the default set of path based routes.
+ /// All links generated for this page will use the specified route.
+ ///
+ ///
+ /// The .
+ /// The page name.
+ /// The route to associate with the page.
+ /// The .
+ public static PageConventionCollection AddPageRoute(this PageConventionCollection conventions, string pageName, string route)
+ {
+ if (conventions == null)
+ {
+ throw new ArgumentNullException(nameof(conventions));
+ }
+
+ if (string.IsNullOrEmpty(pageName))
+ {
+ throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
+ }
+
+ if (route == null)
+ {
+ throw new ArgumentNullException(nameof(route));
+ }
+
+ conventions.AddPageRouteModelConvention(pageName, model =>
+ {
+ // Use the route specified in MapPageRoute for outbound routing.
+ foreach (var selector in model.Selectors)
+ {
+ selector.AttributeRouteModel.SuppressLinkGeneration = true;
+ }
+
+ model.Selectors.Add(new SelectorModel
+ {
+ AttributeRouteModel = new AttributeRouteModel
+ {
+ Template = route,
+ }
+ });
+ });
+
+ return conventions;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/RazorPagesOptionsExtensions.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/RazorPagesOptionsExtensions.cs
deleted file mode 100644
index bbc9582f9e..0000000000
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/RazorPagesOptionsExtensions.cs
+++ /dev/null
@@ -1,318 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using Microsoft.AspNetCore.Mvc.ApplicationModels;
-using Microsoft.AspNetCore.Mvc.Authorization;
-using Microsoft.AspNetCore.Mvc.Filters;
-using Microsoft.AspNetCore.Mvc.RazorPages;
-
-namespace Microsoft.Extensions.DependencyInjection
-{
- ///
- /// Extensions for .
- ///
- public static class RazorPagesOptionsExtensions
- {
- ///
- /// Configures the specified to apply filters to all Razor Pages.
- ///
- /// The to configure.
- /// The factory to create filters.
- ///
- public static RazorPagesOptions ConfigureFilter(this RazorPagesOptions options, Func factory)
- {
- if (options == null)
- {
- throw new ArgumentNullException(nameof(options));
- }
-
- if (factory == null)
- {
- throw new ArgumentNullException(nameof(factory));
- }
-
- options.ApplicationModelConventions.Add(new FolderApplicationModelConvention("/", model => model.Filters.Add(factory(model))));
- return options;
- }
-
- ///
- /// Configures the specified to apply to all Razor Pages.
- ///
- /// The to configure.
- /// The to add.
- /// The .
- public static RazorPagesOptions ConfigureFilter(this RazorPagesOptions options, IFilterMetadata filter)
- {
- if (options == null)
- {
- throw new ArgumentNullException(nameof(options));
- }
-
- if (filter == null)
- {
- throw new ArgumentNullException(nameof(filter));
- }
-
- options.ApplicationModelConventions.Add(new FolderApplicationModelConvention("/", model => model.Filters.Add(filter)));
- return options;
- }
-
- ///
- /// Adds a to the page with the specified name.
- ///
- /// The to configure.
- /// The page name.
- /// The .
- public static RazorPagesOptions AllowAnonymousToPage(this RazorPagesOptions options, string pageName)
- {
- if (options == null)
- {
- throw new ArgumentNullException(nameof(options));
- }
-
- if (string.IsNullOrEmpty(pageName))
- {
- throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
- }
-
- var anonymousFilter = new AllowAnonymousFilter();
- options.ApplicationModelConventions.Add(new PageApplicationModelConvention(pageName, model => model.Filters.Add(anonymousFilter)));
- return options;
- }
-
- ///
- /// Adds a to all pages under the specified folder.
- ///
- /// The to configure.
- /// The folder path.
- /// The .
- public static RazorPagesOptions AllowAnonymousToFolder(this RazorPagesOptions options, string folderPath)
- {
- if (options == null)
- {
- throw new ArgumentNullException(nameof(options));
- }
-
- if (string.IsNullOrEmpty(folderPath))
- {
- throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(folderPath));
- }
-
- var anonymousFilter = new AllowAnonymousFilter();
- options.ApplicationModelConventions.Add(new FolderApplicationModelConvention(folderPath, model => model.Filters.Add(anonymousFilter)));
- return options;
- }
-
- ///
- /// Adds a with the specified policy to the page with the specified name.
- ///
- /// The to configure.
- /// The page name.
- /// The authorization policy.
- /// The .
- public static RazorPagesOptions AuthorizePage(this RazorPagesOptions options, string pageName, string policy)
- {
- if (options == null)
- {
- throw new ArgumentNullException(nameof(options));
- }
-
- if (string.IsNullOrEmpty(pageName))
- {
- throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
- }
-
- var authorizeFilter = new AuthorizeFilter(policy);
- options.ApplicationModelConventions.Add(new PageApplicationModelConvention(pageName, model => model.Filters.Add(authorizeFilter)));
- return options;
- }
-
- ///
- /// Adds a to the page with the specified name.
- ///
- /// The to configure.
- /// The page name.
- /// The .
- public static RazorPagesOptions AuthorizePage(this RazorPagesOptions options, string pageName) =>
- AuthorizePage(options, pageName, policy: string.Empty);
-
- ///
- /// Adds a with the specified policy to all pages under the specified folder.
- ///
- /// The to configure.
- /// The folder path.
- /// The authorization policy.
- /// The .
- public static RazorPagesOptions AuthorizeFolder(this RazorPagesOptions options, string folderPath, string policy)
- {
- if (options == null)
- {
- throw new ArgumentNullException(nameof(options));
- }
-
- if (string.IsNullOrEmpty(folderPath))
- {
- throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(folderPath));
- }
-
- var authorizeFilter = new AuthorizeFilter(policy);
- options.ApplicationModelConventions.Add(new FolderApplicationModelConvention(folderPath, model => model.Filters.Add(authorizeFilter)));
- return options;
- }
-
- ///
- /// Adds a to all pages under the specified folder.
- ///
- /// The to configure.
- /// The folder path.
- /// The .
- public static RazorPagesOptions AuthorizeFolder(this RazorPagesOptions options, string folderPath) =>
- AuthorizeFolder(options, folderPath, policy: string.Empty);
-
- ///
- /// Adds the specified to the page at the specified .
- ///
- /// The page can be routed via in addition to the default set of path based routes.
- /// All links generated for this page will use the specified route.
- ///
- ///
- /// The .
- /// The page name.
- /// The route to associate with the page.
- /// The .
- public static RazorPagesOptions AddPageRoute(this RazorPagesOptions options, string pageName, string route)
- {
- if (options == null)
- {
- throw new ArgumentNullException(nameof(options));
- }
-
- if (string.IsNullOrEmpty(pageName))
- {
- throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
- }
-
- if (route == null)
- {
- throw new ArgumentNullException(nameof(route));
- }
-
- options.RouteModelConventions.Add(new PageRouteModelConvention(pageName, model =>
- {
- // Use the route specified in MapPageRoute for outbound routing.
- foreach (var selector in model.Selectors)
- {
- selector.AttributeRouteModel.SuppressLinkGeneration = true;
- }
-
- model.Selectors.Add(new SelectorModel
- {
- AttributeRouteModel = new AttributeRouteModel
- {
- Template = route,
- }
- });
- }));
-
- return options;
- }
-
- private class PageRouteModelConvention : IPageRouteModelConvention
- {
- private readonly string _path;
- private readonly Action _action;
-
- public PageRouteModelConvention(string path, Action action)
- {
- _path = path;
- _action = action;
- }
-
- public void Apply(PageRouteModel model)
- {
- if (string.Equals(model.ViewEnginePath, _path, StringComparison.OrdinalIgnoreCase))
- {
- _action(model);
- }
- }
- }
-
- private class FolderRouteModelConvention : IPageRouteModelConvention
- {
- private readonly string _folderPath;
- private readonly Action _action;
-
- public FolderRouteModelConvention(string folderPath, Action action)
- {
- _folderPath = folderPath.TrimEnd('/');
- _action = action;
- }
-
- public void Apply(PageRouteModel model)
- {
- var viewEnginePath = model.ViewEnginePath;
-
- if (PathBelongsToFolder(_folderPath, viewEnginePath))
- {
- _action(model);
- }
- }
- }
-
- private class PageApplicationModelConvention : IPageApplicationModelConvention
- {
- private readonly string _path;
- private readonly Action _action;
-
- public PageApplicationModelConvention(string path, Action action)
- {
- _path = path;
- _action = action;
- }
-
- public void Apply(PageApplicationModel model)
- {
- if (string.Equals(model.ViewEnginePath, _path, StringComparison.OrdinalIgnoreCase))
- {
- _action(model);
- }
- }
- }
-
- private class FolderApplicationModelConvention : IPageApplicationModelConvention
- {
- private readonly string _folderPath;
- private readonly Action _action;
-
- public FolderApplicationModelConvention(string folderPath, Action action)
- {
- _folderPath = folderPath.TrimEnd('/');
- _action = action;
- }
-
- public void Apply(PageApplicationModel model)
- {
- var viewEnginePath = model.ViewEnginePath;
-
- if (PathBelongsToFolder(_folderPath, viewEnginePath))
- {
- _action(model);
- }
- }
- }
-
- private static bool PathBelongsToFolder(string folderPath, string viewEnginePath)
- {
- if (folderPath == "/")
- {
- // Root directory covers everything.
- return true;
- }
-
- return viewEnginePath.Length > folderPath.Length &&
- viewEnginePath.StartsWith(folderPath, StringComparison.OrdinalIgnoreCase) &&
- viewEnginePath[folderPath.Length] == '/';
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs
index 0c4970b5cf..5c02a67644 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs
@@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
private readonly IPageRouteModelProvider[] _routeModelProviders;
private readonly MvcOptions _mvcOptions;
- private readonly RazorPagesOptions _pagesOptions;
+ private readonly IPageRouteModelConvention[] _conventions;
public PageActionDescriptorProvider(
IEnumerable pageRouteModelProviders,
@@ -25,7 +25,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
_routeModelProviders = pageRouteModelProviders.OrderBy(p => p.Order).ToArray();
_mvcOptions = mvcOptionsAccessor.Value;
- _pagesOptions = pagesOptionsAccessor.Value;
+
+ _conventions = pagesOptionsAccessor.Value.Conventions
+ .OfType()
+ .ToArray();
}
public int Order { get; set; } = -900; // Run after the default MVC provider, but before others.
@@ -63,9 +66,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
private void AddActionDescriptors(IList actions, PageRouteModel model)
{
- for (var i = 0; i < _pagesOptions.RouteModelConventions.Count; i++)
+ for (var i = 0; i < _conventions.Length; i++)
{
- _pagesOptions.RouteModelConventions[i].Apply(model);
+ _conventions[i].Apply(model);
}
foreach (var selector in model.Selectors)
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs
index f0c588e932..bfce828103 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs
@@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
private readonly IPageApplicationModelProvider[] _applicationModelProviders;
private readonly IViewCompilerProvider _viewCompilerProvider;
- private readonly RazorPagesOptions _options;
+ private readonly IPageApplicationModelConvention[] _conventions;
public DefaultPageLoader(
IEnumerable applicationModelProviders,
@@ -25,7 +25,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
_applicationModelProviders = applicationModelProviders.ToArray();
_viewCompilerProvider = viewCompilerProvider;
- _options = pageOptions.Value;
+ _conventions = pageOptions.Value.Conventions
+ .OfType()
+ .ToArray();
}
private IViewCompiler Compiler => _viewCompilerProvider.GetCompiler();
@@ -52,9 +54,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
_applicationModelProviders[i].OnProvidersExecuted(context);
}
- for (var i = 0; i < _options.ApplicationModelConventions.Count; i++)
+ for (var i = 0; i < _conventions.Length; i++)
{
- _options.ApplicationModelConventions[i].Apply(context.PageApplicationModel);
+ _conventions[i].Apply(context.PageApplicationModel);
}
return CompiledPageActionDescriptorBuilder.Build(context.PageApplicationModel);
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Properties/Resources.Designer.cs
index 4e9aa8a0d3..c2cd73be9f 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Properties/Resources.Designer.cs
@@ -109,18 +109,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
=> string.Format(CultureInfo.CurrentCulture, GetString("AmbiguousHandler"), p0, p1);
///
- /// Path must be an application relative path that starts with a forward slash '/'.
+ /// Path must be a root relative path that starts with a forward slash '/'.
///
- internal static string PathMustBeAnAppRelativePath
+ internal static string PathMustBeRootRelativePath
{
- get => GetString("PathMustBeAnAppRelativePath");
+ get => GetString("PathMustBeRootRelativePath");
}
///
- /// Path must be an application relative path that starts with a forward slash '/'.
+ /// Path must be a root relative path that starts with a forward slash '/'.
///
- internal static string FormatPathMustBeAnAppRelativePath()
- => GetString("PathMustBeAnAppRelativePath");
+ internal static string FormatPathMustBeRootRelativePath()
+ => GetString("PathMustBeRootRelativePath");
///
/// If an {0} provides a result value by setting the {1} property of {2} to a non-null value, then it cannot call the next filter by invoking {3}.
@@ -164,6 +164,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
internal static string FormatInvalidPageType_NoModelProperty(object p0, object p1)
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidPageType_NoModelProperty"), p0, p1);
+ ///
+ /// '{0}' is not a valid page name. A page name is path relative to the Razor Pages root directory that starts with a leading forward slash ('/') and does not contain the file extension e.g "/Users/Edit".
+ ///
+ internal static string InvalidValidPageName
+ {
+ get => GetString("InvalidValidPageName");
+ }
+
+ ///
+ /// '{0}' is not a valid page name. A page name is path relative to the Razor Pages root directory that starts with a leading forward slash ('/') and does not contain the file extension e.g "/Users/Edit".
+ ///
+ internal static string FormatInvalidValidPageName(object p0)
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidValidPageName"), p0);
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/RazorPagesOptions.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/RazorPagesOptions.cs
index e562414a7a..8bb833d4fc 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/RazorPagesOptions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/RazorPagesOptions.cs
@@ -4,8 +4,6 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
-using Microsoft.AspNetCore.Mvc.Filters;
-using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
namespace Microsoft.AspNetCore.Mvc.RazorPages
{
@@ -17,16 +15,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
private string _root = "/Pages";
///
- /// Gets a list of instances that will be applied to
- /// the when discovering Razor Pages.
+ /// Gets a collection of instances that are applied during
+ /// route and page model construction.
///
- public IList RouteModelConventions { get; } = new List();
-
- ///
- /// Gets a list of instances that will be applied to
- /// the when discovering Razor Pages.
- ///
- public IList ApplicationModelConventions { get; } = new List();
+ public PageConventionCollection Conventions { get; } = new PageConventionCollection();
///
/// Application relative path used as the root of discovery for Razor Page files.
@@ -44,7 +36,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
if (value[0] != '/')
{
- throw new ArgumentException(Resources.PathMustBeAnAppRelativePath, nameof(value));
+ throw new ArgumentException(Resources.PathMustBeRootRelativePath, nameof(value));
}
_root = value;
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Resources.resx b/src/Microsoft.AspNetCore.Mvc.RazorPages/Resources.resx
index 6b80508128..be0f94f186 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Resources.resx
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Resources.resx
@@ -138,8 +138,8 @@
Multiple handlers matched. The following handlers matched route data and had all constraints satisfied:{0}{0}{1}
-
- Path must be an application relative path that starts with a forward slash '/'.
+
+ Path must be a root relative path that starts with a forward slash '/'.
If an {0} provides a result value by setting the {1} property of {2} to a non-null value, then it cannot call the next filter by invoking {3}.
@@ -150,4 +150,7 @@
The type '{0}' is not a valid page. A page must define a public, non-static '{1}' property.
+
+ '{0}' is not a valid page name. A page name is path relative to the Razor Pages root directory that starts with a leading forward slash ('/') and does not contain the file extension e.g "/Users/Edit".
+
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/ApplicationModels/PageConventionCollectionTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/ApplicationModels/PageConventionCollectionTest.cs
new file mode 100644
index 0000000000..ee6b4603a1
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/ApplicationModels/PageConventionCollectionTest.cs
@@ -0,0 +1,76 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNetCore.Testing;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Mvc.ApplicationModels
+{
+ public class PageConventionCollectionTest
+ {
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ public void EnsureValidPageName_ThrowsIfPageNameIsNullOrEmpty(string pageName)
+ {
+ // Act & Assert
+ var ex = ExceptionAssert.ThrowsArgument(
+ () => PageConventionCollection.EnsureValidPageName(pageName),
+ "pageName",
+ "Value cannot be null or empty.");
+ }
+
+ [Theory]
+ [InlineData("path-without-slash")]
+ [InlineData(@"c:\myapp\path-without-slash")]
+ public void EnsureValidPageName_ThrowsIfPageNameDoesNotStartWithLeadingSlash(string pageName)
+ {
+ // Arrange
+ var expected = $"'{pageName}' is not a valid page name. A page name is path relative to the Razor Pages root directory that starts with a leading forward slash ('/') and does not contain the file extension e.g \"/Users/Edit\".";
+ // Act & Assert
+ var ex = ExceptionAssert.ThrowsArgument(
+ () => PageConventionCollection.EnsureValidPageName(pageName),
+ "pageName",
+ expected);
+ }
+
+ [Fact]
+ public void EnsureValidPageName_ThrowsIfPageNameHasExtension()
+ {
+ // Arrange
+ var pageName = "/Page.cshtml";
+ var expected = $"'{pageName}' is not a valid page name. A page name is path relative to the Razor Pages root directory that starts with a leading forward slash ('/') and does not contain the file extension e.g \"/Users/Edit\".";
+ // Act & Assert
+ var ex = ExceptionAssert.ThrowsArgument(
+ () => PageConventionCollection.EnsureValidPageName(pageName),
+ "pageName",
+ expected);
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ public void EnsureValidFolderPath_ThrowsIfPathIsNullOrEmpty(string folderPath)
+ {
+ // Arrange
+ // Act & Assert
+ var ex = ExceptionAssert.ThrowsArgument(
+ () => PageConventionCollection.EnsureValidFolderPath(folderPath),
+ "folderPath",
+ "Value cannot be null or empty.");
+ }
+
+ [Theory]
+ [InlineData("path-without-slash")]
+ [InlineData(@"c:\myapp\path-without-slash")]
+ public void EnsureValidFolderPath_ThrowsIfPageNameDoesNotStartWithLeadingSlash(string folderPath)
+ {
+ // Arrange
+ // Act & Assert
+ var ex = ExceptionAssert.ThrowsArgument(
+ () => PageConventionCollection.EnsureValidFolderPath(folderPath),
+ "folderPath",
+ "Path must be a root relative path that starts with a forward slash '/'.");
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/MvcRazorPagesMvcBuilderExtensionsTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/MvcRazorPagesMvcBuilderExtensionsTest.cs
index 0d57132c92..4b209a45fd 100644
--- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/MvcRazorPagesMvcBuilderExtensionsTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/MvcRazorPagesMvcBuilderExtensionsTest.cs
@@ -14,7 +14,7 @@ namespace Microsoft.Extensions.DependencyInjection
public class MvcRazorPagesMvcBuilderExtensionsTest
{
[Fact]
- public void AddRazorPagesOptions_AddsApplicationModelConventions()
+ public void AddRazorPagesOptions_AddsConventions()
{
// Arrange
var services = new ServiceCollection().AddOptions();
@@ -23,18 +23,20 @@ namespace Microsoft.Extensions.DependencyInjection
var builder = new MvcBuilder(services, new ApplicationPartManager());
builder.AddRazorPagesOptions(options =>
{
- options.ApplicationModelConventions.Add(applicationModelConvention);
- options.RouteModelConventions.Add(routeModelConvention);
+ options.Conventions.Add(applicationModelConvention);
+ options.Conventions.Add(routeModelConvention);
});
var serviceProvider = services.BuildServiceProvider();
var accessor = serviceProvider.GetRequiredService>();
// Act & Assert
- var conventions = accessor.Value.ApplicationModelConventions;
+ var conventions = accessor.Value.Conventions;
// Assert
- Assert.Collection(conventions,
- convention => Assert.Same(applicationModelConvention, convention));
+ Assert.Collection(
+ conventions,
+ convention => Assert.Same(applicationModelConvention, convention),
+ convention => Assert.Same(routeModelConvention, convention));
}
}
}
diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/RazorPagesOptionsExtensionsTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/PageConventionCollectionExtensionsTest.cs
similarity index 76%
rename from test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/RazorPagesOptionsExtensionsTest.cs
rename to test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/PageConventionCollectionExtensionsTest.cs
index 99d45ad3b0..b6ea5e394e 100644
--- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/RazorPagesOptionsExtensionsTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/DependencyInjection/PageConventionCollectionExtensionsTest.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
@@ -12,24 +13,24 @@ using Xunit;
namespace Microsoft.Extensions.DependencyInjection
{
- public class RazorPagesOptionsExtensionsTest
+ public class PageConventionCollectionExtensionsTest
{
[Fact]
public void AddFilter_AddsFiltersToAllPages()
{
// Arrange
var filter = Mock.Of();
- var options = new RazorPagesOptions();
+ var conventions = new PageConventionCollection();
var models = new[]
{
- CreateApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
- CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
- CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
+ CreateApplicationModel("/Pages/Index.cshtml", "/Index"),
+ CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account"),
+ CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact"),
};
// Act
- options.ConfigureFilter(filter);
- ApplyConventions(options, models);
+ conventions.ConfigureFilter(filter);
+ ApplyConventions(conventions, models);
// Assert
Assert.Collection(models,
@@ -42,30 +43,30 @@ namespace Microsoft.Extensions.DependencyInjection
public void AuthorizePage_AddsAllowAnonymousFilterToSpecificPage()
{
// Arrange
- var options = new RazorPagesOptions();
+ var conventions = new PageConventionCollection();
var models = new[]
{
- CreateApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
- CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
- CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
+ CreateApplicationModel("/Pages/Index.cshtml", "/Index"),
+ CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account"),
+ CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact"),
};
// Act
- options.AuthorizeFolder("/Users");
- options.AllowAnonymousToPage("/Users/Contact.cshtml");
- ApplyConventions(options, models);
+ conventions.AuthorizeFolder("/Users");
+ conventions.AllowAnonymousToPage("/Users/Contact");
+ ApplyConventions(conventions, models);
// Assert
Assert.Collection(models,
model => Assert.Empty(model.Filters),
model =>
{
- Assert.Equal("/Users/Account.cshtml", model.ViewEnginePath);
+ Assert.Equal("/Users/Account", model.ViewEnginePath);
Assert.IsType(Assert.Single(model.Filters));
},
model =>
{
- Assert.Equal("/Users/Contact.cshtml", model.ViewEnginePath);
+ Assert.Equal("/Users/Contact", model.ViewEnginePath);
Assert.IsType(model.Filters[0]);
Assert.IsType(model.Filters[1]);
});
@@ -77,35 +78,35 @@ namespace Microsoft.Extensions.DependencyInjection
public void AuthorizePage_AddsAllowAnonymousFilterToPagesUnderFolder(string folderName)
{
// Arrange
- var options = new RazorPagesOptions();
+ var conventions = new PageConventionCollection();
var models = new[]
{
- CreateApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
- CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
- CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
+ CreateApplicationModel("/Pages/Index.cshtml", "/Index"),
+ CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account"),
+ CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact"),
};
// Act
- options.AuthorizeFolder("/");
- options.AllowAnonymousToFolder("/Users");
- ApplyConventions(options, models);
+ conventions.AuthorizeFolder("/");
+ conventions.AllowAnonymousToFolder("/Users");
+ ApplyConventions(conventions, models);
// Assert
Assert.Collection(models,
model =>
{
- Assert.Equal("/Index.cshtml", model.ViewEnginePath);
+ Assert.Equal("/Index", model.ViewEnginePath);
Assert.IsType(Assert.Single(model.Filters));
},
model =>
{
- Assert.Equal("/Users/Account.cshtml", model.ViewEnginePath);
+ Assert.Equal("/Users/Account", model.ViewEnginePath);
Assert.IsType(model.Filters[0]);
Assert.IsType(model.Filters[1]);
},
model =>
{
- Assert.Equal("/Users/Contact.cshtml", model.ViewEnginePath);
+ Assert.Equal("/Users/Contact", model.ViewEnginePath);
Assert.IsType(model.Filters[0]);
Assert.IsType(model.Filters[1]);
});
@@ -115,24 +116,24 @@ namespace Microsoft.Extensions.DependencyInjection
public void AuthorizePage_AddsAuthorizeFilterWithPolicyToSpecificPage()
{
// Arrange
- var options = new RazorPagesOptions();
+ var conventions = new PageConventionCollection();
var models = new[]
{
- CreateApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
- CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
- CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
+ CreateApplicationModel("/Pages/Index.cshtml", "/Index"),
+ CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account"),
+ CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact"),
};
// Act
- options.AuthorizePage("/Users/Account.cshtml", "Manage-Accounts");
- ApplyConventions(options, models);
+ conventions.AuthorizePage("/Users/Account", "Manage-Accounts");
+ ApplyConventions(conventions, models);
// Assert
Assert.Collection(models,
model => Assert.Empty(model.Filters),
model =>
{
- Assert.Equal("/Users/Account.cshtml", model.ViewEnginePath);
+ Assert.Equal("/Users/Account", model.ViewEnginePath);
var authorizeFilter = Assert.IsType(Assert.Single(model.Filters));
var authorizeData = Assert.IsType(Assert.Single(authorizeFilter.AuthorizeData));
Assert.Equal("Manage-Accounts", authorizeData.Policy);
@@ -144,24 +145,24 @@ namespace Microsoft.Extensions.DependencyInjection
public void AuthorizePage_AddsAuthorizeFilterWithoutPolicyToSpecificPage()
{
// Arrange
- var options = new RazorPagesOptions();
+ var conventions = new PageConventionCollection();
var models = new[]
{
- CreateApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
- CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
- CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
+ CreateApplicationModel("/Pages/Index.cshtml", "/Index"),
+ CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account"),
+ CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact"),
};
// Act
- options.AuthorizePage("/Users/Account.cshtml");
- ApplyConventions(options, models);
+ conventions.AuthorizePage("/Users/Account");
+ ApplyConventions(conventions, models);
// Assert
Assert.Collection(models,
model => Assert.Empty(model.Filters),
model =>
{
- Assert.Equal("/Users/Account.cshtml", model.ViewEnginePath);
+ Assert.Equal("/Users/Account", model.ViewEnginePath);
var authorizeFilter = Assert.IsType(Assert.Single(model.Filters));
var authorizeData = Assert.IsType(Assert.Single(authorizeFilter.AuthorizeData));
Assert.Equal(string.Empty, authorizeData.Policy);
@@ -175,31 +176,31 @@ namespace Microsoft.Extensions.DependencyInjection
public void AuthorizePage_AddsAuthorizeFilterWithPolicyToPagesUnderFolder(string folderName)
{
// Arrange
- var options = new RazorPagesOptions();
+ var conventions = new PageConventionCollection();
var models = new[]
{
- CreateApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
- CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
- CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
+ CreateApplicationModel("/Pages/Index.cshtml", "/Index"),
+ CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account"),
+ CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact"),
};
// Act
- options.AuthorizeFolder(folderName, "Manage-Accounts");
- ApplyConventions(options, models);
+ conventions.AuthorizeFolder(folderName, "Manage-Accounts");
+ ApplyConventions(conventions, models);
// Assert
Assert.Collection(models,
model => Assert.Empty(model.Filters),
model =>
{
- Assert.Equal("/Users/Account.cshtml", model.ViewEnginePath);
+ Assert.Equal("/Users/Account", model.ViewEnginePath);
var authorizeFilter = Assert.IsType(Assert.Single(model.Filters));
var authorizeData = Assert.IsType(Assert.Single(authorizeFilter.AuthorizeData));
Assert.Equal("Manage-Accounts", authorizeData.Policy);
},
model =>
{
- Assert.Equal("/Users/Contact.cshtml", model.ViewEnginePath);
+ Assert.Equal("/Users/Contact", model.ViewEnginePath);
var authorizeFilter = Assert.IsType(Assert.Single(model.Filters));
var authorizeData = Assert.IsType(Assert.Single(authorizeFilter.AuthorizeData));
Assert.Equal("Manage-Accounts", authorizeData.Policy);
@@ -212,31 +213,31 @@ namespace Microsoft.Extensions.DependencyInjection
public void AuthorizePage_AddsAuthorizeFilterWithoutPolicyToPagesUnderFolder(string folderName)
{
// Arrange
- var options = new RazorPagesOptions();
+ var conventions = new PageConventionCollection();
var models = new[]
{
CreateApplicationModel("/Pages/Index.cshtml", "/Index.cshtml"),
- CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account.cshtml"),
- CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact.cshtml"),
+ CreateApplicationModel("/Pages/Users/Account.cshtml", "/Users/Account"),
+ CreateApplicationModel("/Pages/Users/Contact.cshtml", "/Users/Contact"),
};
// Act
- options.AuthorizeFolder(folderName);
- ApplyConventions(options, models);
+ conventions.AuthorizeFolder(folderName);
+ ApplyConventions(conventions, models);
// Assert
Assert.Collection(models,
model => Assert.Empty(model.Filters),
model =>
{
- Assert.Equal("/Users/Account.cshtml", model.ViewEnginePath);
+ Assert.Equal("/Users/Account", model.ViewEnginePath);
var authorizeFilter = Assert.IsType(Assert.Single(model.Filters));
var authorizeData = Assert.IsType(Assert.Single(authorizeFilter.AuthorizeData));
Assert.Equal(string.Empty, authorizeData.Policy);
},
model =>
{
- Assert.Equal("/Users/Contact.cshtml", model.ViewEnginePath);
+ Assert.Equal("/Users/Contact", model.ViewEnginePath);
var authorizeFilter = Assert.IsType(Assert.Single(model.Filters));
var authorizeData = Assert.IsType(Assert.Single(authorizeFilter.AuthorizeData));
Assert.Equal(string.Empty, authorizeData.Policy);
@@ -247,10 +248,10 @@ namespace Microsoft.Extensions.DependencyInjection
public void AddPageRoute_AddsRouteToSelector()
{
// Arrange
- var options = new RazorPagesOptions();
+ var conventions = new PageConventionCollection();
var models = new[]
{
- new PageRouteModel("/Pages/Index.cshtml", "/Index.cshtml")
+ new PageRouteModel("/Pages/Index.cshtml", "/Index")
{
Selectors =
{
@@ -258,7 +259,7 @@ namespace Microsoft.Extensions.DependencyInjection
CreateSelectorModel(""),
}
},
- new PageRouteModel("/Pages/About.cshtml", "/About.cshtml")
+ new PageRouteModel("/Pages/About.cshtml", "/About")
{
Selectors =
{
@@ -268,14 +269,14 @@ namespace Microsoft.Extensions.DependencyInjection
};
// Act
- options.AddPageRoute("/Index.cshtml", "Different-Route");
- ApplyConventions(options, models);
+ conventions.AddPageRoute("/Index", "Different-Route");
+ ApplyConventions(conventions, models);
// Assert
Assert.Collection(models,
model =>
{
- Assert.Equal("/Index.cshtml", model.ViewEnginePath);
+ Assert.Equal("/Index", model.ViewEnginePath);
Assert.Collection(model.Selectors,
selector =>
{
@@ -295,7 +296,7 @@ namespace Microsoft.Extensions.DependencyInjection
},
model =>
{
- Assert.Equal("/About.cshtml", model.ViewEnginePath);
+ Assert.Equal("/About", model.ViewEnginePath);
Assert.Collection(model.Selectors,
selector =>
{
@@ -317,9 +318,9 @@ namespace Microsoft.Extensions.DependencyInjection
};
}
- private static void ApplyConventions(RazorPagesOptions options, PageRouteModel[] models)
+ private static void ApplyConventions(PageConventionCollection conventions, PageRouteModel[] models)
{
- foreach (var convention in options.RouteModelConventions)
+ foreach (var convention in conventions.OfType())
{
foreach (var model in models)
{
@@ -327,9 +328,9 @@ namespace Microsoft.Extensions.DependencyInjection
}
}
}
- private static void ApplyConventions(RazorPagesOptions options, PageApplicationModel[] models)
+ private static void ApplyConventions(PageConventionCollection conventions, PageApplicationModel[] models)
{
- foreach (var convention in options.ApplicationModelConventions)
+ foreach (var convention in conventions.OfType())
{
foreach (var model in models)
{
diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageLoaderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageLoaderTest.cs
index 29a7268f97..771fd40055 100644
--- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageLoaderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageLoaderTest.cs
@@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
Assert.Same(model, m);
});
- options.Value.ApplicationModelConventions.Add(convention.Object);
+ options.Value.Conventions.Add(convention.Object);
var loader = new DefaultPageLoader(
providers,
diff --git a/test/WebSites/RazorPagesWebSite/Startup.cs b/test/WebSites/RazorPagesWebSite/Startup.cs
index 04e213fdc9..76a0bf7dec 100644
--- a/test/WebSites/RazorPagesWebSite/Startup.cs
+++ b/test/WebSites/RazorPagesWebSite/Startup.cs
@@ -16,11 +16,11 @@ namespace RazorPagesWebSite
.AddCookieTempDataProvider()
.AddRazorPagesOptions(options =>
{
- options.AuthorizePage("/HelloWorldWithAuth");
- options.AuthorizeFolder("/Pages/Admin");
- options.AllowAnonymousToPage("/Pages/Admin/Login");
- options.AddPageRoute("/HelloWorldWithRoute", "Different-Route/{text}");
- options.AddPageRoute("/Pages/NotTheRoot", string.Empty);
+ options.Conventions.AuthorizePage("/HelloWorldWithAuth");
+ options.Conventions.AuthorizeFolder("/Pages/Admin");
+ options.Conventions.AllowAnonymousToPage("/Pages/Admin/Login");
+ options.Conventions.AddPageRoute("/HelloWorldWithRoute", "Different-Route/{text}");
+ options.Conventions.AddPageRoute("/Pages/NotTheRoot", string.Empty);
})
.WithRazorPagesAtContentRoot();
}
diff --git a/test/WebSites/RazorPagesWebSite/StartupWithBasePath.cs b/test/WebSites/RazorPagesWebSite/StartupWithBasePath.cs
index 20af9f043c..040e41597a 100644
--- a/test/WebSites/RazorPagesWebSite/StartupWithBasePath.cs
+++ b/test/WebSites/RazorPagesWebSite/StartupWithBasePath.cs
@@ -16,8 +16,8 @@ namespace RazorPagesWebSite
.AddCookieTempDataProvider()
.AddRazorPagesOptions(options =>
{
- options.AuthorizePage("/Conventions/Auth");
- options.AuthorizeFolder("/Conventions/AuthFolder");
+ options.Conventions.AuthorizePage("/Conventions/Auth");
+ options.Conventions.AuthorizeFolder("/Conventions/AuthFolder");
});
}