Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Content Wrapping with Master Pages and HTTPModules

0.00/5 (No votes)
23 Oct 2007 1  
Wrapping static content with Master Pages and an IHttpModule.

Introduction

Recently, I had a client that was redesigning a website that had hundreds of static files and a few sections of dynamic content. They were asking me for options on how to make this site maintainable, without writing, purchasing, or installing a CMS (Content Management System -- such as DotNetNuke) tool. I initially recommended they create a Master Page and then move all the content of the static pages into individual .aspx pages that used the Master Page. They did not have a lot of ASP.NET skills, so this was somewhat unappealing to them. They liked the Master Page concept; however, they preferred to simply edit the static HTML files and put the new layout into the files.

This seemed like a poor solution to me, because the website would still have hundreds of files to be edited manually whenever it was to be redesigned in the future. I recommended a different solution. Create an IHttpModule that checks to see if the request is for a valid page. If it is, then pass it through. If not, then check to see if a "content" file exists for the URL requested. If so, then pass the request to a page that knows how to read that "content" file and place its contents into a Master Page layout.

This article describes a very simple implementation of this architecture.

Background

You might want to read up on:

Using the Code

Let's assume that our website has the following files:

/index.html
/articles/Article1.html
/articles/Article2.html

and we want all of them to have the same layout. We could create a MasterPage and have the project look like this:

/default.aspx
/Site.Master
/articles/Article1.aspx
/articles/Article2.aspx

but this is not a model that scales well, since you will have to create a new .aspx page for every single page.

What I propose is that you create a MasterPage and extract the content from static pages into .con files (or whatever extension you like). The project layout would then look like this:

/default.aspx
/wrapperform.aspx
/Site.Master
/articles/Article1.con
/articles/Article2.con

WrapperForm.aspx is a webpage that uses the MasterPage and knows how to read the .con file and shove it into its Content control.

There are two other things that are needed.

First is the control that understands how to read in the .con file and write it to the response (WrapperForm.aspx can't do this since it can't write out specifically in the Content control). This control is placed in the Content control on WrapperForm.aspx.

Wrapper (extending WebControl) has this RenderContent method:

// Get the path of the query string
string path = Context.Request["path"];
if ( !string.IsNullOrEmpty(path))
{
    // read it in
    using( FileStream stream = File.OpenRead( Context.Server.MapPath( path )) )
    {
        byte[] b = new byte[1024];
        ASCIIEncoding encoding = new ASCIIEncoding();
        while ( stream.Read( b, 0, b.Length ) > 0 ) 
        {
            // write it out
            output.Write(encoding.GetString(b));
        }
    }
}

It simply gets the .con file from the file system and writes it out to the response.

Secondly, we'll need a way to get IIS to hit our WrapperForm.aspx for pages that don't really exist. We'll use an IHttpModule for that.

RequestHandler listens to every request, and if the physical file requested isn't found, it checks for a corresponding .con file. If the content file exists, then it transfers the request to the WrapperForm.aspx page. The block of code that accomplishes this feat is:

HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
// Have we requested a file that exists?
if ( !File.Exists( context.Server.MapPath( context.Request.FilePath ) ) )
{
    // Check for a content file on the same path
    string path = Path.ChangeExtension( context.Request.FilePath, ".con" );
    if (File.Exists(context.Server.MapPath(path)))
    {
        // Forward to our wrapper
        context.Server.Transfer( "~/WrapperForm.aspx?path=" + path, true );
    }
}

The next issue is that for non-ASP.NET handled extensions. IIS will not pass the request into the .NET code. What this means is that if we wanted all .html files to be handled, they will never make it to our RequestHandler because IIS is watching .html files and throws a 404. You can either make the fake URLs be .aspx pages, or you can change the application's document mapping to route .html (or whatever) pages through ASP.NET (which might be difficult in hosted environments).

Results

In the included source, you'll find the following content structure:

/default.aspx
/WrapperForm.aspx
/test.con
/dir/junk.con

Using this solution, I am able to browse to:

  • default.aspx and the default page is hit
  • test.html (and test.aspx) and the test.con data is reproduced in the correct layout
  • dir/junk.html (and junk.aspx) and the junk.con data is reproduced in the correct layout

Points of Interest

This isn't a particularly special solution. It is very basic, and really it seems to only solve one problem, the "skinning" of static content. This solution really does add quite a bit of value.

  • The content can easily be migrated to a database (just change the Wrapper control to get it from the database rather than the file system)
  • Old, existing, deep links still work
  • Search engine links are maintained

If I was to implement this for a client, I would make the content files have a little more meat. I would make them be XML, containing the title of the page (this solution is defaulting to the title of the file), some SEO meta content, and breadcrumb trail information, all things a CMS would provide.

History

No updates yet.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here