我的环境: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
?
加载应用程序时,框架会为您加载一组从 razor 页面文件夹自动生成的PageRouteModel
(按照惯例)。每个这样的模型都包含一组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"));