Scott on Writing

Musings on technical writing...

Using a Base Class to Fiddle with a Page's Rendered Output

Imagine that you wanted to tweak a page's rendered output in some manner - perhaps inject a copyright notice at the bottom of the page or ensure that <link> tags to required stylesheets were present.  There are a number of options available to make this happen in ASP.NET.:

  1. The simplest (but least tenable) approach is to simply add the logic to each and every page that requires it.  This can be a maintainence nightmare, however, as if the logic changes you need to revisit every page that has this logic hard coded.  Similarly, adding new pages to the site that utilize this same logic requires copying and pasting code.
  2. A more centralized approach would be to tap into an application event, if available.  For example, this might be a good choice if you wanted to use some kind of special encryption or compression on the page's output.
  3. While the Global.asax approach is better than adding the logic to each individual page, it still has the problem of tightly coupling the logic with the application.  If you wanted to replicate the logic in another Web application, for instance, you'd need to replicate the code.  This takes us back to our initial problem - what happens if the logic needs to change or be updated?  What happens when we want to add another Web application that uses this logic?  We'd need to replicate the Global.asax code in each of these applications.  Boo.

    A better, more losely coupled approach is to use an HTTP Module.  It has access to the same application-level events, and can be packaged up as a stand-alone assembly that can be added or removed to Web applications as easily as you can add or remove a file from a directory.  No recompilation/redeployment needed.  (See Using HTTP Modules and Handlers to Create Pluggable ASP.NET Components for more info on using an HTTP Modules in this manner, along with an examination of a global error logging component, ELMAH.)
  4. A final approach is to use a base class that the ASP.NET pages in your application extend.  The logic necessary can be placed in this base class.  This technique is used in DotNetNuke (well, it was in version 2.x, I haven't poked around version 3 yet).  DotNetNuke uses a base class to move the __VIEWSTATE hidden form field to the bottom of the <form> so as not to gunk up search engines or Google AdSense.  Here's the base class, some code has been removed for brevity:

Public Class BasePage
  Inherits System.Web.UI.Page

  '
  ' This method overrides the Render() method for the page and moves the ViewState
  ' from its default location at the top of the page to the bottom of the page. This
  ' results in better search engine spidering.
  '
  Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
      Dim stringWriter As System.IO.StringWriter = New System.IO.StringWriter
      Dim htmlWriter As HtmlTextWriter = New HtmlTextWriter(stringWriter)
      MyBase.Render(htmlWriter)
      Dim html As String = stringWriter.ToString()
      Dim StartPoint As Integer = html.IndexOf("<input type=""hidden"" name=""__VIEWSTATE""")
      If StartPoint >= 0 Then 'does __VIEWSTATE exist?
          Dim EndPoint As Integer = html.IndexOf("/>", StartPoint) + 2
          Dim ViewStateInput As String = html.Substring(StartPoint, EndPoint - StartPoint)
          html = html.Remove(StartPoint, EndPoint - StartPoint)
          Dim FormEndStart As Integer = html.IndexOf("</form>") - 1
          If FormEndStart >= 0 Then
              html = html.Insert(FormEndStart, ViewStateInput)
          End If
      End If
      writer.Write(html)
  End Sub
End Class 'BasePage

For more information check out the DotNetNuke source or my latest 4Guys article, Using a Custom Base Class for your ASP.NET Page's Code-Behind Classes.

posted on Thursday, April 14, 2005 3:02 PM

Feedback

# re: Using a Base Class to Fiddle with a Page's Rendered Output 4/15/2005 2:46 AM Darren Neimke

That's interesting... I thought that you had to use Filters if you wanted to tamper with the output in a manner such as that.

# re: Using a Base Class to Fiddle with a Page's Rendered Output 4/16/2005 8:52 PM Rick Strahl

On a related note, I posted a few approaches to capturing ASP.NET output in a number of ways here, which also implicitly lets you fiddle with the output:

http://west-wind.com/weblog/posts/481.aspx

# re: Using a Base Class to Fiddle with a Page's Rendered Output 4/21/2005 8:32 AM lizardsis

I have problem with option 4. The page that inherits PageBase contains Web User Control with Response.Write code blocks embedded inside its html like:
<img src='<% Response.Write(myPage.Config.Constants.SiteRoot) %>/images/logo.jpg'>. It seems that the result of Response.Write is rendered before the output of Protected Overrides Sub Render(that is html). I would be grateful for any suggestions.

# re: Using a Base Class to Fiddle with a Page's Rendered Output 5/1/2005 12:50 PM Erik Porter

Sorry I'm so late on this, but I just wanted to say...

This is a great tip. I do it all the time for things like, changing the title of the page (i.e. adding more to it), allowing non-server controls to use the "~", changing the body tag, etc

# re: Using a Base Class to Fiddle with a Page's Rendered Output 6/2/2005 3:24 PM Matt

For the C# programmers out there, 'MyBase' is apparently the VB.NET equivalent of C#'s 'base'

(Why did they name it 'MyBase' instead of just 'Base' or 'base'? I thought the MS habit of prefixing variable names with 'My' was just for code examples. It must have something to do with VB's lack of case-sensitivity, but that's no excuse for such a lame name. Even AllYourBase would be cooler than MyBase!)

# re: Using a Base Class to Fiddle with a Page's Rendered Output 12/8/2005 11:49 AM slolife

This code:

Dim FormEndStart As Integer = html.IndexOf("</form>") - 1
If FormEndStart >= 0 Then
html = html.Insert(FormEndStart, ViewStateInput)
End If

creates a problem when a control butts itself up against the ending form tag (</form>). You should remove the -1 and things work well.

I have a page that produces this:

</script></form>

and the -1 in the line above inserts the viewstate like this:

</script<input... />></form>

# Viewstate-Optimization for ASP.NET 5/14/2008 3:20 AM ASP.NET SEO Blog

Viewstate-Optimization for ASP.NET

# re: moving viewstate to bottom of page 9/22/2009 11:29 AM scott

when you use the above code to move "ViewState
from its default location at the top of the page to the bottom of the page", the click event from the button at the bottom of my form fails to fire. Can you suggest a fix for this?

Title:  
Name:  
Url:
Protected by Clearscreen.SharpHIPEnter the code you see:
Comments   

My Links

Ads Via DevMavens

Archives

Post Categories

 

I am a Microsoft MVP for ASP.NET.
I am an ASPInsider.
<March 2010>
SMTWTFS
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910

Comment Stats

DayTotal% of Total
Sunday 2056.8%
Monday 42514.1%
Tuesday 51917.2%
Wednesday 55618.4%
Thursday 58019.2%
Friday 54718.1%
Saturday 1886.2%
Total 3020100.0%

Hour1Total% of Total
12:00 AM 782.6%
1:00 AM 812.7%
2:00 AM 682.3%
3:00 AM 822.7%
4:00 AM 692.3%
5:00 AM 1264.2%
6:00 AM 1193.9%
7:00 AM 1816.0%
8:00 AM 1926.4%
9:00 AM 1585.2%
10:00 AM 1886.2%
11:00 AM 1936.4%
12:00 PM 2016.7%
1:00 PM 1846.1%
2:00 PM 1695.6%
3:00 PM 1354.5%
4:00 PM 1153.8%
5:00 PM 1073.5%
6:00 PM 1013.3%
7:00 PM 1073.5%
8:00 PM 923.0%
9:00 PM 882.9%
10:00 PM 913.0%
11:00 PM 953.1%
Total 3020100.0%

Comments by Blog Entry Date/Time

Day Entry MadeAvg.Total
Sunday 5.00160
Monday 4.80384
Tuesday 4.04477
Wednesday 7.39680
Thursday 6.26676
Friday 5.07466
Saturday 4.78177
Total 5.403020

Hour1 Entry MadeAvg.Total
12:00 AM 5.2937
1:00 AM 1.002
5:00 AM 0.000
7:00 AM 3.8550
8:00 AM 3.72134
9:00 AM 6.06297
10:00 AM 5.63276
11:00 AM 4.22194
12:00 PM 6.16351
1:00 PM 3.09133
2:00 PM 4.89230
3:00 PM 7.67322
4:00 PM 4.00108
5:00 PM 6.07170
6:00 PM 4.64116
7:00 PM 8.95188
8:00 PM 8.63164
9:00 PM 5.00115
10:00 PM 6.31101
11:00 PM 4.5732
Total 5.403020

Learn More About Comment Stats
1 - All times GMT -8...


Blog Stats

Favorite Web Sites

My Books

My MSDN Articles