在 ASP.NET Core RazorPages 中更改 cshtml 文件的名称



我的环境: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.cshtmlContactUs.cshtml但我更喜欢上面这样的东西,因为"生成"更清晰。

<小时 />

更新

为了简化问题:

编译器查找Foo.cshtml.csFoo.cshtml

我如何告诉它寻找Foo.cshtml.csFoo.generated.cshtml

加载应用程序时,框架会为您加载一组从 razor 页面文件夹自动生成的PageRouteModel(按照惯例)。每个这样的模型都包含一组SelectorModel每个模型都有一个AttributeRouteModel。您需要做的只是通过从自动生成的值中删除带后缀的部分来修改该AttributeRouteModel.Template

您可以创建自定义IPageRouteModelConvention以定位每个PageRouteModel。但是,这样您就无法确保路由不被复制(因为在修改AttributeRouteModel.Template后,它可能会与其他一些现有路由重复)。除非您必须管理一组共享的路由模板。相反,您可以创建自定义IPageRouteModelProvider。它在一个地方提供了所有PageRouteModel,以便您可以修改和添加或删除任何内容。这样非常方便,您可以支持 2 个剃刀页面,其中一页的优先级高于另一页(例如:您有Index.cshtmlIndex.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"));

最新更新