Recipe 21.7. Saving and Reusing HTML Output


Problem

To improve the performance of pages that rarely change, you want to capture the output of those pages and save it for reuse when those pages are requested.

Solution

Create the page that contains the desired content as you would any other page, including the server controls you need. At the end of the Page_Load method, use the RenderControl method of the Page control to generate the HTML, and save the HTML to a file.

In the code-behind class for the page, use the .NET language of your choice to:

  1. Create an HtmlTextWriter to use for rendering the page.

  2. Use the RenderControl method of the Page control to render the output of the page to the HtmlTextWriter.

  3. Save the rendered output to a file and redirect to another page.

Examples 21-16 and 21-17 show the VB and C# code-behind files for our application that demonstrates this solution.

Discussion

Occasionally, it's beneficial to save the HTML output from a generated page. This is commonly done when using the saved HTML can significantly improve web site performance. If the content of a page is static, for example, there is no point in dynamically generating HTML each time the page is requested. Until the advent of ASP.NET, the only way to save the HTML was to use the "Save as Complete Web Page" feature of Internet Explorer or another browser. Though this method does save the HTML, it copies all of the page images to the local machine and changes the image references to point to the local copies. If you are trying to improve performance by capturing a static copy of the page to use on your web server, this technique will work poorly.

With ASP.NET, you can easily capture the HTML exactly as it would be sent to the browser. For our example that illustrates this solution, we have used the page from Recipe 21.3 and added code to the Page_Load method to save the rendered output.

The RenderControl method of the Page control provides the ability to render the output of the page to the HtmlTextWriter passed to the method. Unfortunately, the HtmlTextWriter does not provide any methods for reading the contents, so a little more work is required to access the rendered HTML.

By creating a StringBuilder and then using it to create a StringWriter, which is used to create the required HtmlTextWriter, the contents of the HtmlTextWriter are available by way of the original StringBuilder. This works because the underlying storage mechanism for the StringWriter is a StringBuilder, and because the StringWriter (a stream) is used to create the HtmlTextWriter, the RenderControl method is writing the rendered output to the StringBuilder. Here is our example code to accomplish this:

 

renderedOutput = New StringBuilder() strWriter = New StringWriter(renderedOutput) tWriter = New HtmlTextWriter(strWriter)

renderedOutput = new StringBuilder(); strWriter = new StringWriter(renderedOutput); tWriter = new HtmlTextWriter(strWriter);

After creating the HtmlTextWriter, the RenderControl method of the Page is called to render the HTML for the page:

 

Page.RenderControl(tWriter)

Page.RenderControl(tWriter);

Now that the rendered HTML is available, it needs to be saved to a file on the server. This can be accomplished by creating the file with a FileStream and using a StreamWriter to write the rendered output in the StringBuilder to the file:

 

filename = Server.MapPath(".") & "\" & OUTPUT_FILENAME outputStream = New FileStream(filename, _ FileMode.Create) sWriter = New StreamWriter(outputStream) sWriter.Write(renderedOutput.ToString()) sWriter.Flush()

filename = Server.MapPath(".") + "\\" + OUTPUT_FILENAME; outputStream = new FileStream(filename, FileMode.Create); sWriter = new StreamWriter(outputStream); sWriter.Write(renderedOutput.ToString()); sWriter.Flush();

The last step is to redirect to another page. This is necessary because allowing the page to be displayed would result in an additional rendering and an exception being thrown indicating the page has more than one server-side form element. If you need the page to be displayable anyway, a parameter can be passed in the querystring and checked in the code to determine if the output should be rendered and written to a file or handled normally.

This technique can be used for individual controls in the same manner as for the entire page. For example, if you have a page that contains a DataGrid and you want the rendered HTML for just the DataGrid, you can call the RenderControl method of the DataGrid and then save the output, as described earlier.

See Also

Recipe 21.3

Example 21-16. Capturing rendered output (.vb)

 Private Sub Page_Load(ByVal sender As Object, _   ByVal e As System.EventArgs) Handles Me.Load Const OUTPUT_FILENAME As String = "CH21CaptureRenderedOutputVB.html" Dim renderedOutput As StringBuilder Dim strWriter As StringWriter Dim tWriter As HtmlTextWriter Dim outputStream As FileStream = Nothing Dim sWriter As StreamWriter = Nothing Dim filename As String Dim nextPage As String Try 'set the names of the XML and XSLT documents used in the 'transformation xmlTransform.DocumentSource = "xml/books.xml" xmlTransform.TransformSource = "xml/books.xslt" 'create a HtmlTextWriter to use for rendering the page renderedOutput = New StringBuilder strWriter = New StringWriter(renderedOutput) tWriter = New HtmlTextWriter(strWriter) 'render the page output Page.RenderControl(tWriter) 'save the rendered output to a file filename = Server.MapPath(".") & "\" & OUTPUT_FILENAME outputStream = New FileStream(filename, _   FileMode.Create) sWriter = New StreamWriter(outputStream) sWriter.Write(renderedOutput.ToString()) sWriter.Flush() 'redirect to another page 'NOTE: Continuing with the display of this page will result in the ' page being rendered a second time which will cause an exception ' to be thrown nextPage = "DisplayMessage.aspx?" & _    "PageHeader=Information" & "&" & _    "Message1=HTML Output Saved To " & OUTPUT_FILENAME Response.Redirect(nextPage) Finally 'clean up If (Not IsNothing(outputStream)) Then outputStream.Close() End If If (Not IsNothing(tWriter)) Then tWriter.Close() End If If (Not IsNothing(strWriter)) Then strWriter.Close() End If End Try  End Sub 'Page_Load 

Example 21-17. Capturing rendered output (.cs)

 protected void Page_Load(object sender, EventArgs e) { const string OUTPUT_FILENAME = "CH21CaptureRenderedOutputCS.html"; StringBuilder renderedOutput = null; StringWriter strWriter = null; HtmlTextWriter tWriter = null; FileStream outputStream = null; StreamWriter sWriter = null; String filename = null; String nextPage = null; try { // set the names of the XML and XSLT documents used in the // transformation xmlTransform.DocumentSource = "xml//books.xml"; xmlTransform.TransformSource = "xml//books.xslt"; // create a HtmlTextWriter to use for rendering the page renderedOutput = new StringBuilder(); strWriter = new StringWriter(renderedOutput); tWriter = new HtmlTextWriter(strWriter); // render the page output Page.RenderControl(tWriter); // save the rendered output to a file filename = Server.MapPath(".") + "\\" + OUTPUT_FILENAME; outputStream = new FileStream(filename,   FileMode.Create); sWriter = new StreamWriter(outputStream); sWriter.Write(renderedOutput.ToString()); sWriter.Flush(); // redirect to another page // NOTE: Continuing with the display of this page will result in the //       page being rendered a second time which will cause an exception //       to be thrown nextPage =  "DisplayMessage.aspx?" + "PageHeader=Information" + "&" + "Message1=HTML Output Saved To " + OUTPUT_FILENAME; Response.Redirect(nextPage); } finally { // clean up if (outputStream != null) { outputStream.Close(); } if (tWriter != null) { tWriter.Close(); } if (strWriter != null) { strWriter.Close(); } }  } // Page_Load 



ASP. NET Cookbook
ASP.Net 2.0 Cookbook (Cookbooks (OReilly))
ISBN: 0596100647
EAN: 2147483647
Year: 2003
Pages: 202

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net