在 ASP.NET Core RazorPages 中更改 cshtml 文件的名称
Change name of cshtml file in ASP.NET Core RazorPages
我的环境:ASP.NET Core 5 with RazorPages, Webpack 5.
在引用 svg 文件的剃刀页面 (.cshtml
) 中,我想将它们内联。这是 Webpack 可以做的事情(通过插件),但我不确定如何整合这两个技术堆栈。
我可以编写模板化的 cshtml 文件,并通过 webpack 填充它们:
ContactUs.cshtml.cs
ContactUs.cshtml <------ read by webpack
ContactUs.generated.cshtml <------ generated by webpack
但是我如何强制 msbuild / aspnet 在构建时使用生成的文件 (ContactUs.generated.cshtml
) 而不是模板文件 (ContactUs.cshtml
)?
我怀疑答案是使用 IPageRouteModelConvention
但我不确定如何使用。
(一个肮脏的解决方法是使用文件名 ContactUs.template.cshtml
和 ContactUs.cshtml
,但我更喜欢上面的名称,因为“生成”更清晰。)
更新
简化问题:
编译器查找 Foo.cshtml.cs
和 Foo.cshtml
.
如何告诉它寻找 Foo.cshtml.cs
和 Foo.generated.cshtml
?
加载应用程序时,框架会为您加载一组 PageRouteModel
,它们是从 razor 页面文件夹(按照惯例)自动生成的。每个这样的模型都包含一组 SelectorModel
,每个模型都有一个 AttributeRouteModel
。您需要做的只是通过从自动生成的值中删除后缀部分来修改 AttributeRouteModel.Template
。
您可以创建自定义 IPageRouteModelConvention
来定位每个 PageRouteModel
。然而,这样你就不能确保路由被复制(因为在修改 AttributeRouteModel.Template
之后,它可能会与其他一些现有路由重复)。除非您必须管理一组共享的路线模板。相反,您可以创建自定义 IPageRouteModelProvider
。它在一个地方提供了所有 PageRouteModel
,以便您可以修改和添加或删除任何内容。这种方式非常方便,您可以支持 2 个剃须刀页面,其中一页的优先级高于另一页(例如:您有 Index.cshtml
和 Index.generated.cshtml
,并且您希望它选择 Index.generated.cshtml
。如果生成的视图不存在,将使用默认 Index.cshtml
。
所以这里是详细的代码:
public class SuffixedNamePageRouteModelProvider : IPageRouteModelProvider
{
public SuffixedNamePageRouteModelProvider(string pageNameSuffix, int order = 0)
{
_pageNameSuffixPattern = string.IsNullOrEmpty(pageNameSuffix) ? "" : $"\.{Regex.Escape(pageNameSuffix)}$";
Order = order;
}
readonly string _pageNameSuffixPattern;
public int Order { get; }
public void OnProvidersExecuted(PageRouteModelProviderContext context)
{
}
public void OnProvidersExecuting(PageRouteModelProviderContext context)
{
if(_pageNameSuffixPattern == "") return;
var suffixedRoutes = context.RouteModels.Where(e => Regex.IsMatch(e.ViewEnginePath, _pageNameSuffixPattern)).ToList();
var overriddenRoutes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var route in suffixedRoutes)
{
//NOTE: this is not required to help it pick the right page we want.
//But it's necessary for other related code to work properly (e.g: link generation, ...)
//we need to update the "page" route data as well
route.RouteValues["page"] = Regex.Replace(route.RouteValues["page"], _pageNameSuffixPattern, "");
var overriddenRoute = Regex.Replace(route.ViewEnginePath, _pageNameSuffixPattern, "");
var isIndexRoute = overriddenRoute.EndsWith("/index", StringComparison.OrdinalIgnoreCase);
foreach (var selector in route.Selectors.Where(e => e.AttributeRouteModel?.Template != null))
{
var template = Regex.Replace(selector.AttributeRouteModel.Template, _pageNameSuffixPattern, "");
if (template != selector.AttributeRouteModel.Template)
{
selector.AttributeRouteModel.Template = template;
overriddenRoutes.Add($"/{template.TrimStart('/')}");
selector.AttributeRouteModel.SuppressLinkGeneration = isIndexRoute;
}
}
//Add another selector for routing to the same page from another path.
//Here we add the root path to select the index page
if (isIndexRoute)
{
var defaultTemplate = Regex.Replace(overriddenRoute, "/index$", "", RegexOptions.IgnoreCase);
route.Selectors.Add(new SelectorModel()
{
AttributeRouteModel = new AttributeRouteModel() { Template = defaultTemplate }
});
}
}
//remove the overridden routes to avoid exception of duplicate routes
foreach (var route in context.RouteModels.Where(e => overriddenRoutes.Contains(e.ViewEnginePath)).ToList())
{
context.RouteModels.Remove(route);
}
}
}
在Startup.ConfigureServices
中注册IPageRouteModelProvider
:
services.AddSingleton<IPageRouteModelProvider>(new SuffixedNamePageRouteModelProvider("generated"));
我的环境:ASP.NET Core 5 with RazorPages, Webpack 5.
在引用 svg 文件的剃刀页面 (.cshtml
) 中,我想将它们内联。这是 Webpack 可以做的事情(通过插件),但我不确定如何整合这两个技术堆栈。
我可以编写模板化的 cshtml 文件,并通过 webpack 填充它们:
ContactUs.cshtml.cs
ContactUs.cshtml <------ read by webpack
ContactUs.generated.cshtml <------ generated by webpack
但是我如何强制 msbuild / aspnet 在构建时使用生成的文件 (ContactUs.generated.cshtml
) 而不是模板文件 (ContactUs.cshtml
)?
我怀疑答案是使用 IPageRouteModelConvention
但我不确定如何使用。
(一个肮脏的解决方法是使用文件名 ContactUs.template.cshtml
和 ContactUs.cshtml
,但我更喜欢上面的名称,因为“生成”更清晰。)
更新
简化问题:
编译器查找 Foo.cshtml.cs
和 Foo.cshtml
.
如何告诉它寻找 Foo.cshtml.cs
和 Foo.generated.cshtml
?
加载应用程序时,框架会为您加载一组 PageRouteModel
,它们是从 razor 页面文件夹(按照惯例)自动生成的。每个这样的模型都包含一组 SelectorModel
,每个模型都有一个 AttributeRouteModel
。您需要做的只是通过从自动生成的值中删除后缀部分来修改 AttributeRouteModel.Template
。
您可以创建自定义 IPageRouteModelConvention
来定位每个 PageRouteModel
。然而,这样你就不能确保路由被复制(因为在修改 AttributeRouteModel.Template
之后,它可能会与其他一些现有路由重复)。除非您必须管理一组共享的路线模板。相反,您可以创建自定义 IPageRouteModelProvider
。它在一个地方提供了所有 PageRouteModel
,以便您可以修改和添加或删除任何内容。这种方式非常方便,您可以支持 2 个剃须刀页面,其中一页的优先级高于另一页(例如:您有 Index.cshtml
和 Index.generated.cshtml
,并且您希望它选择 Index.generated.cshtml
。如果生成的视图不存在,将使用默认 Index.cshtml
。
所以这里是详细的代码:
public class SuffixedNamePageRouteModelProvider : IPageRouteModelProvider
{
public SuffixedNamePageRouteModelProvider(string pageNameSuffix, int order = 0)
{
_pageNameSuffixPattern = string.IsNullOrEmpty(pageNameSuffix) ? "" : $"\.{Regex.Escape(pageNameSuffix)}$";
Order = order;
}
readonly string _pageNameSuffixPattern;
public int Order { get; }
public void OnProvidersExecuted(PageRouteModelProviderContext context)
{
}
public void OnProvidersExecuting(PageRouteModelProviderContext context)
{
if(_pageNameSuffixPattern == "") return;
var suffixedRoutes = context.RouteModels.Where(e => Regex.IsMatch(e.ViewEnginePath, _pageNameSuffixPattern)).ToList();
var overriddenRoutes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var route in suffixedRoutes)
{
//NOTE: this is not required to help it pick the right page we want.
//But it's necessary for other related code to work properly (e.g: link generation, ...)
//we need to update the "page" route data as well
route.RouteValues["page"] = Regex.Replace(route.RouteValues["page"], _pageNameSuffixPattern, "");
var overriddenRoute = Regex.Replace(route.ViewEnginePath, _pageNameSuffixPattern, "");
var isIndexRoute = overriddenRoute.EndsWith("/index", StringComparison.OrdinalIgnoreCase);
foreach (var selector in route.Selectors.Where(e => e.AttributeRouteModel?.Template != null))
{
var template = Regex.Replace(selector.AttributeRouteModel.Template, _pageNameSuffixPattern, "");
if (template != selector.AttributeRouteModel.Template)
{
selector.AttributeRouteModel.Template = template;
overriddenRoutes.Add($"/{template.TrimStart('/')}");
selector.AttributeRouteModel.SuppressLinkGeneration = isIndexRoute;
}
}
//Add another selector for routing to the same page from another path.
//Here we add the root path to select the index page
if (isIndexRoute)
{
var defaultTemplate = Regex.Replace(overriddenRoute, "/index$", "", RegexOptions.IgnoreCase);
route.Selectors.Add(new SelectorModel()
{
AttributeRouteModel = new AttributeRouteModel() { Template = defaultTemplate }
});
}
}
//remove the overridden routes to avoid exception of duplicate routes
foreach (var route in context.RouteModels.Where(e => overriddenRoutes.Contains(e.ViewEnginePath)).ToList())
{
context.RouteModels.Remove(route);
}
}
}
在Startup.ConfigureServices
中注册IPageRouteModelProvider
:
services.AddSingleton<IPageRouteModelProvider>(new SuffixedNamePageRouteModelProvider("generated"));