如何在asp.net核心HtmlHelper扩展中使用IDisposable模式来捕获内部内容

时间:2017-11-16 13:23:01

标签: c# asp.net-core

我是ASP.NET Core的新手。 我正在尝试将旧项目迁移到ASP.NET核心。 我们在部分内部使用了许多脚本块,这些脚本块在预定义的布局位置集中呈现。我们使用带有一次性模式的HtmlHelper,如here所述。问题是由于ASP.NET核心中的新架构,不可能使用

webPageBase.OutputStack.Pop()

抓住内容。我找到了与TagHelpers类似的解决方案 但是我仍然会继续使用HtmlHelper扩展来收集脚本逻辑并在单个位置渲染。否则我将需要编写2个标记帮助程序,协调它们并将HtmlHelper扩展的所有50-100个数字替换为标记帮助程序。

1 个答案:

答案 0 :(得分:1)

HTML帮助程序仍然是ASP.NET Core中的一个东西。仅仅因为标记帮助程序是呈现自定义HTML的新的且通常更灵活的解决方案,这并不意味着HTML帮助程序已经消失或者它们没有用处。内置标记帮助程序实际上基于HTML帮助程序,并将使用相同的内部后端来生成输出。所以它只是同一个不同的界面。

话虽如此,由于ASP.NET Core呈现视图的方式,捕获using块内的内容比在标记帮助程序中的工作方式(它是一个非常通用的功能)要困难一些。

我现在已经坐了一会儿,想出了以下内容。只要块打开,暂时将视图编写器替换为StringWriter即可。 请注意,这可能是一个非常糟糕的主意。但它确实有效......

public static class ScriptHtmlHelper
{
    private const string ScriptsKey = "__ScriptHtmlHelper_Scripts";

    public static ScriptBlock BeginScripts(this IHtmlHelper helper)
    {
        return new ScriptBlock(helper.ViewContext);
    }

    public static IHtmlContent PageScripts(this IHtmlHelper helper)
    {
        if (helper.ViewContext.HttpContext.Items.TryGetValue(ScriptsKey, out var scriptsData) && scriptsData is List<object> scripts)
            return new HtmlContentBuilder(scripts);
        return HtmlString.Empty;
    }

    public class ScriptBlock : IDisposable
    {
        private ViewContext _viewContext;
        private TextWriter _originalWriter;
        private StringWriter _scriptWriter;
        private bool _disposed;

        public ScriptBlock(ViewContext viewContext)
        {
            _viewContext = viewContext;
            _originalWriter = viewContext.Writer;

            // replace writer
            viewContext.Writer = _scriptWriter = new StringWriter();
        }

        public void Dispose()
        {
            if (_disposed)
                return;

            try
            {
                List<object> scripts = null;
                if (_viewContext.HttpContext.Items.TryGetValue(ScriptsKey, out var scriptsData))
                    scripts = scriptsData as List<object>;
                if (scripts == null)
                    _viewContext.HttpContext.Items[ScriptsKey] = scripts = new List<object>();

                scripts.Add(new HtmlString(_scriptWriter.ToString()));
            }
            finally
            {
                // restore the original writer
                _viewContext.Writer = _originalWriter;
                _disposed = true;
            }
        }
    }
}

用法如下:

@using (Html.BeginScripts()) {
    <script>console.log('foo');</script>
    <script>console.log('bar');</script>
}

@using (Html.BeginScripts()) {
    <script>console.log('baz');</script>
}

然后渲染一切:

@Html.PageScripts()