HtmlHelper使用内部消费html / text

时间:2016-08-29 22:16:18

标签: c# asp.net-mvc html-helper

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

剃刀:

<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);
}

The Wrapper:

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>现在包含在原始编写器中。

剃刀引擎是否保留了编写器的本地副本,并忽略了它已设置为不同实例的事实?这对我来说似乎是个错误。

2 个答案:

答案 0 :(得分:0)

如果有人想要一个解决方案,因为我不耐烦,我找到了一种方法来完成这项工作。它不太理想,但它可以解决问题。如果您将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(...)语句中完成消费任何内容的预期结果。

但是我仍然很想知道是否有办法在不破解这样的解决方案的情况下做到这一点。

答案 1 :(得分:0)

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

test

但正如您所发现的那样,man sudoers以任何方式消耗 。你希望这个标记能够被“重定向”到你创建的作者的其他地方,但事实并非如此。相反,您创建的编写器也用于写入响应流,这是<script type="text/javascript"> alert("hello world"); </script> 所用的内容,无论它恰好是什么作者。

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

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

我认为,考虑到你想要实现的目标,你的解决方案不是黑客,而是正确的解决方法。