|
Introduction
There is a way to implement a caching system on your Web site. HTML Knowledge (especially the use of FORMS and cookies), JavaScript, and SQL Insight is preferable to understand this paper.
In this Introduction we will first define what we mean by a caching system, and explain where and why it is useful, and what it should do. In the next section (Outline) we will outline what kind of functionality we need, and how to divide this over some main functions. These functions will be detailed in the Implementation section, including the source code in JavaScript. After this we will show how to use the cache (Example Pages section) in a simple example. The How to Go On section gives some ideas to experiment with or to enhance the system.
What is a caching system?
In terms of computer hardware, the "cache" is a relatively small amount of high-speed memory. Programs accessing the same data over and over again store this data in the cache memory, instead of accessing the slower conventional memory.
When collecting data entered by visitors on your Web site, we can create a similar system to:
- Collect and store all data in a cache;
- Retrieve and/or process this data; and
- Save collected and processed data from the cache to any database.
Origin and use of the caching system
First, let us explain where and why we developed the caching system.
We develop and maintain the Web site for DYME computers in the Netherlands.
DYME is a company that delivers custom-built PCs. On its Web site it’s possible for interested and potential customers to interactively construct their own "dream PC."
Only limited by technical possibilities (a SCSI hard disk needs a controller, some motherboards need a different kind of RAM, etc.) a customer has full control in choosing products for this new computer.
A Web site visitor first chooses for one of the "PC lines," such as Graphix, Business, Connexions, or Enthousiast. The visitor will be presented a basic configuration from which all the products can be changed to fit the visitor’s purposes. The group of products available depends on the PC line chosen. For example, a Games line doesn’t need SCSI devices and the Grafix line only offers 17-inch (or more) monitors.
Next, the system will present the adjusted configuration with the new price. Below this section some interesting options are presented. Based on the difference in price after adding the chosen product, some other products are presented and can also be added.
For example, when a visitor chooses a Western Digital 4.3GB hard disk, the system may present the 6.3GB disk because the difference in price is below a certain limit - let’s say $50. By doing this we can give the customers the possibility to tweak their dream PC while staying within their budget or maybe tempt them to spend more money.
The customer has now created the basis of their dream PC. The next page presents a list of questions, like "Do you want to play games?", "Do you want to surf the Internet?", "Do you plan to edit videos on your PC?", "Do you want to scan or print documents?".
The system presents new products related to the answers to these questions, such as joysticks, printers, scanners, modems, etc. Customers can choose their products and are then presented with a final overview of their entire PC configuration.
This system works in much the same way that DYME sells its computers in the (IRL) showroom.
The system also allows users to search for previously created configurations, to order PCs, and to leave their name and address, if they require further information, etc.
(Note: This system isn’t online yet and will only be available in Dutch!).
The program flow makes it easy for users to switch between the different steps, see their final configuration at any time in the process, etc.. Records of earlier versions of the site show the average user creates 3.5 configurations per visit. So users appreciate not having to follow the entire creation process and being able to adjust single aspects of a preselected computer configuration.
In terms of databases, it’s preferable to keep only the final created PC, without saving all trials that were not satisfactory to the customer. On the other hand, from a marketing and sales point of view, it’s interesting to know how DYME customers create a system and on which part/product they want to spend or save money. So it must also be possible to save and restore the customers’ behavior (data warehousing).
When constantly posting form variables to keep track of the user and the chosen options, the standard browser cache either can not or will not automatically keep track of the posted form variables. It only returns annoying messages (content/form has expired) which prevent most users from easily switching between steps.
Above we highlighted some design goals and features for a Web site. We developed the caching system to meet these goals. It’s used as a background tool, so this paper does not tell you how to build this site. This paper explains how to set up a caching system that proved very useful in the situation outlined above, where lots of user-interaction needs to be collected, processed, stored, and made available again.
Why do we need a caching system like this?
In HTML it is already possible to collect and parse data. This is usually done by:
- Using cookies to save and retrieve data.
-
- Using the FORMS GET method.
- Using the FORMS POST method.
This way works fine when you only have 1 or 2 pages where user data is collected.
When your Web site consists of several pages where information is submitted, it can easily run into trouble.
Let’s look at an example where we have 4 pages where user data is collected.
- On page 1 the data is typed in input boxes and parsed via the FORM’s POST or GET method. Using cookies isn’t advisable for long texts, so that’s not an option.
- On page 2 we need to read this data (by means of the Request.Form or Request.QueryString commands) and then save this data via hidden form vars or cookies. Maybe some additional data is entered in page 2.
- On page 3 this step is repeated, making the list of saved data even longer. Maybe some additional data must be entered here too, and parsed, along with data collected on page 1 and 2, to page 4.
- On page 4, we can collect all data and save it to all appropriate databases. Since it has to be possible to jump into this process at any point (start on page 3, go to 1, 2, and end on page 4), we must make it possible to store and parse all data on every page.
When using the caching system presented here, we can achieve some benefits:
- It’s hard to mess up or corrupt entered data by manually changing the QueryString because all data is saved invisible to users.
- Disabling cookies won’t prevent proper working. The caching system does not save entered data in cookies.
- Entered data is only submitted once. (We still have to worry about the number of sent and received bytes!)
- From a programming perspective, using a separate system to parse data makes your code simpler, with fewer changes for bugs.
- While it is possible for users to skip from one page to another, it can be complicated to save any changed data to the final database.
Using the caching system makes your Web site more reliable, more secure, and faster!
What should this cache look like?
The main line should be obvious. We want to write values to the cache database, and on any moment we want to read back this data.
When implementing the caching system as an Include file, it’s easy to start, debug, and use caching. Actually using Includes is always an easy way to debug extensions.
It is, of course, essential to remember which entry in the cache database refers to which user, so we need to pay some attention to this.
We don’t want users to get mixed up, and we also don’t want the database to grow too much, so we also need some cleaning routines, time checks, etc.
We want to create a very tweakable system that can be used in any Web site, with any database (within certain limits, like Windows NT and the Internet Information Server (IIS) as the operating system, Open Database Connection (ODBC) as the database port, HTML for front-end/interface, etc). As explained above we had an explicit goal for our system, but we also wanted to be certain that the system was tweakable to other projects.
A few simple function calls should do the background processing for us.
There are some limits to the caching system.
For instance, the amount of data processed can be large. This system is tested on an average usage of 100 visitors per day. We don’t know the system’s behavior with larger amounts of traffic.
Every visitor gets an internal SessionID, based on a time stamp. Theoretically it’s possible for users to generate the same time stamp (when they enter the site at exactly the same time). Perhaps the system should be extended with an IP-address recognition feature. However, IP address alone will not be sufficient because proxy servers give the same IP addresses for different users!
The combination of an IP address and a time stamp can provide enough certainty about a unique user. An IP address for user recognition isn’t used here, but you can work this out, if necessary. We also added a random number to the time stamp, making duplicates nearly impossible.
Above we stated the main functionality needed. In our use of the caching system we enhanced it to better fit our purposes. If you understand the working of the caching system, these conditions can be programmed in or out as you wish.
Outline
In this outline we will describe what sort of functions we need and introduce some minor details and features.
Presets
As said earlier we are programming the caching system as an Include file.
The first few lines from this Include should define some globally used variables and create the database object.
Init cache
After this we need to check some things: Are we dealing with a new user? If so, we need to give this user an unique ID and create an entry in the cache database.
If the user already has a database entry, we need to look up the user’s SessionID. This point is very important. If we are not able to find a database entry for a user who already entered some data, we lose this data. At the same time we don’t want to connect a known user to the wrong entry in the database or connect the user twice.
Some possibilities to obtain this:
- Setting a cookie with the caching systems’ SessionID,
- Parsing the SessionID via (hidden) form var’s (obtained via Request.QueryString and/or Request.Form),
- Using the SessionID already available in IIS (in fact just a cookie),
- Or as mentioned above, by IP address.
The only way to ensure the degree of security needed is to use them all!
The exception is the uncontrollable SessionID of MS IIS which creates and sets its own SessionID.
Finally, it would be nice if this function returns the created or used SessionID for later use.
Write to cache
If we have a SessionID we can write any data to the cache in the proper entry. We prefer to not be dependent on any database structures or naming of variables.
Read from cache
Well, it should be clear by now that we need a SessionID and a reference to the data we want to read from the cache and to make it usable in the rest of our ASP application.
Close the cache
To prevent the cache from growing too much (and making it slower) we need to detect the moment when a user logged off (i.e., left our site). Then we must delete the user’s database entry.
Other functions
Maybe we need to backup the data for future use or copy the entered data to the final databases. We may want to be able to extract some statistics from the cache database (like cache hits, etc.).
Functions used but only included in the downloadable part
Some functions we use are only included in the downloadable source attached to this article.
These are:
SetCookieJS, DelCookieJS: To set and delete a cookie using JavaScript.
CloseConnection: To close the database object.
SE (StringEqual): Compares two strings and returns true if the strings match, otherwise it returns false.
GetNewIDNumber: This function returns the new IDNumber for inserting a new Session in the cache database.
FieldExits: Checks for the existence of a field in the cache database. Writing a value named "ClientName" can only be done if there is a field in the Access database with that name!
Implementation
In this section we start to write the JavaScript code for the functions mentioned above.
The code should be self-explanatory. First we offer some notes and remarks.
Why JavaScript?
We prefer programming in JavaScript because:
- We like the C-like syntax,
- Of its clear structure,
- Of the lack of error-checking,
- Of the explicit declaration of variables (well, as specified for JavaScript).
- Of the no-nonsense approach (function, if, else, while, for, etc.).
- Bill hates it, and Visual Basic (VB) is for wimps.
Unfortunately we’re forced by evil to use VBScript for setting server-side cookies to Internet Explorer (IE), as well as to Navigator. (We consider this a bug of IIS!)
We did not do this for this article (because it takes some more coding, which is not essential for the scope of this article). We used JavaScript cookies here. Using VBScript gives you much more control over a cookie: setting expiration date, domain, and path, etc.
Debugging
In every extension we develop we use a separate debugging structure in the form of:
if (bDebugExt) // Boolean which activates debugging
sDebugext += // String saving all debugging information
At the end of the page when bDebugName is true, the sDebugName is flushed and printed on the screen.
This is useful when debugging and working with server-side cookies. These cookies have to be sent to the Web client first. Sending debugging info and other data to the client can only be done after setting cookies and other HTTP headers. The sDebug string also makes it easier to track and watch your program flow.
HTML
There is another point that needs attention, however it isn’t worked out in this paper. Always take measures to prevent special characters from being inserted in any database. SQL does not like " and ‘ mixed up when saving strings. Replacement of < (<) with << (G) is advisable to prevent users from entering malicious HTML code (VBScript or JavaScript "viruses").
MS Access
The last note is about the used Access database. We know it is possible to alter tables from within ASP/HTML pages, but we did not include this in this article.
When doing this it’s not necessary anymore to create an entry in your Access file for every used cache value you wish to save. See the included source code.
We will now include the source code for the caching system, hopefully with enough comments to make clear what we’re doing.
Presets and Intialization
<%
var iTimeOut = 3600000; // TimeOut to CleanUp Cache
var sTemp = new String();
var sSQL = new String();
// Debug caching:
var sDebugCache = new String( '<HR><FONT FACE="Verdana" SIZE="2" COLOR="Green">' );
sDebugCache += '<B>««« Caching include (CachingMain.inc) »»»</B><BR><BR>\n';
// Create the driver, full path, and file name strings:
var sDriver = "driver={Microsoft Access Driver (*.mdb)};dbq=";
var sPath = Request.ServerVariables( 'APPL_PHYSICAL_PATH' );
sPath += "\SampleSite\\mdb\\";
var sDB_Cache = sDriver + sPath + "Cache.mdb;";
// Connect to cache database:
cn_Cache = Server.CreateObject("ADODB.Connection");
cn_Cache.Open( sDB_Cache );
%>
InitCache()
This function initiates the cache for a certain user. The SessionID (which is not the same SessionID as used in ASP) references to the user. First we use three ways to look for an existing SessionID. If this is found, we can do some updating; if not, we create a new SessionID.
function InitCache()
{
var sSessionID = new String();
sDebugCache += '<B>InitCache</B><BR>';
sDebugCache += '<U>Start searching existing session: </U><BR>';
var bSessionNotFound = true;
// Search cookies for SessionID:
sDebugCache += 'Check Cookie "sSessionID": ';
if (bSessionNotFound)
{
sTemp = new String( Request.Cookies( 'sSessionID' ));
sDebugCache += 'sSessionID=' + sTemp + '<BR>';
// Consider Cookie value false, if its length > 5:
if ( sTemp.length > 5 ) { bSessionNotFound = false; }
}
else
sDebugCache += '<I>Not used</I><BR>';
// Search Request string for SessionID:
sDebugCache += 'Check Form var "sSessionID": ';
if (bSessionNotFound)
{
sTemp = new String( Request.Form( 'sSessionID' ));
sDebugCache += 'sSessionID=' + sTemp + '<BR>';
if ( !SE(sTemp, 'undefined') ) { bSessionNotFound = false; }
}
else
sDebugCache += '<I>Not used</I>.<BR>';
// Search QueryString for SessionID:
sDebugCache += 'Check QueryString "SessionID": ';
if (bSessionNotFound)
{
sTemp = new String( Request.QueryString( 'sID' ));
sDebugCache += 'sSessionID=' + sTemp + '<BR>';
if ( !SE(sTemp, 'undefined') ) { bSessionNotFound = false; }
}
else
sDebugCache += '<I>Not used</I>.<BR>';
sDebugCache += '<BR>';
if (bSessionNotFound)
{
// No Session found: Create a new SessionID:
sDebugCache += '<U>Init New Session:</U><BR>';
// Create new SessionID:
var dt = new Date();
var dtTime = dt.getTime();
sSessionID = new String( parseInt( dt.getTime() ) - 900000000000 );
sSessionID += '-' + parseInt( 100*Math.random(10) );
sDebugCache += "sSessionID = " + sSessionID + '<BR>';
// Get NewID to save new cache entry:
var iIDNumber = GetNewIDNumber( cn_Cache, 'Cache' );
// Create database entry:
sDebugCache += 'Creating database entry: ';
sSQL = "INSERT INTO Cache ";
sSQL += "(iIDNumber, sSessionID, iSessionStart, iSessionUpdate)";
sSQL += " VALUES (" + iIDNumber + ", '" + sSessionID + "', " + dtTime + ", " + dtTime + ");";
sDebugCache += '<I>' + sSQL + '</I><BR>';
cn_Cache.Execute( sSQL );
// Create cookie:
sDebugCache += 'Setting Cookie.<BR>';
SetCookieJS( 'sSessionID', sSessionID );
}
else
{
// Session found!
sDebugCache += '<U>Session Found!</U><BR>';
sDebugCache += 'Verify found SessionID:<BR>';
sSessionID = sTemp;
// Check existence in database:
sSQL = "SELECT sSessionID FROM Cache WHERE sSessionID LIKE '" + sTemp + "';";
sDebugCache += "sSQL: " + sSQL + "<BR>";
rs_Cache = cn_Cache.Execute( sSQL );
if (!rs_Cache.EOF && !rs_Cache.BOF)
{
// Found!
sDebugCache += 'SessionID found in database!<BR>';
}
else
{
// Problem: Session not found in database:
sDebugCache += '<B>Error</B>: Recreating database entry: ';
sSQL = "INSERT INTO Cache (sSessionID) VALUES ('" + sSessionID + "');";
sDebugCache += '<I>' + sSQL + '</I>';
cn_Cache.Execute( sSQL );
}
}
sDebugCache += '<BR>';
sDebugCache += 'Returning: ' + sSessionID + '<BR>';
sDebugCache += '<HR>\n';
return sSessionID;
}
Write cache value
Here is the code for writing a value to the cache. If a value is already set or saved, we update it with the new value.
function WriteCacheValue( _sName, _sValue )
{
sDebugCache += '<B>WriteCacheValue</B><BR>';
sDebugCache += 'Writing for Session: ' + sSessionID + '<BR>';
// Check field existence:
sDebugCache += 'Checking field existence for <I>' +
_sName + '</I>.<BR>';
if ( FieldExists( _sName ) )
{
// Field exists:
sDebugCache += 'Field found. Update: ';
// Get this time:
var dt = new Date();
var dtTime = dt.getTime();
// Write field value:
sSQL = "UPDATE Cache SET " + _sName + "='" + _sValue +
"',iSessionUpdate=" + dtTime + " WHERE sSessionID LIKE '" +
sSessionID + "';"; sDebugCache += '<I>' + sSQL +
'</I><BR>';
rs_Cache = cn_Cache.Execute( sSQL );
}
else
{
// Field doesn’t exist:
// Below looks nice, but the problem is to make the database "exclusive"
// which isn't a very good idea if it's possible to have
// more than 1 simultaneous user!
sDebugCache += '<B>Error</B>: Field <I>not</I> found in database.<BR>';
sSQL = "ALTER TABLE Cache ADD COLUMN " + _sName + " TEXT NOT NULL"
sDebugCache += '<I>' + sSQL + '</I><BR>';
rs_Cache = cn_Cache.Execute( sSQL );
}
sDebugCache += '<HR>\n';
}
Here is the code for reading a value from the cache:
function ReadCacheValue( _sName )
{
sDebugCache += '<B>ReadCacheValue</B><BR>';
sDebugCache += 'Reading for Session: ' + sSessionID + '<BR>';
sDebugCache += 'Reading Field: ' + _sName + '<BR>';
sSQL = "SELECT " + _sName + " FROM Cache WHERE sSessionID LIKE '" + sSessionID + "';";
sDebugCache += '<I>' + sSQL + '</I><BR>';
rs_Cache = cn_Cache.Execute( sSQL );
if (!rs_Cache.EOF && !rs_Cache.BOF)
{
// Found value:
sTemp = rs_Cache( _sName ).value;
sDebugCache += 'Found Value: ' + sTemp + '<BR>';
}
else
{
// Error: Field not found!
sDebugCache += '<B>Error</B>: Value <I>not</I> found!<BR>';
}
sDebugCache += '<HR>\n';
return sTemp;
}
CloseCache()
function CloseCache()
{
sDebugCache += '<B>CloseCache</B><BR>';
sDebugCache += 'Closing session: ' + sSessionID + '.<BR>';
// Delete database entry:
sDebugCache += 'Database: ';
sSQL = "DELETE FROM Cache WHERE sSessionID LIKE '" + sSessionID + "';";
cn_Cache.Execute( sSQL );
sDebugCache += 'Session deleted.<BR>';
// Delete cookie:
sDebugCache += 'Cookie: ';
DelCookieJS( 'sSessionID' );
sDebugCache += 'Session deleted.<BR>';
sDebugCache += '<HR>\n';
}
CleanUpCache()
function CleanUpCache()
{
sDebugCache += '<B>CleanUpCache</B><BR>';
var dt = new Date();
var dtTime = dt.getTime();
dtTime -= iTimeOut;
sSQL = "DELETE FROM Cache WHERE iSessionUpdate < " + dtTime;
sDebugCache += 'sSQL: ' + sSQL + '<BR>\n';
cn_Cache.Execute( sSQL );
sDebugCache += 'Cache Cleaned.<BR>';
sDebugCache += '<HR>\n';
}
We now implemented the main functions. Let’s look at an example of how to use the caching system in some simple ASP pages.
Example Pages
In this section we will create a very simple application using the caching system.
A visitor can first enter his name, then a color, and finally a taste.
After this, all submitted data will be presented on screen and the cache entry can be deleted.
These pages work with the Personal Web Server under Windows 95 or Windows 98, and with the Internet Information Server (IIS) under Windows NT.
There is a tricky part in every page below. To make it possible to navigate through the pages there are (positioned right in your browser window) some links. In the pages below this part starts from the following:
<P ALIGN="right"> tag.
Note that the only possible ways to parse the SessionID is by means of the cookie and with a QueryString. When your visitor has cookies disabled and manually changes the QueryString, we lose the connection between the user and his or her SessionID. As mentioned above, this can lead to loss of data!
You can solve things problem by making the link a "Form.Submit();." The SessionID can then also be parsed via a can-not-change-hidden-form var. You can also hide the address bar to prevent user interference in the QueryString from the Web browser, but every browser does not support this.
GetName.asp
<%@ Language=JavaScript %>
<!--#INCLUDE VIRTUAL="/SampleSite/Inc/CachingMain.inc"-->
<!--#INCLUDE VIRTUAL="/SampleSite/Inc/SE.inc"-->
<%
// Init cache:
var sSessionID = InitCache();
// Do an initial cleanup:
CleanUpCache();
// Read name:
var sName = new String( Request.Form( "sName" ) );
// If valid name found, save name and redirect:
if ( !SE( sName, 'undefined' ) && sName.length>0 )
{
// Write entered data to cache:
WriteCacheValue( "Name", sName );
// Redirect to the next page:
Response.redirect( "GetColor.asp?sID=" + sSessionID );
}
%>
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso8859-1">
<META NAME="Author" CONTENT="Jan Hein de Waal Malefijt (MDCio@DDS.NL), Bart van Langen (Bart@Dyme.NL)">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="now">
<TITLE>Enter your name</TITLE>
</HEAD>
<BODY>
<BASEFONT FACE="Verdana, Arial" SIZE="2">
<FORM ACTION="GetName.asp?sID=<%= sSessionID %>" METHOD="POST" NAME="SampleForm">
<INPUT TYPE="HIDDEN" NAME="sSessionID" VALUE="<%= sSessionID %>">
Type your name: <INPUT TYPE="Text" NAME="sName" VALUE=""><BR>
<BR>
<INPUT TYPE="Submit">
</FORM>
<P ALIGN="right">
<U>Already entered:</U><BR>
Your <A HREF="GetName.asp?sID=<%= sSessionID %>">name</A> is <B><%= ReadCacheValue( "Name" ) %></B><BR>
Your <A HREF="GetColor.asp?sID=<%= sSessionID %>">Color</A> is <B><%= ReadCacheValue( "Color" ) %></B><BR>
<A HREF="GetTaste.asp?sID=<%= sSessionID %>">You Like</A> <B><%= ReadCacheValue( "Taste" ) %></B><BR>
</P>
<BR>
<% CloseCacheConnection() %>
<%= sDebugCache %>
</BODY>
</HTML>
GetColor.asp
This page looks much like the page printed above:
<%@ Language=JavaScript %>
<!--#INCLUDE VIRTUAL="/SampleSite/Inc/CachingMain.inc"-->
<!--#INCLUDE VIRTUAL="/SampleSite/Inc/SE.inc"-->
<%
// Init cache:
var sSessionID = InitCache();
// Read color:
var sColor = new String( Request.Form( "sColor" ) );
// Save color and redirect:
if ( !SE( sColor, 'undefined' ) && (sColor.length>0) )
{
// Write entered data to cache:
WriteCacheValue( "Color", sColor );
// Redirect to the next page:
Response.redirect( "GetTaste.asp?sID=" + sSessionID );
}
%>
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso8859-1">
<META NAME="Author" CONTENT="Jan Hein de Waal Malefijt (MDCio@DDS.NL), Bart van Langen (Bart@Dyme.NL)">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="now">
<TITLE>Enter a Color</TITLE>
</HEAD>
<BODY>
<BASEFONT FACE="Verdana, Arial" SIZE="2">
<FORM ACTION="GetColor.asp?sID=<%= sSessionID %>" METHOD="POST" NAME="SampleForm">
<INPUT TYPE="HIDDEN" NAME="sSessionID" VALUE="<%= sSessionID %>">
Choose a color:
<SELECT NAME="sColor">
<OPTION VALUE="Red">Red</OPTION>
<OPTION VALUE="Yellow">Yellow</OPTION>
<OPTION VALUE="Green">Green</OPTION>
</SELECT><BR>
<BR>
<INPUT TYPE="Submit">
</FORM>
<BR>
<P ALIGN="right">
<U>Already entered:</U><BR>
Your <A HREF="GetName.asp?sID=<%= sSessionID %>">name</A> is <B><%= ReadCacheValue( "Name" ) %></B><BR>
Your <A HREF="GetColor.asp?sID=<%= sSessionID %>">Color</A> is <B><%= ReadCacheValue( "Color" ) %></B><BR>
<A HREF="GetTaste.asp?sID=<%= sSessionID %>">You Like</A> <B><%= ReadCacheValue( "Taste" ) %></B><BR>
</P>
<BR>
<% CloseCacheConnection() %>
<%= sDebugCache %>
</BODY>
</HTML>
GetTaste.asp
And again:
<%@ Language=JavaScript %>
<!--#INCLUDE VIRTUAL="/SampleSite/Inc/CachingMain.inc"-->
<!--#INCLUDE VIRTUAL="/SampleSite/Inc/SE.inc"-->
<%
// Init cache:
var sSessionID = InitCache();
// Read taste:
var sTaste = new String( Request.Form( "sTaste" ) );
// Save taste and redirect
if ( !SE( sTaste, 'undefined' ) && sTaste.length>0 )
{
// Write entered data to cache:
WriteCacheValue( "Taste", sTaste );
// Redirect to the next page:
Response.redirect( "ShowResults.asp?sID=" + sSessionID );
}
%>
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso8859-1">
<META NAME="Author" CONTENT="Jan Hein de Waal Malefijt (MDCio@DDS.NL), Bart van Langen (Bart@Dyme.NL)">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="now">
<TITLE>Enter what you like</TITLE>
</HEAD>
<BODY>
<BASEFONT FACE="Verdana, Arial" SIZE="2">
<FORM ACTION="GetTaste.asp?sID=<%= sSessionID %>" METHOD="POST" NAME="SampleForm">
<INPUT TYPE="HIDDEN" NAME="sSessionID" VALUE="<%= sSessionID %>">
What do you like: <INPUT TYPE="Text" NAME="sTaste" VALUE=""><BR>
<BR>
<INPUT TYPE="Submit">
</FORM>
<BR>
<P ALIGN="right">
<U>Already entered:</U><BR>
Your <A HREF="GetName.asp?sID=<%= sSessionID %>">name</A> is <B><%= ReadCacheValue( "Name" ) %></B><BR>
Your <A HREF="GetColor.asp?sID=<%= sSessionID %>">Color</A> is <B><%= ReadCacheValue( "Color" ) %></B><BR>
<A HREF="GetTaste.asp?sID=<%= sSessionID %>">You Like</A> <B><%= ReadCacheValue( "Taste" ) %></B><BR>
</P>
<BR>
<% CloseCacheConnection() %>
<%= sDebugCache %>
</BODY>
</HTML>
ShowResults.asp
<%@ Language=JavaScript %>
<!--#INCLUDE VIRTUAL="/SampleSite/Inc/CachingMain.inc"-->
<!--#INCLUDE VIRTUAL="/SampleSite/Inc/SE.inc"-->
<%
// Init cache:
var sSessionID = InitCache();
// Process results:
// You can process the collected data here.
// This could be something like:
// - Checking if all entered data is valid, and if not,
// send the visitor back to the page where data is invalid or not entered.
// - Saving data to the appropriate databases.
%>
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso8859-1">
<META NAME="Author" CONTENT="Jan Hein de Waal Malefijt (MDCio@DDS.NL), Bart van Langen (Bart@Dyme.NL)">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="now">
<TITLE>Results</TITLE>
</HEAD>
<BODY>
<BASEFONT FACE="Verdana, Arial" SIZE="2">
Your name is <B><%= ReadCacheValue( "Name" ) %></B><BR>
Your Color is <B><%= ReadCacheValue( "Color" ) %></B><BR>
You Like <B><%= ReadCacheValue( "Taste" ) %></B><BR>
<BR>
<A HREF="GetName.asp">Start over without resetting</A><BR>
<BR>
<A HREF="CloseCache.asp?sID=<%= sSessionID %>">Close cache and start over</A><BR>
<BR>
<% CloseCacheConnection() %>
<%= sDebugCache %>
</BODY>
</HTML>
CloseCache.asp
<%@ Language=JavaScript %>
<!--#INCLUDE VIRTUAL="/SampleSite/Inc/CachingMain.inc"-->
<!--#INCLUDE VIRTUAL="/SampleSite/Inc/SE.inc"-->
<%
// Init cache:
var sSessionID = InitCache();
CloseCache();
CloseCacheConnection();
%>
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso8859-1">
<META NAME="Author" CONTENT="Jan Hein de Waal Malefijt (MDCio@DDS.NL), Bart van Langen (Bart@Dyme.NL)">
<META HTTP-EQUIV="Pragma" CONTENT="no cache">
<META HTTP-EQUIV="Expires" CONTENT="now">
<TITLE>Closing cache</TITLE>
</HEAD>
<BODY>
<BASEFONT FACE="Verdana, Arial" SIZE="2">
The cache is closed now.<BR>
<BR>
<A HREF="GetName.asp">Start over</A><BR>
<BR>
<BR>
<%= sDebugCache %>
</BODY>
</HTML>
We now created a sample site using the caching system. This sample is for demonstration purposes only. Obtaining three input fields from a user can be done in a much easier way! Things change when you have 4 pages asking detailed information, such as addresses for work, home, and occupation, etc. (Ever subscribed to Microsoft Developer Network (MSDN)?)
Though the code provided here is self-explanatory, it’s better to test and see these pages via a Web server!
How to Go On
We showed you how to program a caching system and how to use it. Of course there are a lot more possibilities when using such a system. We encourage you to play with it, add functionality, or whatever. Of course we are interested in your comments and add-ons.
Save cache data for future visits
In the last page of the sample application (CloseCache.asp) we only close the cache, deleting all entered date. Of course your application should first process and then save the data to all appropriate databases.
Maybe you would like personal data (like name, address, etc.) saved in your contacts database. If your Web site offers the possibility to customize the site (like choose a language, text, or full-graph version, used browser, etc.) you might save this data in another database. All could be connected and based on the processed user. So you have a user profile.
Use the cache to collect visitor data and statistics
As the cache database used here saves details as time of entrance for new users and time of last update, it is possible to get a detailed picture on how your visitors use your Web site. This can be enhanced even more if you save an entry for all the used pages. Creating "walk-through profiles" gives you the opportunity to completely analyze the way your Web site is used by your visitors.
Use the cache for debugging
When creating complicated ASP applications with a very wide program flow, debugging can be achieved by saving all temporarily variables in the cache. When testing your page, you can follow the CacheDebug string or the Cache database, or any self-constructed string, to watch variables and program flow change.
About the Author
Jan Hein de Waal Malefijt (JanHein@ODISYS.nl) and Bart van Langen (Bart@dyme.nl) maintain and develop the Web site for DYME computers in the Netherlands. In the first quarter of 1999 they planned to combine their skills and experience in a company to advise, design, and create highly interactive database-driven applications for the Intranet and Internet.
|