HtmlHelper 在 using 中消耗 html/text

HtmlHelper to consume html/text inside using

我正在尝试制作一个 HTML 帮助器,它允许使用 using 中的任何文本,而不是写入最后一页。

剃刀:

<div>
    <p>
        Before Inline
    </p>

    @using (Html.IncludeInlineScript("testScript"))
    {
        <script type="text/javascript">
            alert("hello world");
        </script>
    }

    <p>
        After Inline
    </p>
</div>

结果HTML:

<div>
    <p>
        Before Inline
    </p>

        <script type="text/javascript">
            alert("hello world");
        </script>
<!-- Top of Dispose -->
<!-- Bottom of Dispose -->

    <p>
        After Inline
    </p>
</div>

助手扩展方法:

public static ScriptWrapper IncludeInlineScript(this HtmlHelper helper, string scriptName)
{
    return new ScriptWrapper(helper);
}

包装器:

public class ScriptWrapper : IDisposable
{
    private HtmlHelper _helper;
    private TextWriter _originalWriter;
    private StringBuilder _scriptContents;

    public ScriptWrapper(HtmlHelper helper)
    {
        _helper = helper;
        _originalWriter = _helper.ViewContext.Writer;

        _scriptContents = new StringBuilder();
        _helper.ViewContext.Writer = new StringWriter(_scriptContents);
    }

    public void Dispose()
    {
        _originalWriter.WriteLine("<!-- Top of Dispose -->");
        _helper.ViewContext.Writer.Flush();
        _helper.ViewContext.Writer = _originalWriter;
        _originalWriter.WriteLine("<!-- Bottom of Dispose -->");
    }
}

这里的问题是,尽管将 ViewContext.Writer 设置为新的 TextWriter,它仍然在写入原始作者。很明显,dispose 的调用顺序正确,因为 dispose 的顶部在脚本之后。在设置新编写器后进行调试时,<script> 块不在流中,但是在处理时 <script> 现在包含在原始编写器中。

razor 引擎是否保留了 writer 的本地副本并忽略了它已被设置为不同实例的事实?这对我来说似乎是一个错误。

万一有人想变通,因为我不耐烦,我找到了一种方法来完成这项工作。它不太理想,但可以解决问题。如果将ScriptWrapper修改为:

public class ScriptWrapper : IDisposable
{
    private HtmlHelper _helper;
    private string _originalString;
    private StringBuilder _sb;

    public ScriptWrapper(HtmlHelper helper)
    {
        _helper = helper;
        _sb = ((StringWriter) _helper.ViewContext.Writer).GetStringBuilder();
        _originalString = _sb.ToString();
        _sb.Clear();
    }

    public void Dispose()
    {
        var contents = _sb.ToString();
        _sb.Clear();
        _sb.Append(_originalString);
    }
}

您可以在 using(...) 语句中使用任何内容来达到预期的结果。

不过,我仍然很想知道是否有一种方法可以做到这一点而无需破解这样的解决方案。

在你原来的(问题)中,很容易认为因为标记在 using 块内,所以 ScriptWrapper 以某种方式消耗了标记

<script type="text/javascript">
    alert("hello world");
</script>

但正如您所发现的,它 而不是 以任何方式被 ScriptWrapper 消耗。您希望此标记会在其他地方获得 "redirected" 给您创建的作者,但事实并非如此。相反,您创建的编写器也用于写入响应流,这就是 ViewContext.Writer 的用途,无论它是什么编写器。

因此,Razor 不是让 ScriptWrapper 使用标记,而是像视图中的任何其他内容一样将其解释为标记,并在解释后将其写入响应流。从 Razor 的角度来看,标记用于输出,而不是用于 ScriptWrapper。只是将它包含在一个 using 块中,不会使标记成为整体视图的一部分。

你回答成功是因为你没有将标记重定向到其他地方(这不起作用),而是因为你正在编辑响应流中的内容,有效地从响应流中删除标记。

我认为你的解决方案不是 hack,而是解决这个问题的正确方法,鉴于你想要完成的事情。