c# - 添加一个简单的 TagHelper 会导致此错误:“尚未为页面调用 RenderBody”
问题描述
在 ASP.NET Core 3.0 Web 应用程序中,我添加了以下简单的标签助手:
[HtmlTargetElement("submit-button")]
public class SubmitButtonTagHelper : TagHelper
{
public string Title { get; set; } = "Submit";
public string Classes { get; set; }
public string Id { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.Content.SetHtmlContent(
$@"<span class=""btn btn-primary {Classes}"" id=""{Id}"">{Title}</span>");
}
}
我打算这样使用:
<submit-button></submit-button>
但是,添加 SubmitButtonTagHelper 甚至不尝试使用它的行为会导致以下运行时异常:
InvalidOperationException:尚未为“/Views/Shared/_Layout.cshtml”处的页面调用 RenderBody。忽略调用 IgnoreBody()。
我已通过将此行添加到 Pages/_ViewImports.cshtml 文件中来导入标签助手:
@addTagHelper *, Web
我的 _Layout.cshtml 页面如下所示:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><vc:product-name></vc:product-name> @ViewData["Title"]</title>
@RenderSection("Styles", required: false)
<partial name="_FrameworkStyles" />
</head>
<body class="top-navigation">
<div id="wrapper">
<div id="page-wrapper" class="gray-bg">
<div class="">
<vc:Navigation></vc:Navigation>
@RenderBody()
<div class="footer"></div>
</div>
</div>
</div>
<partial name="_FrameworkScripts" />
@RenderSection("Scripts", required: false)
</body>
</html>
我怎么会在这里出错?在调试时,我可以看到 SubmitButtonTagHelper 中的断点被击中,但我还没有在任何地方引用它?我的理解是 [HtmlTargetElement] 属性意味着它只适用于元素标签是“提交按钮”的地方。这不正确吗?
我的项目中有另一个标签助手,我还注意到该类中的断点也在我没有引用它的地方被命中。
我肯定在做一些愚蠢的事情,但是什么?
解决方案
ASP.NET Core MVC 有Tag Helper Components的概念:
Tag Helper 组件是一个 Tag Helper,它允许您从服务器端代码有条件地修改或添加 HTML 元素。
...
Tag Helper 组件不需要在_ViewImports.cshtml中注册应用程序。
...
Tag Helper 组件的两个常见用例包括:
同样来自文档,这里是标签助手组件的实现:
public class AddressStyleTagHelperComponent : TagHelperComponent { private readonly string _style = @"<link rel=""stylesheet"" href=""/css/address.css"" />"; public override int Order => 1; public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { if (string.Equals(context.TagName, "head", StringComparison.OrdinalIgnoreCase)) { output.PostContent.AppendHtml(_style); } return Task.CompletedTask; } }
一旦注册(如下所示),AddressStyleTagHelperComponent
将运行两次:一次用于head
元素;一次为body
元素。以下是它在 DI 中的注册方式:
services.AddTransient<ITagHelperComponent, AddressScriptTagHelperComponent>();
在这一点上(或者甚至更早),你可能认为我疯了。这有什么关系SubmitButtonTagHelper
?
通过它的继承树,SubmitButtonTagHelper
最终实现ITagHelperComponent
. 如果您要添加以下 DI 注册,SubmitButtonTagHelper
将作为 Tag Helper 组件运行,一次为head
一次,一次为body
:
services.AddTransient<ITagHelperComponent, SubmitButtonTagHelper>();
SubmitButtonTagHelper
是破坏性的,替换它操作的元素的全部内容。如果它要替换元素的内容body
,那么主体当然会失去其RenderBody
指令。
因此,这是对如果注册为标记帮助程序组件可能发生的情况的详细解释。SubmitButtonTagHelper
这正是您上传到 GitHub(源代码)的示例中发生的事情,这不足为奇:
private static void WebRegistration(ContainerBuilder builder) { builder.RegisterAutowiredAssemblyInterfaces(Assembly.Load(Web)); builder.RegisterAutowiredAssemblyTypes(Assembly.Load(Web)); }
我对 Autofac 了解不多,但很明显,上面显示的调用会针对其所有接口RegisterAutowiredAssemblyInterfaces
找到并注册它,包括.SubmitButtonTagHelper
ITagHelperComponent
同样,我对 Autofac 了解不多,但最终,不打算作为标签助手组件运行的标签助手应该被排除在这个自动注册过程之外。这是关于如何进行此过滤的建议,尽管我认为这是一种糟糕的 Autofac 方法:
builder.RegisterAutowiredAssemblyInterfaces(Assembly.Load(Web))
.Where(x => !x.Name.EndsWith("TagHelper"));
推荐阅读
- java - 词搜索 II java.lang.ArrayIndexOutOfBoundsException
- javascript - 我正在使用JS,我知道replace方法的使用,你能在下面的函数中解释这个特定的代码=> /.(?=.{4})/g吗?
- matlab - 如何在子图中有灰度图和彩虹图?
- javascript - 我的服务器信息命令显示 1 个人和 1 个机器人,我有 10 个机器人和 3 个用户
- python - 在 CSV 文件中添加和追加数据
- python - Django UpdateView:如何显示原始值?
- python - 阻尼余弦函数曲线拟合的可能方法 [回归]
- javascript - 使用存储在变量中的值来更新对象属性
- google-chrome - 从离线 Windows 10 硬盘恢复 chrome 开发工具片段
- javascript - 为什么 concat 可能不是一个函数,即使有两个字符串?