Infinite Web Pages from One ASP
Generate infinite HTML pages on a single Web site with one
ASP rather than two, courtesy of object-oriented features in .Net
by Andrew C. Mayo
Posted April 1, 2003
Two years ago I created a Web site composed of two
active server pages (ASPs) that could produce an infinite number of HTML pages (see
Resources for a link to the article).
I used this technique because I wanted a Web site for my XML course that was scalable,
maintainable, and able to showcase XML technology in action for my students. Now,
taking advantage of its object-oriented (OO) features, I've recoded that Web site
with .Net and ported it to a single ASP.Net page. The technique I used is useful
for those who want to unravel the XML transformation aspects of .Net, as well as
for those who are interested in using a design pattern that permits creating a scalable
Web site with one ASP.Net page containing just three lines of code. This technique
also is useful as a piece of a Web site that performs XML-to-HTML transformations
as part of its processing.
XML is about data, and the technologies surrounding it can be segregated in three
categories: configuration, messaging, and transformation. You can find examples
of the flexibility of XML files using application configuration information such
as saving user preferences in a Windows application or the web.config file in ASP.Net
applications. The messaging/data transport mechanism of XML is found through such
technologies as Simple Object Access Protocol (SOAP), Web services, and the disconnected
recordset object or data set in the .Net platform. Transformation exists with the
Extensible Stylesheet Language Transformation (XSLT) standard, which serves as a
means of transforming XML documents into other text-based formats. XML's configuration
and transformation categories are the key ingredients for the construction of the
Web site example described here. An XML file containing configuration information
constructs the site, which is presented to the user through server-side transformations
of XML into HTML.
In my first article, I mentioned a similarity between an Internet site diagram and
an XML document. Both are hierarchical and depend on a root. Thus, I could structure
an HTML Web site using an XML document. Page elements within an XML document represent
the pages. The Page element contains attributes that state the page's identification,
its name, and whether the page is active. This element has subelements (doc) attached
to it that state where the data for the page is located (type="xml") and also where
the transformation of that data is located (type="xsl"). The configuration information
has this structure:
<Master>
<Page active='Yes' default=
'Yes' MainMenu='Yes' id="P00"
Name="Home">
<doc Type="xml" URL=
"\xml\home.xml" />
<doc Type="xsl" URL=
"\xsl\home.xsl" />
</Page>
…
</Master>
A Web site designed in this manner loads this configuration information as soon
as the application starts and caches it for use in creating a page requested by
a user of the site. The Web site also uses the information in this configuration
file to create a common menu that displays on each page of the Web site. The common
menu is a result of the transformation of the configuration file from XML to HTML,
and it's also cached by the application.
In a nutshell, whenever a user requests a Web page, the application interprets the
configuration information to find the requested page. If it's found, and if it's
active, it then uses the doc subelements to locate the content for the page (type="XML")
and the transformation instructions (type="XSL") of that content. These pieces,
data and transformation, are combined through an XSLT transformation to create an
HTML page that is output to the user.
The Pattern
Because the process of finding, gathering, and creating an HTML page is a discrete
process, it can be accomplished by a preexisting OO design pattern. The pattern
is called Director-Builder. The Director gathers the metadata and directs the Builder
to build. In our example, the Director verifies that the page exists and is available
and then supplies that information to a Builder that builds an HTML page. The information
supplied to the Builder is the data, or content, and the presentation/transformation
instructions.
At the time that I developed this concept, ASP was the Internet programming vehicle
of choice for anyone programming Internet applications for the Microsoft platform.
When I applied this pattern to ASP, since ASP was not object-oriented, I had no
choice but to create a site that contained two ASPs that communicated with each
other through session variables and query strings. The main page is Director.asp,
which performs a Response.redirect to the Builder.asp, which in turn outputs the
HTML transformation result (see Figure 1).
This site is scalable; because of the design pattern and the configuration structure,
any site can be deployed using the same code, and all that needs changing is the
site diagram and its corresponding data/transformation content. Because of the inherent
limitations of ASP, namely the inability of code reuse and VBScript as the programming
language, the site could not be architected as I had intended. To perform transformations
programmatically, you need to use the COM version installed on the server, you can
get different results based on the parsers' support of the XSLT standard. This parser
also requires DOM objects as both input and output for XSLT transformations.
One of the great things about the .Net platform is its support of OO programming.
By porting the application to ASP.Net, you no longer need two ASPs or session variables
for communication because you can create a Director object and have it perform the
work through a single method call (see Figure 2).
Since the syntax between VBScript and Visual Basic .Net is both similar and different,
the port is not a direct copy of code. For my site I had to re-architect the system
by creating base classes and specialized classes to perform the same functionality,
which was a good thing, because this code is now reusable and easier to maintain.
Under the ASP/VBScript architecture, the most reuse that you can get is from a server-side
include file, and then that code can be reused only within an ASP/VBScript application
and not across applications. The Director-Builder class structure provides a visual
representation of these definitions (see Figure
3).
XMLTransformer
The major difference between .Net and the ASP/VBScript implementation is that transformations
are now performed differently; that is, to perform a transformation in VBScript,
a COM solution is used with the MSXML parser. The COM version of the XML parser,
or MSXML.dll, is a DOM object that performs the transformation in one of several
method calls, depending on your preference and need. Conversely, the .Net platform
elegantly segregates the pieces of XML functionality into separate namespaces. Specifically,
the transformation functionality is placed into its own namespace (system.xml.xsl)
and its own object within that namespace (xsltransform) and has full support of
the XSLT standard. The XslTransform object has been generalized so that any object
supporting the IXPathNavigable interface can be transformed. It has also been optimized
through the ability to transform to streams, XMLWriter, TextWriter, String URL,
or the XMLReader class.
Because my site depends on XML transformations, I created a custom XML transformation
object that encapsulated the functionality of system.xml.xsl.xsltransform. Therefore,
the XMLTransformer class performs an XML transformation using string arguments,
or XMLReader objects, each of which contains the XML and XSLT instructions, respectively.
I also included custom error handling in this object that was capable of generating
either a Windows or Web-based error message and, therefore, didn't tightly couple
the transformation process to this application—namely, HTML transformation. The
XMLTranformer class is useful to either Windows- or Internet-based applications
that need to transform XML using XSLT.
With the XMLTransformer in hand, I next needed to create a class that contains functionality
specific to this application: the Builder pattern. The HTMLBuilder class is a subclass
of the XMLTransformer object and delegates XSLT transformation to its parent, thereby
reusing its transformation and error-handling code. This class implements the methods
outlined for the pattern and overloaded Build function as well as a GetResults property
that retrieves the results of the Build. Any Web site using this code would probably
customize the common header output; therefore, I added an interface to the class
called ICustomHeader. The purpose of this interface is to add any customized information
to the transformed HTML that will contain the common header for the Web site. In
this case I use it to navigate to the page element and replace it with the name
of the page being displayed. I made it an interface so that it could be implemented
easily in a different way by anyone wishing to reuse this code in a Web site designed
in this fashion.
I also created a base class for the Director functionality and named it HTMLDirectorBase.
It contains the Constructors specific to building a Director class, as outlined
in the Design Pattern. This class has a single constructor that accepts an instance
of System.Web.UI.Page, which is intended to be the ASP.Net page that creates it
(Default.aspx). With an instance of the creating page, this class and any of its
subclasses now has access to the rich set of features supplied by the ASP.Net platform
(caching, session variables, global variables, and so on), as well as the familiar
and faithful Response.Write method. The class also contains one member function
called Direct that takes a string argument containing the page to be built. This
design allows us to have just three lines of code in the ASP.Net page to produce
the Web site:
Dim m_HTMLDirector As New
HTMLDirector(Me)
'Create output a page based on the
querystring
m_HTMLDirector.Direct(CStr(
Request.QueryString("id")))
m_HTMLDirector = Nothing
HTMLDirector—a subclass of HTMLDirectorBase that overloads the Constructor and the
Direct method—is a specialized class specific to the Web site. It is responsible
for validating and assembling the metadata for the HTMLBuilder, as outlined in the
sequence diagram (see Figure 4). If no page
is requested, it defaults to the page that is designated as Main in the Web site
configuration file. If a page is requested and it is valid (it exists and it's available),
the metadata for the Page (its data and presentation) are dispatched to the HTMLBuilder
object for Building (see Listing 1).
Develop a Menu
You can cache the common header for the Web site in the ASP/VBScript solution through
a lot of custom code. Because the site map is in the Master.xml file, any change
made to it affects the functioning of the Web site. The same is also true for Master.xsl,
which takes the contents of Master.xml and transforms it into the common menu for
the system. As you can see, any change to either of these files also causes a rebuild
of the common menu, as the main menu could be rendered or pages added to or deleted
from the site.
With this system requirement in mind, the original ASP/VBScript solution keeps track
of changes by caching the create and modify time stamps for the configuration file
(Master.XML ) and the transformation file (Master.xsl). The create and modify timestamps
are derived through the COM-based filesystem object and obtained whenever the application
starts or a new user session is created. If the create and modify timestamps for
either file change, the Master.xml and Master.xsl files are reached, rebuilt, and
the common menu is recached.
The legacy code didn't have to be ported, thanks to the ASP.Net caching object (System.Web.Caching.Cache),
which is part of the plumbing that has been incorporated into ASP.Net to make developers'
lives easier. With the dependency on changes to an underlying set of files, instead
of rewriting the legacy code to detect a file change, I created an entry in the
cache that was dependant on a set of files. The cache object determines if the files
have changed and, if so, sets the value of the cache to Nothing. (See
Resources for more information about the caching object in ASP.Net.) When
the named cache object is Nothing, we recreate the dependant information (see Listing 2).
Beyond all of the neat and cool things that you can do with ASP.Net—security, server-side
controls, custom user controls, field editing and validation, page caching, application
tracing, and debugging, to name a few—the object-oriented capabilities of .Net allow
you to use design patterns and reusable code to create robust solutions. It is these
frameworks that will allow you to go beyond the RAD capabilities of ASP.Net to create
elegant, reusable frameworks for any .Net platform applications that you deploy.
About the Author
Andrew C. Mayo is an adjunct professor at Raritan Valley Community College in New
Jersey, where he teaches a course on XML. He is the principal of Carlton Software
Solutions Inc., a New Jersey-based information technology consulting company, where
he specializes in delivering custom software applications using XML and the .Net
platform. Reach Andrew at
XMLProfessor@CarltonSolutions.com.
|