В настоящее время я пытаюсь реализовать тематический сайт, используя Razor Pages в dotnetcore 2.1, но у меня возникли некоторые затруднения / путаница с тем, почему страницы не загружаются.
Каждый запрос к сайту приводит к значению темыбыть установленным на основе доступного домена, по умолчанию используется тема «По умолчанию», которая хранится в RouteData каждого запроса.
Я реализовал следующие ThemeViewLocationExpander
public class ThemeViewLocationExpander : IViewLocationExpander
{
private const string ValueKey = "Theme";
public void PopulateValues(ViewLocationExpanderContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
context.Values[ValueKey] = (context.ActionContext.RouteData.Values["tenant"] as Tenant)?.Theme;
}
public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
if (viewLocations is null)
{
throw new ArgumentNullException(nameof(viewLocations));
}
context.Values.TryGetValue(ValueKey, out string theme);
if (!string.IsNullOrEmpty(theme))
{
viewLocations = new[] {
$"/Pages/Themes/{theme}/{{1}}/{{0}}.cshtml",
$"/Pages/Themes/{theme}/Shared/{{0}}.cshtml",
$"/Pages/Shared/{{0}}.cshtml",
};
}
return viewLocations;
}
КакВы можете видеть, что я пытаюсь загрузить страницы (поскольку каждая тема может иметь совершенно разный макет, а не просто изменяющийся CSS) из /Pages/Themes/{THEME_NAME}
... вместо /Pages/
, за исключением общей папки верхнего уровня /Pages/Shared
для таких вещей, как частичные для сценариев проверки, которые могут существовать во всех темах.
Я не хочу, чтобы были какие-либо другие страницы непосредственно под /Pages
, такие как /Pages/Index.cshtml
, поскольку КАЖДЫЙ запрос должен заканчиваться внутриодна из папок тем как имеющая тему является обязательной.
Я добавил следующее к своему Startup.cs
services.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationExpanders.Add(new ThemeViewLocationExpander());
});
Я могу установить точку останова этого кода на fПервая загрузка, так что я знаю, что она зарегистрирована, однако, если я запускаю сайт и получаю https://localhost:44352/
, он загружает /Pages/Index.cshtml
, когда он должен загружаться /Pages/Themes/Default/Index.cshtml
.
Если я удаляю /Pages/Index.cshtml
и запускаюсайт, который я получаю
This localhost page can’t be found
No webpage was found for the web address: https://localhost:44352/
HTTP ERROR 404
Может кто-нибудь предложить мне какое-либо понимание или помочь с этим?
Возможно, мне нужно изменить какую-то маршрутизацию, чтобы включить тему, или я упускаю что-то важное, похоже, что места просмотра, которые я установил в ThemeViewLocationExpander.ExpandViewLocations()
, никогда не используются.
Тамэто много мультитенантных вещей, о которых я читал для DNC 2.1, но это либо для MVC, а не RazorPages, либо реализует только страницы CSS / _Layout.cshtml внутри папок тем и использует страницы по умолчанию в /Pages/
для макета (я пытаюсь иметь индивидуальные реализации всех страниц в каждой папке темы)
Редактировать:
Я решил, что было бы разумнее сохранить /Pages/Index.cshtml
и другиестраниц в папке верхнего уровня и используйте это как «Тема» по умолчанию, тогда любые новые темы будут использовать эти страницы, если только они не будут переопределены внутри их папки /Pages/Themes/{THEME_NAME}
.
Я реализовал это, но все еще нахожусь напотеря в том, почему страница загружается из /Pages/Index.cshtml
, даже если задано значение темы и в папку viewLocations
добавлена папка темы.
Редактировать 2:
Протестировал его и с установленной темой он определенно использовал правильную страницу макета в папке темы.Например, с темой «Fresh» при загрузке https://localhost:44352/
загружается страница макета /Pages/Themes/Fresh/Shared/_Layout.cshtml
, однако Index.cshtml
загружается из /Pages/Index.cshtml
, а не /Pages/Themes/Fresh/Index.cshtml
.
.viewLocations
были:
/Pages/Themes/Fresh/{1}/{0}.cshtml
/Pages/Themes/Fresh/Shared/{0}.cshtml
/Pages/Shared/{0}.cshtml
/Pages/{1}/{0}.cshtml
/Pages/Shared/{0}.cshtml
/Views/Shared/{0}.cshtml
Редактировать 3:
Вот ссылка на github для упрощенной версии проекта, которая демонстрирует мою проблему.https://github.com/BenMaxfield/ThemeTest
Чтобы протестировать его, просто запустите приложение, и вы должны увидеть, что оно загружает макет темы DARK, но содержимое индекса DEFAULT.
Чтобы перейти к теме ПО УМОЛЧАНИЮ, найдите метод PopulateValues
в ThemePageViewLocationExpander
и установите context.Values[ThemeKey] = string.Empty;
Редактировать 4
У меня естьзаметил, что ThemePageViewLocationExpander.ExpandViewLocations
- это каждый вызов только для частичных файлов, и никогда для реальных страниц с кодом позади.
Это объясняет, почему для темы была загружена правильная страница _Layout.cshtml
, а правильный Index.cshtml
файл былнет (так как /Pages/Index.cshtml
является страницей с кодом позади и имеет объявление @page
- т.е. оно не является частичным)
Чтобы обойти это хакерским способом, я создал новую папку /Pages/Views/
иположить частичный .cshtml
, который соответствует каждой из @page
страниц на верхнем уровне /Pages/
,
, например, у меня теперь есть /Pages/Index.cshtml
(не частичный) и /Pages/Views/Index.cshtml
(частичный).
Внутри /Pages/Index.cshtml
Я добавил <partial name="Index" model="@Model"/>
для загрузки соответствующего частичного из новой папки /Pages/Views/
.
Поскольку сейчас идет загрузка, вызывается частичный ThemePageViewLocationExpander.ExpandViewLocations
, и яможет затем переопределить местоположение представления для этого на основе данного ключа темы (если присутствует).
Это означает, что все запросы к /Index
будут загружать OnGet()
из /Pages/Index.cshtml
, что впоследствии загружает частичное в его представление, которое я могу при желании переопределить, добавив еще один Index.cshtml
частичный внутри /Themes/{THEME_NAME}/Views/
.
Вот мое текущее переопределение расположения для ThemePageViewLocationExpander.ExpandViewLocations
viewLocations = new[] {
$"/Themes/{theme}/{{0}}.cshtml",
$"/Themes/{theme}/Views/{{0}}.cshtml",
$"/Pages/Views/{{0}}.cshtml",
}.Concat(viewLocations);
и пример структуры папок:
.
├── Pages
| ├── Index.cshtml (with code behind, called on localhost:45635/)
| └── Views
| └── Index.cshtml (partial)
└── Themes
| ├── Dark
| └── Views
| └── Index.cshtml (partial)