自定义 WebViewPage 在渲染 Razor 模板时注入代码

Custom WebViewPage inject code when razor template is rendering

我正在尝试创建自定义 Razor 视图基础 class(继承 WebViewPage),它将为每个呈现的视图模板(包括布局和部分视图),以便我在客户端上参考每个 Razor 模板的开始位置(对其结束位置不感兴趣)。

到目前为止我尝试过的是

  1. 覆盖 Write 方法(如注释 here 中所述)。这会在每个剃须刀部分注入代码,而不仅仅是每个模板一次(例如,每次您使用 HTML.TextBoxFor)
  2. 覆盖 ExecutePageHierarchy 方法(如上面 link 的 post 中所述)。每次遇到第一个 PopContext 调用时都会抛出一个错误:The "RenderBody" method has not been called for layout page "~/Views/Shared/_Layout.cshtml".

觉得我现在有答案了:

public abstract class CustomWebViewPage: WebViewPage
{
    public override void ExecutePageHierarchy()
    {
        var layoutReferenceMarkup = @"<script type=""text/html"" data-layout-id=""" + TemplateInfo.VirtualPath + @"""></script>";

        base.ExecutePageHierarchy();
        string output = Output.ToString();

        //if the body tag is present the script tag should be injected into it, otherwise simply append
        if (output.Contains("</body>"))
        {
            Response.Clear();
            Response.Write(output.Replace("</body>", layoutReferenceMarkup+"</body>"));
            Response.End();
        }
        else
        {
            Output.Write(layoutReferenceMarkup);
        }
    }
}

public abstract class CustomWebViewPage<TModel>: CustomWebViewPage
{
}

似乎可行,但如果有人有更好的解决方案,请分享。

在尝试了您的解决方案后,我在呈现 HTML 具有部分视图的复杂页面时遇到了一些问题。

我的问题是一切都颠倒了。 (部分观看顺序)

更正 - 我最终替换了 OutputStack 中的输出流

    public override void ExecutePageHierarchy()
    {

        // Replace output stream with a fake local stream
        StringWriter fakeOutput = new StringWriter();

        // Save output stack top level stream, and replace with fake local stream
        TextWriter outputStackTopOutput = OutputStack.Pop();
        OutputStack.Push(fakeOutput);

        // Run Razor view engine
        base.ExecutePageHierarchy();

        string content = fakeOutput.ToString();
        // Set back real outputs, and write to the real output 
        OutputStack.Pop();
        OutputStack.Push(outputStackTopOutput);
        outputStackTopOutput.Write(content);
    }