ASP.NET has inherent caching capabilities. However, what if
you are working with a legacy classic ASP application, and need to cache a
particularly intensive page? Perhaps the page pulls a very large set of data
from a database, or does a good deal of calculations. If the results don't
change often, it would be nice to able to cache that pages output and deliver
the cached result to the next user that comes along. Here we demonstrate a simple
technique for doing so.
The goal of the technique is to meet the following criteria:
1) A cached page of the final HTML after the ASP page runs is generated and
stored on the server. This would then be automatically delivered to the user
instead of the ASP page.
2) The caching routine should allow for the storage of a different cached page
per querystring value. Thus, for example, if we are passing an ID in a
querystring to the ASP page, one cached page should be generated for each ID
passed, thus ensuring that even dynamic pages will be cached properly.
3) The caching routine should be simple enough that it can be encapsulated into
server-side includes and placed onto any classic ASP page.
We will approach this technique as two files. One to be referenced as a
server-side include at the top of any classic ASP page, and one to be
referenced at the bottom. The first file we will call
"ASPCacheTop.asp" and the second "ASPCacheBottom.asp".
ASPCacheTop.asp
Let's take this file piece by piece:
Listing 1
<%
Server.ScriptTimeOut = 300
'Declare variables
Dim strResponse
Dim objTextStream
Dim objHTTP
Dim strDynURL, strDynURL2
Dim cache_FILEOBJ, cache_FILETXT
'Get URL
strDynURL = Request.ServerVariables("URL") & _
"?" & Request.ServerVariables("QUERY_STRING")
strDynURL2 = strDynURL
'Generate file name based on URL
strDynURL = Replace(strDynURL, "&", "_")
strDynURL = Replace(strDynURL, "=", "-")
strDynURL = Replace(strDynURL, "/", "")
strDynURL = Replace(strDynURL, "?", "$")
strDynURL = Replace(strDynURL, ".", "@")
Our first step is to increase the script timeout. You may
want to do this if the ASP page takes a long time to execute. This is only
important the first time the page runs - after the page caches the cached
result will simply be read from the cache file. We then declare all the variables
we will use.
The next step is to grab the URL and querystring for the
page we are currently running. We do this by grabbing these values from the
Request.ServerVariables collection. We combine the two into one string.
After this, we generate a "saveable" name for the text file. The URL
and querystring contain characters which cannot be used in the name of a file,
so we replace those characters with others. So, for example, if the URL was
"www.theabstractionpoint.com/cachingclassicasp.asp?ID=2&Category=1",
after replacing we would have a string that looks like
"www@theabstactionpoint@comcachingclassicasp@asp$ID-2_Category-1".
This string could then be saved as the name of a text file (i.e.
"www@theabstactionpoint@comcachingclassicasp@asp$ID-2_Category-1.txt").
This will be the name of our cached file. By basing the file name on the entire
querystring, we will cache a different file for each value passed in the
querystring.
Moving on, we will actually begin the caching process:
Listing 2
'Create file system object
set cache_FILEOBJ = Server.CreateObject("Scripting.FileSystemObject")
'check the "expires" file, which tells us if we've tried to start caching
if not cache_FILEOBJ.FileExists(Server.Mappath(
_"cachefiles/" & strDynURL & "_started.txt")) then
'create the "expires" file, which simply
'tells us that we've tried to start caching
Set cache_FILETXT = _
cache_FILEOBJ.CreateTextFile(Server.Mappath( _
"cachefiles/" & strDynURL & "_started.txt"), True)
We first declare a file system object. We then run an important
check. The first step to caching a file will be to write an empty text file
which tells us that we have begun the caching process. Why? Suppose the first
user comes in and requests the ASP page. Now suppose while that page is caching
another user comes in requesting the same page. We don't want multiple attempts
to the write the cache file at the same time. So, we first write a quick empty
text file to let us know that the process has already started. If the process
has already started, we won't try to cache again. We name this empty file the
same as the cache file itself, but place "_started" on the end of the
filename.
The other very important reason is to prevent an infinite loop. We have just
requested a file, say it is called "cachingclassicasp.asp". We will
very shortly be requesting that page again (through the ASP XMLHTTP object). If
we didn't somehow mark that we had started the caching process, we would find
ourselves in an infinite loop.
Note, too, that in the Server.Mappath we assume you want to place your cache
files in a directory called "cachefiles". You can change this folder
name to suit your own needs. Just be certain ASP has permission to write files
to the folder.
If this file does not exist (i.e. nothing has started caching yet), then we
need to run the actual caching process:
Listing 3
'grab the contents of the page
Set objHTTP = Server.CreateObject("MSXML2.XMLHTTP")
objHTTP.open "GET", "http://www.theabstractionpoint.com" & strDynURL2, false
objHTTP.send()
'output results to screen
response.write objHTTP.responseText
'create cache text file
Set cache_FILETXT = _
cache_FILEOBJ.CreateTextFile(Server.Mappath( _
"cachefiles/" & strDynURL & ".txt"), True)
'save results
cache_FILETXT.WriteLine objHTTP.responseText
'clean up
cache_FILETXT.Close()
set objHTTP = nothing
response.end
Here we have the meat of the caching routine. We declare an
XMLHTTP object which we then use to make an HTTP request to the page we are
trying to cache. You will need to replace "http://www.theabstractionpoint.com"
with the address of your own site. Recall that the variable
"strDynURL2" held a copy of the original URL (i.e. name of the page)
and querystring being requested.
We then make the request, and response.Write the response (which will be the
HTML of the page). We response.Write so that the user will actually see the
page after it is cached.
We then create the text file in our "cachefiles" directory using the
adjusted text file name. Then, we write to the file the HTML we got when the
page was done executing. Finally, we close the file and
"response.end", ending the execution of the page. Thus, the first
time someone requests the page (with particular querystring values), the empty
"started" file will be written, the ASP page will be requested via
the XMLHTTP object and the results outputted and saved to the cache file.
Now, what if the "started" file already existed?
The following occurs:
Listing 4
else
'otherwise, we've already cached, so grab the contents of the cache file and output
if cache_FILEOBJ.FileExists( _
Server.Mappath("cachefiles/" & strDynURL & ".txt")) then
set cache_FILETXT = _
cache_FILEOBJ.OpenTextFile(Server.Mappath( _
"cachefiles/" & strDynURL & ".txt"), 1)
response.write cache_FILETXT.ReadAll()
cache_FILETXT.Close()
response.end
else
'if we haven't already requested the page for caching,
'then actually run the code for the page
%>
If the cache file has been started, check to see if it is
finished (i.e. the cache file exists). If it does exist, grab the contents of
the cache file and output to the screen. Otherwise, just run the ASP file like
normal. Since this file will go at the very top of your ASP page, the code of
your page will be below the final "else".
ASPCacheBottom.asp
The second page, which should be a server-side include at
the bottom of your page, couldn't be any simpler. We just need to close out the
IF-statements we started in the top file. So this file is nothing more than:
Listing 5
Usage
Thus we have a simple and easy to implement caching routine
for any classic ASP page. If you want to cache any page, just save these two
files, give ASP permission to write to the folder you chose to store your cache
files in, and then update your ASP page as follows:
Listing 6
<!--#include file="ASPCacheTop.asp" -->
<%
YOUR HTML AND ASP CODE
%>
<!--#include file="ASPCacheBottom.asp" -->
The only thing left would be the question: when and how do
we clear the cache files? That is up to you. You could have a job running on
the server that regularly empties or archives the folder where the cache files
live. Or you could update the caching process to write a "expires"
file which holds the date the cache file should expire. When you check if the
cache file exists, you could then check that file to see if the cache file has
expired and should be rewritten or not.
Once you implement everything, you get the rewards of far
less load on your server and pages being delivered lightening fast!
NOTE: This is a reprint of an
article originally published here