【问题标题】:Adding a simple TagHelper causes this error: "RenderBody has not been called for the page "添加一个简单的 TagHelper 会导致此错误:“尚未为页面调用 RenderBody”
【发布时间】:2020-01-26 13:32:34
【问题描述】:

Code available here

在一个 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:尚未为页面调用 RenderBody 在'/Views/Shared/_Layout.cshtml'。忽略调用 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] 属性意味着它只适用于元素标签是“提交按钮”的地方。这不正确吗?

我的项目中有另一个标签助手,我还注意到该类中的断点也在我没有引用它的地方被命中。

我肯定在做一些傻事,但是什么?

【问题讨论】:

    标签: c# asp.net-core tag-helpers


    【解决方案1】:

    ASP.NET Core MVC 有Tag Helper Components的概念:

    标签助手组件是一个标签助手,允许您从服务器端代码有条件地修改或添加 HTML 元素。

    ...

    标签助手组件不需要在 _ViewImports.cshtml 中注册应用。

    ...

    Tag Helper Components 的两个常见用例包括:

    1. Injecting a <link> into the <head>.
    2. Injecting a <script> into the <body>.

    同样来自文档,这里是标签助手组件的实现:

    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 (source) 的示例中所发生的事情,这应该不足为奇:

    private static void WebRegistration(ContainerBuilder builder)
    {
        builder.RegisterAutowiredAssemblyInterfaces(Assembly.Load(Web));
        builder.RegisterAutowiredAssemblyTypes(Assembly.Load(Web));
    }
    

    我对 Autofac 了解不多,但很明显,上面显示的对 RegisterAutowiredAssemblyInterfaces 的调用会找到 SubmitButtonTagHelper 并将其注册到其所有接口,包括 ITagHelperComponent

    再次重申,我对 Autofac 了解不多,但最终,不应将不打算作为 Tag Helper 组件运行的 Tag Helper 排除在此自动注册过程之外。以下是关于如何进行此过滤的建议,尽管我认为这是一种糟糕的 Autofac 方法:

    builder.RegisterAutowiredAssemblyInterfaces(Assembly.Load(Web))
        .Where(x => !x.Name.EndsWith("TagHelper"));
    

    【讨论】:

      猜你喜欢
      • 2016-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-19
      • 1970-01-01
      • 1970-01-01
      • 2019-11-13
      相关资源
      最近更新 更多