Custom WebViewPage inject code when razor template

2019-07-23 08:42发布

问题:

I'm trying to create a custom Razor view base class (inheriting WebViewPage) that will inject a bit of HTML for each view template being rendered (including Layouts and Partial Views) so that I have a reference on the client of where each Razor template starts (not interested in where it ends).

What I have tried so far is

  1. overriding the Write method (as described in a comment here) . This injects code at every razor section, not just once per template (for example every time that you use the HTML.TextBoxFor)
  2. overriding the ExecutePageHierarchy method (as described in the post of the link above). This throws an error every time it hits the first PopContext call: The "RenderBody" method has not been called for layout page "~/Views/Shared/_Layout.cshtml".

回答1:

after trying your solution, I had some problems with the rendered HTML of complex pages with partial views.

my issue was that everything was reversed. (order of partial views)

to correct - I ended up replacing the Output stream in the 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);
    }


回答2:

Think that I have an answer to this now:

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
{
}

Seems to work, but if anyone has a better solution, please share.