使用HtmlHelper来消费html/text

本文关键字:html text HtmlHelper 使用 | 更新日期: 2023-09-27 18:04:32

我正在尝试制作一个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>

Helper扩展方法:

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,它仍然是写到原来的writer。显然,调用dispose的顺序是正确的,因为dispose的顶部位于脚本之后。在设置了新的写入器后进行调试时,<script>块不在流中,但是在处理时,<script>现在包含在原始写入器中。

razor引擎是否保留了写入器的本地副本,并忽略了它已被设置为不同实例的事实?

使用HtmlHelper来消费html/text

如果有人想要解决这个问题,因为我很不耐烦,我找到了一个方法来完成这个工作。虽然不太理想,但还是管用的。如果将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以任何方式消耗。您希望此标记将被"重定向"到其他地方,即您创建的编写器,但事实并非如此。相反,您创建的写入器也用于写入响应流,这就是ViewContext.Writer的用途,无论它恰好是什么写入器。

因此,Razor并没有让ScriptWrapper消费标记,而是将其解释为视图中的其他标记,并在解释后将其写入响应流。从Razor的角度来看,标记是用于输出的,而不是用于ScriptWrapper。只是将它包含在using块中,并不会使标记减少整体视图的一部分。

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

我认为你的解决方案不是一个hack,而是一个正确的方法,考虑到你想要完成的任务。