| File: Infrastructure\PageViewLocationExpander.cs | Web Access |
| Project: src\aspnetcore\src\Mvc\Mvc.RazorPages\src\Microsoft.AspNetCore.Mvc.RazorPages.csproj (Microsoft.AspNetCore.Mvc.RazorPages) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Mvc.Razor; namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; /// <summary> /// An <see cref="IViewLocationExpander"/> for PageView. /// </summary> public class PageViewLocationExpander : IViewLocationExpander { /// <inheritdoc/> public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations) { if ((context.ActionContext.ActionDescriptor is PageActionDescriptor) && !string.IsNullOrEmpty(context.PageName)) { return ExpandPageHierarchy(); } // Not a page - just act natural. return viewLocations; IEnumerable<string> ExpandPageHierarchy() { foreach (var location in viewLocations) { // For pages, we only handle the 'page' token when it's surrounded by slashes. // // Explanation: // We need the ability to 'collapse' the segment which requires us to understand slashes. // Imagine a path like /{1}/{0} - we might end up with //{0} if we don't do *something* with // the slashes. Instead of picking on (leading or trailing), we choose both. This seems // less arbitrary. // // // So given a Page like /Account/Manage/Index using /Pages as the root, and the default set of // search paths, this will produce the expanded paths: // // /Pages/Account/Manage/{0}.cshtml // /Pages/Account/{0}.cshtml // /Pages/{0}.cshtml // /Views/Shared/{0}.cshtml if (!location.Contains("/{1}/")) { // If the location doesn't have the 'page' replacement token just return it as-is. yield return location; continue; } // For locations with the 'page' token - expand them into an ascending directory search, // but only up to the pages root. // // This is easy because the 'page' token already trims the root directory. var end = context.PageName.Length; while (end > 0 && (end = context.PageName.LastIndexOf('/', end - 1)) != -1) { // PageName always starts with `/` yield return location.Replace("/{1}/", context.PageName.Substring(0, end + 1)); } } } } /// <inheritdoc/> public void PopulateValues(ViewLocationExpanderContext context) { // The value we care about - 'page' is already part of the system. We don't need to add it manually. } }