Beginning Active Server Pages 3.0
Beginning Active Server Pages 3.0
Overview
Active Server Pages (ASP) is a great tool for creating dynamic web pages. ASP is a Microsoft
technology, and it works by allowing us the functionality of a programming language; we write
programming code that will generate the HTML for the web page dynamically. So, whenever a
user browses to our web site and requests one of our ASP pages, the ASP code is processed at
that time by a special piece of software – the web server. This processing generates the HTML,
which is then passed to the browser and used to create the page itself, on the user's screen.
The power of ASP lies in two facts: first, the HTML is not created until the user wants to see the
web page, and second, it doesn't care what web browser is being used. ASP isn't the first
technology to offer these features, but it's undoubtedly one of the most powerful and widely used
in industry; and crucially, it's one of the fastest. Active Server Pages is different from many
Microsoft technologies in the following respect: while ASP must be executed on a computer that
supports it, we can view ASP-driven web pages from any computer, and with any modern
browser. This has enabled developers to enhance their web pages with interactive features, and
even to solve common business problems – to such an extent that pages with the .asp suffix are
fast becoming as common as those with the .htm suffix.
ASP is potentially one of the most important innovations to emerge on the Web – for developers
and users of the Internet and intranets alike.
So what can we do with ASP? Well, arguably the most important advantage that ASP brings is its
ability to create pages that are sensitive to factors such as time and place, and the user's identity
and previous choices and actions. In other words, we can use ASP to customize our web pages
to the specific needs of each individual user. It means that the text, images, tables, forms, and
even the layout of the page can be selected automatically at the time the user requests the page
– and to suit that user's requirement.
ASP is relatively simple to learn. All you need is a little experience with HTML and a scripting
language (this book teaches and uses the VBScript scripting language) – and you can start to
build ASP functionality into your existing HTML pages. In short, ASP is a great way to bring the
best features of programming to your web pages.
ASP was officially announced to the world by Microsoft on July 16, 1996, codenamed Denali. A
beta version was released in November 1996, and ASP version 1.0 was shipped on December
12, 1996. It gained much wider recognition when it was bundled with version 3.0 of Microsoft's
Internet Information Server (IIS) web server suite in March 1997; and it has been gaining
steadily in popularity since then.
Microsoft have continued to work on the development of ASP. In 1998, they released new
versions of their web server software, Internet Information Server 4.0 (IIS 4.0) and Personal Web
Server 4.0 (PWS 4.0): both of these supported the new ASP version 2.0. ASP 2.0 offered
considerable enhancements over ASP 1.0, including an enriched model for managing
communications between browser and web server.
With the release of Windows 2000, we have a further updated version Internet Information Server
(version 5.0) and a new version of Active Server Pages (version 3.0). Moreover, IIS 5.0 is more
naturally integrated with the Windows 2000 operating system, resulting in a more streamlined
overall package.
We'll start by understanding what we mean when we talk about a dynamic web page, and how
dynamic web pages are different from static web pages. Then, we'll use that as a springboard into
understanding ASP.
What is a Dynamic Web Page?
If you surf around the Internet today, you'll see that there are lots of static web pages out there.
What do we mean by a static web page? Essentially, it's a page whose content consists of some
HTML that was typed directly into a text editor and saved as an .htm or .html file. Thus, the
author of the page has already completely determined the exact content of the page, in HTML, at
some time before any user visits the page.
Static web pages are often quite easy to spot; sometimes you can pick them out by just looking at
the content of the page. The content (i.e. text, images, hyperlinks, etc) and appearance of a static
web page is always the same – regardless of who visits the page, or when they visit, or how they
arrive at the page, or any other factors.
For example, suppose we create a page called Welcome.htm for our website, by writing some
simple HTML like this:
<HTML>
<HEAD><TITLE>A Welcome Message</TITLE></HEAD>
<BODY>
<H1>Welcome</H1>
Welcome to our humble website. Please feel free to view our
<A HREF="contents.htm">list of contents</A>.
<BR><BR>
If you have any difficulties, you can
<A HREF="mailto:webmaster@wrox.com">send email to the webmaster</A>.
</BODY>
</HTML>
Whenever any client comes to our site to view this page, it will look like this. The content of the
page was determined before the request was made – at the time the webmaster saved the .htm
file to disk.
Static Pages vs Dynamic Pages
OK, so let's think for a moment about how a static, pure-HTML page finds its way onto a client
browser:
1. A web author writes page composed of pure HTML, and saves it within an .htm file
2. Sometime later, a user types a page request into their browser, and the request is
passed from the browser to the web server
3. The web server locates the .htm page
4. The web server sends the HTML stream back across the network to the browser
5. The browser processes the HTML and displays the page
Important We use the term web server to refer to the software that manages
web pages and makes them available to 'client' computers – via a
local network or via the Internet. In the case of the Internet, the
web server and browser are usually on two different machines,
possibly many miles apart.
Static, pure-HTML files like Welcome.htm file make perfectly serviceable web pages. We can
even spruce up the presentation and usability of such pages by adding more HTML to create
frames and tables. However, there's only so much we can achieve by writing pure HTML,
precisely because their content is completely determined before the page is ever requested.
The Limitations of Static Web Pages
For example, suppose we want to enhance our Welcome page – so that it displays the current
time or a special message that is personalized for each user. These are simple ambitions, but
they are impossible to achieve using HTML alone. If you're not convinced, try writing a piece of
HTML for a web page that displays the current time, like this:
As you type in the HTML, you'll soon realize the problem – you know that the user will request the
page sometime, but you don't know what the time will be when they do so! Hard-coding the time
into your HTML will result in a page that always claims that the time is the same (and will almost
always display the wrong time).
In other words, you're trying to write pure HTML for a web page that displays the time – but you
can't be sure of the exact time that the web page should display until the time the page is
requested. It can't be done using HTML alone.
Since we can't create our page by saving our hard-coded HTML into a file before the page is
requested, then what we need is a way to generate the HTML after the page is requested.
In this case, we can compose most of the page using pure HTML. It's just that we can't hard-code
the current time; instead, we can write a special code (which would replace the highlighted line
here) that instructs the web server to generate that bit of HTML during Step 4, at the time the
page is requested.
We'll return to this example later in the chapter, and we'll see how to write the highlighted
instruction using ASP.
Note We could extend the notion of dynamic pages even further, by adding another
step (between Steps 5 and 6 above) – in which we include more instructions
that are carried out by the browser (not by the server). For the purpose of this
chapter, we will not consider this type of 'dynamic page' – but we will return to it
in Chapter 2.
Our HTML-generation instructions can be written in such a way that they use this newly-captured
information to create up-to-the-minute, personalized, interactive web pages, that serve fresh
information every time they are requested.
You'll find that sites that contain ASP code are more dynamic – they're quite often tailored to the
individual user, can reflect the fact that a user has visited the site before, can be customized
easily to view preferred topics, and in general offer the user a more interactive and personalized
experience. You can see this in pages on some of the larger, more commercial sites, such as
those produced by Microsoft, ABC News, Dell, Compaq, Gateway 2000, ESPN SportsZone and
the official NASCAR, NBA, NFL and WNBA sites. Many of these pages are easily identifiable as
Active Server Pages, since they are suffixed by .asp (although some have ASP-driven code
masquerading behind .htm pages). All of these sites have fairly guessable addresses –
http://www.microsoft.com, http://www.abcnews.com, and so on – take a look!
In other words, with ASP we can write a set of instructions that can be used to generate HTML
just after the web page has been requested by a client, and just before it is delivered.
ASP is the perfect tool for any HTML writer to add to their toolkit, because (as we saw in the
previous section, and will demonstrate throughout the book) it gives us the power and flexibility to
generate fresher HTML, and ultimately to produce more spectacular, interactive, personalized,
up-to-date web pages.
How can we describe ASP? It's not a language (in the sense that Pascal and C++ are languages)
– although it does make use of existing scripting languages such as VBScript or JavaScript.
Moreover, it's not really an application (in the sense that FrontPage and Word are applications)
either. Instead, we describe ASP using a rather more ambiguous term, technology. ASP is a
technology for building dynamic and interactive web pages.
The notion of mixing different types of logic (fragments of text, some HTML tags and some ASP
code) within a single block of code is one that can be very useful in ASP – however, it can be one
of the main stumbling blocks for ASP beginners. So, let's take a look at this breakdown with an
example, to show that it's really not as tricky as it sounds.
The following table summarizes these three ingredients, their purpose and their appearance:
ASP Instructions to the Web site host's web Each ASP section
statements web server server software with contained within <% %>
running ASP ASP extensions delimiters
about how to performs the
ASP statements contain
create portions of instructions of the ASP
the structures of more
the page to be code
'traditional' programming
sent out
languages, such as Visual
Basic and Java, as they
have features such as
variables, decision trees,
cyclical repetitions etc.
Let's take a look at a simple ASP page. At the moment, we don't need to be too concerned with
exactly what it does – we'll learn all about that in good time. The main point here is that it's not too
hard to distinguish the different elements of the ASP page. Anything that falls between the <% and
%> markers is ASP script, and will be processed on the web server by the ASP script engine,
after the ASP page is requested and just before it is delivered to the browser. The script code is
shown on a gray background, and the HTML and text are on a white background:
<HTML>
<HEAD>
<TITLE>The Polite Web Server</TITLE>
</HEAD>
<BODY BGCOLOR="wheat">
<H1>Welcome</H1>
<FONT SIZE="3">
<B>This is the Polite Web Server, at <% = Time %> on <% = Date
%></B></FONT><BR>
<BR>
<% If Hour(Now) < 8 Then %>
Do you know what time it is? I was still in bed!
<% Else
Randomize
intChoice = Int(Rnd * 4)
We've highlighted the script code that is executed by the server to make it easier to distinguish
the ASP code from the plain HTML. The web server searches out the <% … %> markers, and
executes the code contained within to generate a pure HTML stream – which it sends back to the
browser. Then, the browser can process the HTML and display the web page:
Thus, the content of the resulting web page depends on the HTML that is generated by the ASP
code. In this particular example, the effect of the script code is to generate HTML for the time and
date that the page is requested, and then to make a decision (based on the situation) on what
text will be sent to the browser as part of the HTML stream. Juxtaposing bits of ASP, HTML and
text in this way is an effective way of getting the result we want.
That's not to say ASP pages are perfect. For example, they increase the workload on the server,
so if your web site becomes popular you may need to invest in more hardware – but this is true of
any server-side technology, and ultimately many web developers decide that the benefits of
server-side functionality outweigh any disadvantages.
What Do I Need to Run ASP?
So far, we've established what ASP and HTML offers us over and above pure HTML, a little about
how it works, and some advantages of using it. But what software do we need in order to work
with ASP?
To answer this we need to consider the role that we'll be playing in this book – namely that of the
web author or webmaster. In this role, we'll be writing web pages, we'll be publishing them on a
web server, and we'll be testing them to see what they look like and whether they work.
In order to write pages, we'll need a text editor or other web development tool. Notepad
works fine for this purpose, but there are plenty of other editors on the market. We'll discuss
some of these options in more detail later in the chapter.
In order to publish the pages, we'll need a web server that supports Active Server
Pages. This book describes using ASP 3.0, which comes with a web server called Internet
Information Server 5.0 (IIS 5.0), which in turn installs as part of the Windows 2000 operating
system. IIS 5.0 supports ASP version 3.0. There are other web servers which also support
various versions of ASP, and again we'll discuss some options shortly – but remember that if
you're not running Windows 2000 and IIS 5.0 then you'll probably find one or two differences
in the way some of the code works.
In order to view and test the pages, we'll need a web browser! As we mentioned before,
ASP is processed on the web server, not on the browser – this means that any browser
should suffice.
Of course, when you're browsing pages on the Internet or your local intranet, the browser and
web server software are generally hosted on two different physical machines – we refer to these
machines as the client and the server. In fact, we even illustrated it this way in the diagrams that
we've seen so far in this chapter. But it's quite possible to host browser, server and text editor all
on the same machine – indeed, it's a technique often used by web developers as they write, test,
rewrite and tweak their ASP pages. In this case, the single machine acts as both web client and
web server.
Note Of course, if you're hosting your web server on one machine, and your browser
on another machine, you'll need a network so that they can talk to one another.
If you're doing it all on one machine, the network won't be necessary.
Moreover (and as we mentioned a moment ago), Microsoft is distributing Windows 2000 with its
own web server software, Internet Information Server 5.0 (IIS 5.0), which supports ASP 3.0. So
if you're planning to use a Windows 2000 machine as your web server, then your web server
software is already available to you. You just need to check that IIS 5.0 has been installed onto
your machine – we'll run through the check and the installation procedure shortly.
Important The ideal set-up for this book is a single machine running the Windows
2000 operating system, with IIS 5.0 and a web browser installed and
running on that machine. At the very least, you'll need a web server that
supports ASP.
If you're using running Windows NT Workstation 4.0, Windows 95, or Windows 98, then you can
use Microsoft's Personal Web Server (PWS) as your web server software. Again, PWS supports
ASP 2.0 and is currently available from the NT 4.0 Option Pack. It's worth noting that while PWS
on Windows 95 and 98 was an individual product, PWS on Windows NT Workstation was in fact
IIS 4.0 under a different name.
We can't guarantee the continued availability of PWS on Microsoft's site in the future. However,
it's worth noting that a number of ASP-related FTP sites – such as
http://www.crackedrabbit.com/aspfirststeps – are also currently making PWS available, so take a
look online for a copy.
Note For his book Beginning Active Server Pages Databases (Wrox, ISBN 1-
861002-72-6), John Kauffman created a comprehensive set of instructions on
the installation and setup of PWS. We've reproduced those notes here in
Appendix E.
When Microsoft released ASP 1.0, it was as an extension that could be installed onto an existing
Microsoft web server. This 'extension' was just a piece of software that was written to run on a
server operating system, and could be installed by the system administrator. But this extension
could only be used in conjunction with Microsoft web servers. Since then, other companies have
worked to provide ASP support for other web servers, and on platforms other than Windows.
Chili!Soft and Halcyon Software are two such companies.
Chili!Soft
At the time of writing, you can download a developer edition of Chili!Soft ASP from
http://www.chilisoft.com – this gives a 30-day trial edition of the product together with details of
how to register for a permanent five-user session license. Once installed, Chili!Soft ASP enables
you to run ASP on web servers from Apache, Lotus, Netscape, O'Reilly and Microsoft, running on
Microsoft, Sun, and IBM systems. Chili!Soft version 3.0 is now available. For more details of this
product, check the Chili!Soft web site.
Halcyon Software
Halcyon Software provides a product called Instant ASP, which runs as a Java servlet on your
web server to provide ASP support. There is a free 'developer version' of Instant ASP available
from Halcyon's web site, at http://www.halcyonsoft.com – the full version retailed at $495 at the
time of writing. The developer version is an almost full-featured edition, with just a few
unsupported functions (as listed in the Release Notes on their web page) and limited by the number
of users that the server can support at any one time.
Because Instant ASP is a Java servlet, it also requires the Java Development Kit (JDK) to be
installed on your web server. The JDK can be downloaded separately and for free, from Sun
Microsystems' web site at http://www.sun.com.
Let's press on with Windows 2000 and IIS 5.0. You might not have to do much in this initial stage,
if you discover that you're already running IIS 5.0. We'll look at how to check that right now.
2. Select the Add/Remove Windows Components icon on the left side of the dialog, to get to
the screen that allows you to install new windows components.
3. Locate the Internet Information Services (IIS) entry in the dialog, and note the checkbox that
appears to its left. Unless you installed Windows 2000 via a custom install and specifically
requested IIS, it's most likely that the checkbox will be unchecked (as shown above).
4. a. If the checkbox is cleared, then place a check in the checkbox and click on Next to load
Internet Information Services 5.0 and Active Server Pages. You might be prompted to place
your Windows 2000 installation disk into your CD-ROM drive. It will take a few minutes to
complete. Then go to Step 5.
4. b. If the checkbox is checked then you won't need to install the IIS 5.0 component – it's
already present on your machine. Go to Step 5.
5. Click on the Details button – this will take you to the dialog shown below. There are a few
options here, for the installation of various optional bits of functionality. For example, if the
World Wide Web Server option is checked then our IIS installation will be able to serve and
manage web pages and applications. If you're planning to use FrontPage 2000 or Visual
InterDev to write your web page code, then you'll need to ensure that the FrontPage 2000
Server Extensions checkbox is checked. The Internet Information Server Snap-In is very
desirable, as you'll see later in the chapter – so ensure that is checked too.
For the purpose of this installation, make sure all the checkboxes in this dialog are
checked; then click on OK to return to the previous dialog.
There's one other component that we'll need to install, for use later in this book – it's the Script
Debugger. If you scroll to the foot of the Windows Components Wizard dialog that we showed
above, you'll find a checkbox for Script Debugger. If it isn't already checked, check it now and click
on Next to complete the installation. Otherwise, if both IIS 5.0 and the script debugger are already
present, then click on Cancel to abort the process.
How It Works
Web Services start up automatically as soon as your installation is complete, and thereafter
whenever you boot up Windows – so you don't need to run any further startup programs, or click
on any short-cuts as you would to start up Word or Excel.
IIS installs most of its bits and pieces on your hard drive, under the \WinNT\system32\
inetsrv directory; however, more interesting to us at the moment is the \InetPub directory
that is also created at this time. This directory contains subdirectories that will provide the home
for the web page files that we create.
If you expand the InetPub directory, you'll find that it contains several subdirectories:
\iissamples\homepage contains some example ASP pages.
\iissamples\sdk contains as set of subdirectories that hold ASP scripts which
demonstrate the various ASP objects and components.
\scripts is an empty directory, which is a useful place to store any ASP scripts you
might create.
\webpub is also empty. This is a 'special' virtual directory, used for publishing files via
the Publish wizard. Note that this directory only exists if you are using Windows 2000
Professional Edition.
\wwwroot is the top of the tree for your web site (or web sites). This should be your
default web directory. It also contains a number of subdirectories, which contain various bits
and pieces of IIS. This directory is generally used to contain subdirectories which hold the
pages that make up our web site – although, in fact, there's no reason why you can't store
your pages elsewhere. We'll be discussing the relationship between physical and virtual
directories later in this chapter.
\ftproot, \mailroot and \nntproot should form the top of the tree for any sites
that use FTP, mail or news services, if installed.
In fact, some versions of IIS 5.0 provide two user interfaces, which come from PWS and the
earlier versions of IIS – the two Microsoft web servers that existed in the time before Windows
2000. The Personal Web Manager was distributed with PWS, and is still supported by IIS 5.0 in
some versions of Windows 2000. The Microsoft Management Console is a generic way of
managing all sorts of services, and is often preferred. Let's take a quick look at both of them.
2. Before we snap in the IIS snap-in, we need to locate it. The IIS 5.0 snap-in is
encapsulated in a file called iis.msc, which should be contained in your \WinNT\
system32\inetsrv directory. Have a look for it now; and if it's not there, open your
Windows Explorer and use the Search facility (at View | Explorer Bar | Search) and make a
note of its location.
3. Now return to the MMC shell, select the Console menu, and choose Open…. You'll be
presented with a dialog that allows you to browse the files on your machine. Use this to
navigate to the iis.msc file that you located a moment ago – then click on Open. This will
open the IIS snap-in within the MMC shell – a site that looks something like this should
greet you:
Having opened the IIS snap-in within the MMC, you can perform all of your web management
tasks from this window. The properties of the web site are accessible via the Default Web Site
node. We'll be using the MMC more a little later in the chapter. Before that, we'll take a look at the
Personal Web Manager.
Note To avoid the inconvenience of stepping through this process each time you
need to administrate IIS, you could choose to create a shortcut. To do this, use
Windows Explorer to navigate to the iis.msc file. Then right-click on iis.msc, and
select Create Shortcut – this creates a new shortcut (called Shortcut to iis.msc) in
the same folder. Finally, drag the new shortcut from the Explorer window onto
the desktop. You can rename the shortcut if you like.
You can get to it by opening the Control Panel, selecting the Administrative Tools icon and selecting
Personal Web Manager. It looks like this.
Which IIS User Interface Should I Use?
Depending on which version of Windows you're using, you might not have a choice. If you're
using an older version of Windows, or one of the 'higher-end' versions of Windows 2000, then
your choice will probably be restricted to one.
What if you do have an option? The MMC-based admin tool for IIS – the IIS snap-in – provides a
richer interface than the PWM, and provides services not only for web pages, but also for FTP
sites (enabling the transfer of files from one site to another), NNTP (newsgroup) services and
video and audio services. The MMC is designed to integrate seamlessly with many of Microsoft's
other products, including the database facilities of SQL Server, the management facilities of Site
Server, and the emailing and messaging abilities of Exchange. For this reason, we'll be using the
MMC-based interface in this book.
In order to test the web server, we'll start up a browser and try to view some web pages that we
know are already placed on the web server. In order to do that, we'll need to type a universal
resource locator (or URL) into the browser's Address box, as we often do when browsing on the
Internet. The URL is an http://... web-page address which indicates which web server to
connect to, and the page we want to view.
What URL do we use in order to browse to our web server? If your web server and web browser
are connected by a local area network, or if you're using a single machine for both web server
and browser, then it should be enough to specify the name of the web server machine in the
URL.
Throughout this book, in the examples that require a web server name, we'll show the expression
my_server_name whenever you need to insert your own web server's name. When you see this,
you'll need to substitute your own server's name for my_server_name, rather than typing this
expression in literally.
If you get this page then it means that your Web services are not switched on. To switch on Web
services, you'll first need to start the IIS admin snap-in that we described earlier in the chapter (to
reprise: select Start | Run, type MMC and hit OK; then select Open from the MMC's Console menu
and locate the iis.msc file from the dialog; alternatively, just use the shortcut that you created
there).
Now, click on the + of the root node in the left pane of the snap-in, to reveal the Default sites.
Then right-click on Default Web Site, and select Start:
If it's still not working then here are a few more suggestions, which are based on particular
aspects of your PC's setup. If you're running on a network and using a proxy server, there's a
possibility that this can prevent your browser from accessing your web server. Most browsers will
give you an opportunity to bypass the proxy server:
If you're using Internet Explorer, you need to go to View | Internet Options (IE4) or Tools |
Internet Options (IE5) and select the Connections tab. In IE5 press the LAN settings button and
select Bypass the proxy server for local addresses. In IE4, this section forms part of the
Connections dialog.
If you're using Netscape Navigator and you are having problems then need to turn off all
proxies and make sure you are accessing the Internet directly. To do this, select Edit |
Preferences; in the resulting dialog select Advanced | Proxies from the Category box on the left.
Then on the right, select the Direct Connection to Internet option, and hit OK. Although you
won't be browsing online to the Internet, it'll allow Netscape Navigator to recognize all
variations of accessing local ASP pages – such as http://127.0.0.1, http://localhost, etc.
You may hit a problem if your machine name is similar to that of some web site out there on the
Internet – for example, if your machine name is jimmyd but there also happens to be a public
web site out there called http://www.jimmyd.com. When you type http://jimmyd into your
browser's address box, expecting to view a page on your local web server, you unexpectedly get
transported to http://www.jimmyd.com instead. If this is happening to you, then you need to
make sure that you're not using a proxy server in your browser settings – again, this can be
disabled using the Internet Options | Connection dialog or the Edit | Preferences dialog.
Lastly, if your web server is running on your home machine with a modem, and you get an error
message informing you that your web page is offline, this could in fact be a misperception on the
part of the web server. This can be corrected by changing the way that your browser looks for
pages. To do this, select View | Internet Options (IE4) or Tools | Internet Options (IE5), choose the
Connections tab and select Never dial a connection.
Of course, you might encounter problems that aren't answered above. In this case, the chances
are that it's related to your own particular system setup. We can't possibly cover all the different
possible configurations here; but if you can't track down the problem, you may find some help at
one of the web sites and newsgroups listed later in this chapter.
Managing Directories on your Web Server
These days, many browsers are sufficiently advanced that you can use them to locate and
examine files and pages that exist on your computer's hard disk. So, for example, you can start
up your browser, type in the physical location of a web page (or other file) such as C:\My
Documents\mywebpage.html, and the browser will display it. However, this isn't real web
publishing at all:
First, web pages are transported using a protocol called HTTP – the HyperText Transfer
Protocol. Note that the http:// at the beginning of URL indicates that the request is being
sent by HTTP. Requesting C:\My Documents\mywebpage.html in your browser doesn't
use HTTP, and this means that the file is not delivered and handled in the way a web page
should be. We'll discuss this in greater detail when we tackle HTTP at the start of Chapter 2.
Second, consider the addressing situation. The string C:\My Documents\
mywebpage.html tells us that the page exists in the \My Documents directory of the C:
drive of the hard disk of the machine on which the browser is running. In a network situation,
with two or more computers, this simply doesn't give enough information about the web
server.
However, when a user browses (via HTTP) to a web page on some web server, the web server
will need to work out where the file for that page is located on the server's hard disk. In fact,
there's an important relationship between the information given in the URL, and the physical
location (within the web server's file system) of the .htm or .asp file that contains the source for
the page.
Virtual Directories
So how does this relationship work? In fact, it can work by creating a second directory structure
on the web server machine, which reflects the structure of your web site. It sounds like it could be
complicated, but it doesn't have to be. In fact, in this book it's going to be very simple.
The first directory structure is what we see when we open Windows Explorer on the web server –
these directories are known as physical directories. For example, the folder C:\My Documents
is a physical directory.
The second directory structure is the one that reflects the structure of the web site. This consists
of a hierarchy of virtual directories. We use the web server to create virtual directories, and to
set the relationship between the virtual directories and the real (physical) directories.
When you try to visualize a virtual directory, it's probably best not to think of it as a directory at all.
Instead, just think of it as a nickname or alias for a physical directory that exists on the web server
machine. The idea is that, when a user browses to a web page that is contained in the physical
directory on the server, they don't use the name of the physical directory to get there: instead,
they use the physical directory's nickname.
To see how this might be useful, consider a website that publishes news about many different
sporting events. In order to organize his web files carefully, the webmaster has built a physical
directory structure on his hard disk, which looks like this:
Now suppose you visit this web site to get the latest news on the Javelin event in the Olympics. If
the URL for this web page were based on the physical directory structure, then the URL for this
page would be something like this:
http://www.oursportsite.com/sportsnews/athletics/field/javelin/
default.asp
That's OK for the webmaster, who understands his directory structure; but it's a fairly
unmemorable web address! So, to make it easier for the user, the webmaster can assign a virtual
directory name or alias to this directory – it acts just like a nickname for the directory. Here, let's
suppose we've assigned the virtual name javelinnews to the c:\inetpub\...\javelin\
directory. Then the URL for the latest Javelin news is:
http://www.oursportsite.com/javelinnews/default.asp
By creating virtual directory names for all the directories (such as baseballnews, 100mnews,
200nnews, etc) it's easy for the user to type in the URL and go directly to the page they want:
http://www.oursportsite.com/baseballnews/default.asp
http://www.oursportsite.com/100mnews/default.asp
http://www.oursportsite.com/200mnews/default.asp
Not only does this save the user from long, unwieldy URLs – it also serves as a good security
measure, because it hides the physical directory structure from all the web site visitors. Moreover,
it allows the webmaster's web site structure to remain independent of the directory structure on
his hard drive – so he can move files on his disk between different physical folders, drives, or
even servers, without having to change the structure of his web pages.
Let's have a crack at setting up our own virtual directories and permissions (please note that
these permissions are set automatically, if you use Visual Interdev or FrontPage to create a new
site).
2. Next, start up the IIS admin tool (using the MMC, as we described earlier). Right-click on
Default Web Site, and from the menu that appears select New | Virtual Directory. This starts
the Virtual Directory Creation Wizard, which handles the creation of virtual directories for
you and the setting up of permissions as well. You'll see the splash screen first, which
looks like this. Click on Next.
5. Make sure that the Read and Run scripts checkboxes are checked, and that the Execute
checkbox is empty. Click on Next, and in the subsequent page click on Finish.
1. The BegASP virtual directory will appear on the tree in the IIS admin window.
How It Works
You've just created a physical directory called BegASPFiles, in which we can store all of the
examples for this book. You've also created a virtual directory called BegASP, which you created
as an alias for the physical BegASPFiles directory. When we create examples later in this
chapter, and throughout the book, we'll place the ASP files in the physical C:\Inetpub\
wwwroot\BegASPFiles directory; and when we use the browser to test the pages, we'll use the
URL http://my_server_name/BegASP/….
Note that the URL uses the alias /BegASP – IIS knows that this stands for the directory path C:\
Inetpub\wwwroot\BegASPFiles. When executing ASP pages, you can reduce the amount of
typing you need to do in the URL, by using virtual directory names in your URL in place of the
physical directory names.
Permissions
As we've just seen, we can assign permissions to a new directory as we create it, by using the
options offered in the Virtual Directory Wizard. Alternatively, we can set permissions at any time,
from the IIS admin tool in the MMC. To do this, right-click on the BegASP virtual directory in the
IIS admin tool, and select Properties. You'll get the following dialog:
It's quite a complicated dialog, and it contains a lot of options – not all of which we wish to go into
now.
Creating an Application
If you look at the above screenshot, you will notice that we have created an application called
BegASP – the same as our virtual directory. If the box next to the Application name label is blank,
you need to hit the Create button, which is on the right of the box. After the application is created,
this button becomes Remove. This will enable us to use Global.asa in our scripts – we will
discuss Global.asa in Chapter 8.
Access Permissions
The four check boxes on the left are of interest to us, as they govern the types of access for the
given directory and dictate the permissions allowed on the files contained within that directory.
Let's have a look at what each of these options means:
Scripts source access This permission enables users to access the source code of a
script. It's only possible to allow this permission if the Read or Write permission has already
been assigned. But we generally don't want our users to be able to view our ASP source
code, so we would usually leave this checkbox unchecked for any directory that contains
ASP scripts. By default, all directories created during setup have Scripts Source Access
permission disabled.
Read This permission enables browsers to read or download files stored in a home
directory or a virtual directory. If the browser requests a file from a directory that doesn't have
the Read permission enabled, then the web server will simply return an error message (such
as the one shown below). Generally, directories containing information that you want to
publish (such as HTML files, for example) should have the Read permission enabled.
Note that when the folder has Read permission turned off, HTML files within the folder cannot be
read; but ASP scripts within the folder can still be run.
Write If the write permission on a virtual directory is enabled, then users will be able to
create or modify files within the directory, and change the properties of these files.
Directory Browsing If you want to allow people to view the contents of the directory (that
is, to see a list of all the files that are contained in that directory), then you can allow this by
checking the Directory Browsing option. Also, if someone tries to browse the contents of a
directory that has Directory Browsing enabled but Read disabled, then they may receive the
following message:
Important For security reasons, we'd recommend disabling this option unless
your users specifically need the Directory browsing option – such as
when FTPing files from your web site.
Execute Permissions
There's a text box near the foot of the Properties dialog, labeled Execute permissions – this specifies
what level of program execution is permitted on pages contained in this directory. There are three
possible values here – None, Scripts only, or Scripts and Executables:
Setting Execute permissions to None means that users can only access static files, such
as image files and HTML files. Any script-based files of other executables contained in this
directory are inaccessible to users.
Setting Execute permissions to Scripts Only means that users can also access any script-
based pages, such as ASP pages. So if the user requests an ASP page that's contained in
this directory, the web server will allow the ASP script to be executed, and for the resulting
HTML to be sent to the browser.
Setting Execute permissions to Scripts and Executables means that users can execute any
type of file type that's contained in the directory. It's generally a good idea to avoid using this
setting, in order to prohibit users from executing potentially damaging applications on your
web server.
For any directory containing ASP files that you're publishing, the appropriate setting for the
Execute permissions is Scripts Only.
11. Click on the Refresh button: the displayed time will change. In effect, the browser is
showing a new and different instance of the same web page.
12. Now on your browser, select View | Source (or View | Page Source, or similar, depending on
which browser you're using) from the browser menu to see the HTML source that was sent
from the web server to the browser. The result is shown below. You can see that there is
no ASP script to be seen – the <% = Time %> ASP script has been processed by the
web server and used to generate pure HTML, which is hard-coded into the HTML source
that's sent to the browser.
Here, you can see the HTML that was sent to the browser when I Refreshed the page at
10.31:00am.
13. As we mentioned before, you can expect this to work in any browser – because the ASP
is processed on the web server. If you have another browser available, try it out.
Easy wasn't it? (If you didn't get it to work first time, then don't rush off to email technical support
just yet – have a little look at the next section, Common Pitfalls and Errors with ASP, first.) Now
let's take a look at the ASP that makes this application tick.
How It Works
Of course, there is only one block of ASP in the whole program. It's enclosed by the <% and %>
tags, on this line:
In Webserverland, the time is exactly <% = Time %>
This line tells the web server to go off and run the VBScript Time function on the web server. The
VBScript Time function returns the current time at the web server. If the web server and browser
are on different machines, then the time returned by the web server might not be the same as the
time kept by the machine you're using to browse. For example, if this page is hosted on a
machine in Los Angeles, then you can expect the page to show the local time in Los Angeles –
even if you're browsing to the page from a machine in Cairo.
The Time function isn't unique to ASP: indeed, it's just a VBScript function, that's being run on the
server.
This example isn't wildly interactive or dynamic, but it illustrates that we can ask the web server to
go off and do something for us, and return the answer within the context of an HTML page. Of
course, by using this technique with things like HTML forms and other tools, we'll be able to build
a more informative, interactive interface with the user.
C:\InetPub\wwwroot\BegASP\punctual.asp
You'll also get this problem if you click on the file in Windows Explorer. If you have Microsoft
FrontPage or Visual InterDev installed, then it will start up and attempt to help you to edit the
code. Otherwise, your browser may display a warning message:
The Problem
That's because you're trying to access the page in a way that doesn't cause the ASP page to be
requested from the web server. Because you're not requesting the page through the web server,
the ASP doesn't get processed – and that's why you don't get the expected results of the ASP.
To call the web page through the web server and have the ASP processed, you need to
reference the web server in the URL. Depending on whether you're browsing to the server across
a local network, or across the Internet, the URL should look something like one of these:
http://chrisu/BegASP/punctual.asp
http://www.distantserver.com/BegASP/punctual.asp
As you'll recall, the permissions are controlled by the properties of the virtual directory that
contains the ASP page. To change these properties, you'll need to start up the IIS admin snap-in
in the MMC, as we described earlier in the chapter. Find the BegASP virtual directory in the left
pane, right-click on it and select Properties. This will bring up the BegASP Properties dialog that we
met earlier in the chapter:
Here, you'll need to check that the value shown in the Execute Permissions box is Scripts only.
If you get this page, then you might suspect one of the following errors:
A simple typing error in the URL, e.g. http://chrisu/BegASP/punctually.asp
Including a directory separator (/) after the file name, e.g.
http://chrisu/BegASP/punctual.asp/
Using the directory path in the URL, rather than using the alias, e.g.
http://chrisu/Inetpub/wwwroot/BegASP/punctual.asp
Saving the page as .html or .htm, rather than as an .asp, e.g.
http://chrisu/BegASP/punctual.htm
Of course, it may be that you've typed in the URL correctly, and you're still experiencing this error.
In this case, the most likely cause is that you have used Notepad to save your file and that (when
you saved the file) it used its default Save As Type setting, which is Text Documents (*.txt). This
automatically appends a .txt suffix to the end of your file name. In this case, you will unwittingly
have finished up with a file called punctual.asp.txt.
To check if that is what happened, go to Windows Explorer, and view the (physical) folder that
contains the file. Go to the Tools menu and select Folder Options…. Now, in the View tab, ensure
that the Hide file extensions for known file types is unchecked, as shown here.
Now click OK and return to view your file in Windows Explorer. You may well see something like
the following.
As you can see, Notepad has been less-than-honest in its dealings with you: when you thought
that you had saved your file as punctual.asp, it had inconveniently saved it as
punctual.asp.txt. Not surprisingly, your web server won't be able to find your file if it's been
renamed accidentally. To correct the filename, right click on the filename in the right pane above,
select Rename from the drop-down menu that appears and remove the .txt at the end.
This could also be caused if you're working on a network and using a proxy server to access the
Internet. In this case, you need to bypass the proxy server or disable it for this page, as we
described in the section Browsing to a Page on your Web Server, earlier in the chapter.
Alternatively, if you're using a modem and you don't need to connect, you can correct this
misperception by changing the way that IE looks for pages. To do this, select the Tools |
Connections option and select Never dial a connection.
http://www.15seconds.com
http://www.activeserverpages.com
http://www.asptoday.com
http://www.asp101.com
http://asptracker.com
There are lots of solutions, discussions and tips on these pages, plus click-throughs to other
related pages. Moreover, you can try the newsgroup:
microsoft.public.inetserver.iis.activeserverpages
So, by now you should have successfully downloaded, set up and installed IIS, created your first
application in ASP, and been able to get it up and running. Let's look at some of the more popular
editors with which you can create and edit ASP scripts.
Here, we'll have a quick look at four of the most common code editors. Three of them (Visual
InterDev, FrontPage and Allaire's Homesite) are available commercially, while the fourth
(Notepad) comes free with Windows.
VI 6.0 is the tool that Microsoft is promoting as their favored ASP editing tool. One simple but very
useful feature of VI 6.0 is that it highlights ASP <% and %> tags in yellow, and the ASP script itself
is highlighted using blue for legal keywords – so they stand out from the HTML. Unfortunately,
this book isn't printed in color, so unless you try it out, you'll have to take our word for it. But
here's a screenshot of a VI 6.0 session in action anyway:
As you can see, there are three tabs in the main window here, which correspond to three possible
views of your web page:
The Design view, on the left of the three, is a WYSIWYG (What You See Is What You
Get) interface. This allows you to put together a web page in much the same way as you
might do when creating a document in Microsoft Word – you can insert pictures, links and
sounds without having to write a single line of HTML.
The middle tab is Source. If you go to this view, you'll see all of the HTML generated by
any work that you've done in the Design view. You can also use the Source view to write and
save your own code.
You can use the Quick View tab, on the right of the three, to preview HTML pages in
advance.
If you're using the Source view to write your own ASP code, you should note that the Design and
the Quick View tabs aren't able to process the ASP. Both are limited to viewing HTML only.
However, if the ASP file in the Source view is contained within what VI calls a project, there's an
ASP-friendly alternative – you can select View | View in Browser to see what your processed ASP
will look like.
In addition, Visual Interdev boasts strong links with SQL Server, which makes it very easy to set
up databases combining ASP and SQL Server. It also provides several useful web-based tools
for doing things like checking links, highlighting the broken ones on your site, and allowing you to
drag-and-drop pages from one location to another.
Visual Interdev does have a couple of drawbacks – it's the most difficult to master of the editors
discussed here, and also the most expensive. But having said that, it's undoubtedly the most
powerful of these editors as it offers many more tools and features to the developer.
Note VI 6.0 has some of the most advanced features currently available; if you're
intending to work in a web-development shop then it's worth learning to use
Visual InterDev. We're not going to cover it in this book – its coverage justifies
an entire book on its own. There's plenty of literature on VI 6.0 – you could try
Beginning Visual InterDev 6 (Wrox, ISBN 1-861002-94-7).
Microsoft FrontPage
FrontPage 2000 comes as part of Microsoft's Office 2000 suite – it's another tool for creating and
designing web pages, but it doesn't offer all the functionality of Visual InterDev. It is ultimately a
weaker but easier application to use, and it costs a lot less than Visual InterDev. If you don't mind
the fact that there are fewer features, then it's a simpler, cheaper alternative for the novice.
Again, it offers three views of the web page. The Normal tab gives a WYSIWYG page creation
view (like the Design view in VI), which allows you to write pages without having to code the
HTML explicitly. The HTML tab allows you to write your code explicitly, and the Preview tab gives
a quick view of what the page should look like in a browser.
This screenshot is taken from FrontPage 2000, but you can also use Front Page 97 or 98 to edit
ASP pages. Note, however, that the further back the release, the less the support there is for
ASP and the more pitfalls you may run into.
Again, the Normal and Preview tabs are unable to process any ASP scripts in your page. In order
to view the results of ASP scripts in FrontPage 2000, you need to create something that
FrontPage calls a web, and place your .asp pages within the web; then you can select File |
View in Browser to see what your processed ASP will look like.
Another quirk of FrontPage is that it likes to 'improve' your HTML and ASP, by rearranging it.
FrontPage 2000 now has a Preserve existing HTML option on the Tools | Page Options | HTML
Source dialog, but older versions will still 'autoedit' your HTML for you. Beware – this window
dressing can change your code and even affect the intended function of the code.
Allaire's Homesite
One of the best non-Microsoft web page editors is Allaire's Homesite. The evaluation copy of
version 4.0.1 is currently available from their web site at http://www.allaire.com. The evaluation
copy allows you run the program 50 times or for 30 days –whichever elapses sooner. It has
special features that allow you to edit and preview ASP scripts on a web server (i.e. actually
physically run the ASPs). In fact, of the four editors that we discuss here, Homesite is the only
one of the four that offers this capability. It also features an easy-to-use interface, which allows
you to keep track of your files and folders at the same time as your file contents:
Homesite, like both Visual Interdev and FrontPage 2000, color-codes your ASP script to make it
easy to identify. In short, Homesite is a very powerful editor, and well worth a look.
There are other editors, such as Sausage Software's HotDog, SoftQuad's HotMetal Pro and
Adobe's PageMill. They all feature varying degrees of ASP support, and are all useful tools with
which to create ASP scripts.
Notepad
Notepad is a time-honored text editor. No matter how much Microsoft promotes Visual InterDev,
there will always be people who will use Notepad as their editor of choice. The fact that it's been
free with every incarnation of Windows certainly helps sustain its popularity.
Of course, it doesn't highlight the ASP in any way, but also it doesn't generate any extra code. It
doesn't feature many additional functions; but it's because it's so simple that it's still a very
popular choice. In Windows 2000, Notepad offers a GoTo feature (under the Edit menu), which
allows you to move around your documents using line numbers.
Note It doesn't really matter which editor you use in this book - it won't affect how
you run the examples. We'll avoid any attempt to provide a tutorial on how to
use any of these editorial tools – since this is really beyond the scope of the
book.
By contrast, you may also have noticed that your browser software doesn't enable the browser to
view the source ASP code that generated this HTML. Instead, if you want to view the ASP code
then you must view it on the server, by using a web page editor such as Visual InterDev or
Notepad on the server.
If you're using different machines for your web server and your client, then this distinction will be
fairly easy to grasp. But if you're using the same machine to perform both roles (which is a
reasonable thing to do, especially in a learning or development scenario) then you may need to
consider this distinction a little more carefully.
The fact that the browser doesn't allow the end-user to view the ASP source has a rather
important and valuable consequence. Consider the following scenarios, as a couple of examples:
You're writing an ASP page that uses a database query to get some data (which you
want to display in the page). As we'll see in Chapter 12, you will need to code in connection
details in order to access the database – and these connection details may contain sensitive
information such as a username and password
You're writing an ASP page that uses an algorithm to calculate an insurance quote.
However, you don't want customers to know how the calculation works, so you need to keep
the algorithm secret
If you code these things using ASP, then it means that your code isn't available for end-users to
view through their browser. In other words, writing your pages with ASP is much more secure
than writing them in pure HTML. Coding in ASP enables us to ensure that things like sensitive
database queries and proprietary formulas are kept away from prying eyes.
Summary
In this chapter we've learned about the difference between static and dynamic web pages, and
we've talked about some of the different ways in which you can add dynamic behavior to your
web pages. This book focuses on ASP as a technology for writing dynamic web pages, so we
have looked at some of the main advantages that ASP-generated HTML offers over pure hard-
coded HTML, and why you might want to use ASP to enhance your web pages. There are other
technologies on the Web that also rely on server-side processing, but we haven't considered
them here.
We have looked at the things you'll need to run ASP, and where you can find them. We've
covered how to set up IIS 5.0, and how to set up a directory in which to place the examples that
we'll meet in the remainder of this book.
We created our first ASP page, and viewed it; we looked at some of the common pitfalls that you
might encounter when checking that your ASP example is correctly set up. In order to provide a
general overview of why you'd want use ASP, we've avoided detailed explanation of ASP code,
and how to write the ASP scripts within your .asp files. We'll move onto that in forthcoming
chapters.
Here's a reminder of some things we learned in this chapter, which are worth remembering at all
times:
You can use any text editor to create an ASP page
ASP code is processed on the web server, and then the subsequent HTML code it
creates is sent back to the browser
You can use any modern browser to view an ASP page
In the next chapter we'll take a closer look at the differences between running your programs on
the server and running them on the client; and we'll investigate why we might choose one of
these in preference to the other.
Chapter 2: Server-Side Scripting and Client-Side
Scripting
Overview
In the first chapter we introduced Active Server Pages, and we demonstrated how you'd go about
setting up your web server and getting Active Server Pages working. We learned that ASP web
pages are a mixture of plain text, HTML code and ASP script. We also learned that ASP pages
are stored as .asp files on the server – when a user requests an ASP page, the ASP script
contained in the .asp file is processed on the server before the resulting HTML is sent to the
browser.
What we haven't done is discuss the workings of ASP in any detail, or how web servers interpret
ASP. In this chapter, we aim to reinforce your understanding of the workings of ASP, by
describing three different models. First, we'll look at how information is transmitted between the
browser and web server; second, we'll examine how the web server handles web page requests;
and third, we'll see how the ASP script engine on your web server actually handles lines of ASP
script code.
We'll also look at the two ways at which script can be handled, either on the web server or the
browser; and we will explain why ASP script can only be handled on the server. Once we've
looked at how ASP works, we'll look in greater depth at ASP itself and break it up into its
individual objects. So we'll be explaining the following:
How do web servers work? What do they do?
What is a web application?
What is a request? What is a response? And how do they relate to the roles of the
browser and web server?
What's the difference between server-side scripting and client-side scripting?
What is the ASP object model?
What other methods, apart from ASP, can be used to generate dynamic web pages?
This is the second and last of our foundation chapters. We'll be writing some ASP pages in this
chapter, but they'll be mainly for illustrative purposes – we'll avoid in-depth discussions of syntax
and programming technique until we've laid down the concepts of ASP programming. We'll begin
to work on ASP syntax and techniques in Chapter 3.
How the Web Server Works
In the first chapter we introduced the notion of a web server – a piece of software running on a
computer that distributes web pages to users on demand, and provides an area in which to store
and organize the pages of a web site. The machine that runs the web server software could be a
remote machine sitting at the other side of your network, or even on the other side of the world, or
it could be your very own home machine. We also introduced the idea that the user's browser
was the client in this relationship, and we saw how ASP fits into this 'client–server' relationship.
These days, the term client–server is probably overused; but in fact, when used to describe the
workings of the web, it's almost perfect. In a nutshell, the client–server relationship describes
the distribution of tasks between a server (which stores, processes and distributes data, like an
ATM or cashpoint machine) and the clients that access the server (like customers queuing to get
their money out), in order to achieve universal access for the network on which they are
connected.
The client–server scenario is also commonly known as a two-tier system. More generally,
application architecture has talked in terms of n-tier systems, where n refers to the number of
layers in the system. In the client–server scenario, there are two layers. In Chapter 12, we'll
introduce a third layer – the database layer – and we'll start to think in terms of three-tier
examples. But for now, let us expand on the two-tier or client–server system, as it relates to web
pages.
How the Web Server and Browser Communicate
It probably won't surprise you to learn that, when we discussed the communication between web
server and browser in Chapter 1, we gave an over-simplified picture of what really happens. In
particular, we side-stepped any explanation of the physical processes involved in the transfer of
information across the Internet. In this section, we return to discuss that topic in more depth. We
won't digress into a full-scale history of the Internet and the World Wide Web – it's covered in
many other places. Instead, we'll look at the physical workings of the Internet and intranet
networks.
While a subway system is built on a basis of steel (and other materials), the Internet uses a suite
of networking protocols (known as TCP/IP) to transfer information around the Internet. A
networking protocol is simply a method of describing information packets so they can be sent
down your telephone-, cable-, or T1-line from node to node, until it reaches its intended
destination.
One advantage of the TCP/IP protocol is that it can reroute information very quickly if a particular
node or route is broken or is just plain slow. The perfectly-designed railway system would work in
much the same way – taking passengers efficiently by a different route whenever one of the
stations or tracks was closed down for repair.
When the user tells the browser to go fetch a web page, the browser parcels up this instruction
using a protocol called the Transmission Control Protocol (or TCP). TCP is a transport
protocol, which provides a reliable transmission format for the instruction. It ensures that the
entire message is correctly packaged up for transmission (and also that it is correctly unpacked
and put back together after it reaches its destination).
Before the parcels of data are sent out across the network, they need to be addressed. So a
second protocol called Hypertext Transfer Protocol (or HTTP) puts an address label on it.
HTTP is the protocol used by the World Wide Web in the transfer of information from one
machine to another – when you see a URL prefixed with http://, you know that the internet
protocol being used is HTTP.
Note Internet protocols (such as HTTP and FTP) control addressing and delivery,
while transport protocols (such as TCP) ensure that each message is broken
down, transported and reassembled correctly.
So if the Internet is like a railway system, then a web page request is like a
non-stop train journey from A to B. Here, TCP is like the seating system that
breaks down a group of passengers and freight into different sections of the
train; while HTTP or FTP is like the intended destination instruction that is given
to the train driver before the train departs.
The message passed from the browser to the web server is known as an HTTP request. When
the web server receives this request, it checks its stores to find the appropriate page. If the web
server finds the page, it parcels up the HTML contained within (using TCP), addresses these
parcels to the browser (using HTTP), and sends them back across the network. If the web server
cannot find the requested page, it issues a page containing an error message (in this case, the
dreaded Error 404: Page Not Found) – and it parcels up and dispatches that page to the browser.
The message sent from the web server to the browser is known as the HTTP response.
Here's an illustration of the process as we understand it so far.
A lot of the information that is passed within the HTTP message is generated automatically, and
the user doesn't have to deal with it directly, so you don't need to worry about transmitting such
information yourself. While you don't have to worry about creating this information yourself, you
should be aware that this extra information is being passed between machines as part of the
HTTP request and HTTP response – because the ASP that we write can allow us to have a direct
effect on the exact content of this information.
Every HTTP message assumes the same format (whether it's a client request or a server
response). We can break this format down into three sections: the request/response line, the
HTTP header and the HTTP body. The content of these three sections is dependent on whether
the message is an HTTP request or HTTP response – so we'll take these two cases separately.
Let's just pause and illustrate our understanding of the process now:
We can see that the HTTP request and HTTP response have broadly similar structures and that
there is information that is common to both, which is sent as part of the HTTP header. There are
other pieces of information that can only be known to either the browser or the server, and that
are also sent as part of either the request or response. It makes sense to examine their
constituent parts in greater detail.
The method is used to tell the server the amount of information the browser requires, and how
much information is being sent. Here are three of the most common methods that might appear in
this field:
Method Description
GET This is a request for information residing at a particular URL. The majority of
HTTP requests made on the Internet are GET requests. The information
required by the request can be anything from an HTML or ASP page to the
output of a VBScript, JavaScript or PerlScript program or some other
executable. You can send some limited data to the browser, in the form of an
attachment to the URL.
HEAD This is the same as the GET method except that it indicates a request for the
HTTP header only and no data.
POST This request indicates that data will be sent to the server as part of the HTTP
body. This data is then transferred to a data-handling program on the web
server. For example, we'll use this setting in Chapter 7 to pass information,
which will then be used on the server as part of the ASP-handling process.
There are a number of other methods supported by HTTP – including PUT, DELETE, TRACE,
CONNECT, OPTIONS. As a rule, you'll find that these are less common; they are beyond the scope
of this discussion. If you want to know more about these, take a look at RFC 2068, which you'll
find at http://www.cis.ohio-state.edu/htbin/rfc/rfc2068.html.
As you can see, the header is composed of a number of lines; each line contains the description
of a piece of header information, and then its value.
There are a lot of headers, and most of them are optional, so HTTP has to indicate when it has
finished transmitting the header information. To do this, a blank line is used. A list of HTTP
headers can be found in Appendix I.
HTTP/1.0 200 OK
This example returns the HTTP status code 200, which represents the message 'OK'. This
denotes the success of the request, and that the response contains the required page or data
from the server. You may recall that we mentioned the status code 404 a few pages ago – if the
response line contains a 404 then the web server failed to find the requested page. Error code
values are three-digit numbers, where the first digit indicates the class of the response. There are
five classes of response:
100-199 These codes are informational – they indicate that the request is
currently being processed
200-299 These codes denote success – that the web server received and
carried out the request successfully
300-399 These codes indicate that the request hasn't been performed, because
the information required has now been moved
400-499 These codes denote a client error – that the request was either
incomplete, incorrect or impossible
500-599 These codes denote a server error – that the request appeared to be
valid, but that the server failed to carry it out
Once again, the header consists of a number of lines, and uses a blank line to indicate that the
header information is complete. Here's a sample of what a header might look like:
HTTP/1.1 200 OK the status line
Date: Mon, 1st Nov 1999, 16:12:23 GMT the general header
Server: Microsoft-IIS/4.0 the response header
Last-modified: Fri, 29th Oct 1999, 12:08:03 GMT the entity header
The first line we've already discussed, the second is self-explanatory. The third line indicates the
type of software the web server is running, and as we are requesting a file somewhere on the
web server, the last bit of information refers to the last time the page we are requesting was
modified.
Note The header can contain much more information than this, or different
information, depending on what exactly is requested. If you want to know more
about the different types of information contained in the three parts of the
header, you'll find them listed in RFC 2068 (Sections 4.5, 7.1 and 7.2).
In between, as we saw in Chapter 1, the web server needs to locate the page that was requested
(Step 3); and, if it's an ASP page, then the web server will need to process the ASP, in order to
generate the HTML that is returned to the browser (Step 4).
Well, we've studied many of the necessary aspects surrounding ASP, but our understanding of
the ASP processing itself is still rather sketchy. Up to now, for simplification, we've discussed it
rather as though the ASP script engine were like a sausage machine – the web server feeds in
the raw ASP code 'meat' at one end, and out of the other end comes a neatly packaged product.
HTML sausage – um, I mean pure-HTML page. It's time to demystify this process.
The first attempts to add depth to the capability provided by the HTML language involved creating
a mixture of pure HTML code and programming commands. As we saw in Chapter 1, it's this that
allows us to write instructions about how the page is to be created – effectively, we can write
code (in the form of the programming commands), that will describe how the HTML should be put
together at the time the page is requested.
In order to distinguish these nuggets of programming capability embedded within our HTML, we
refer to them as scripts. However, HTML isn't a programming language, and so it's necessary to
write these commands in other languages. We use the term scripting language to describe the
languages in which these scripts are written.
HTML allows us to include scripts at (almost) any point in our HTML code – it does this by
providing us with legal ways of inserting scripts, which we'll come to shortly. Subsequently, when
the page has been requested and its HTML is being generated, each script within the page is
sent to a script host (an application that communicates with different scripting engines). The
script host in turn instructs the appropriate script engine to interpret the script.
Scripting languages form the basis of ASP. We use scripts to write the instructions that allow
pages to be created dynamically. We also use scripts to access the various bits and pieces that
ASP provides; these bits and pieces are known as objects, and we will look at them shortly.
Which Scripting Language to Use
There are quite a number of scripting languages. Arguably the two most popular at the moment
(and certainly the two of interest to us in this book) are VBScript and JavaScript. JavaScript was
the first client-side scripting language (we'll talk about the difference between client-side and
server-side scripting shortly). The VBScript scripting language was developed by Microsoft – it's
based on their Visual Basic programming language.
Each script that we write must be interpreted at the time it is requested. For this purpose, each
scripting language has a script interpreter – the script engine. So a script or program written in
VBScript must be sent to a VBScript script engine, and a script or program written in JavaScript
must be sent to a JavaScript script engine. Microsoft's IIS 5.0 web server comes with script
engines for both VBScript and JScript. (JScript is Microsoft's implementation of JavaScript.)
Note Note that JavaScript shouldn't be confused with Java. In fact, JavaScript was
originally to have been named LiveScript; at that time, Netscape had intended
to market the language completely separately from Java. However, following
the popularity of Java, Netscape teamed up with Sun during the development
of LiveScript, changed its name to JavaScript, and borrowed several structures
from Java's syntax. Hence, the JavaScript scripting language shares some
superficial resemblances with its namesake.
The only difference comes when, in the act of preparing a page to be sent to the browser, the
server comes across a script. The first thing that the server must do is identify which machine is
responsible for processing the script. This is an important point because, when we write a script,
we can choose whether it is to be processed by the server or by the browser. Let's make this
difference more precise:
A script that is interpreted by the web server is called a server-side script. A server-side
script is an instruction set that is processed by the server, and which generates HTML. The
resulting HTML is sent as part of the HTTP response to the browser.
A script that is interpreted by the browser is called a client-side script. A client-side
script is also an instruction set, but it is not processed by the web server. Instead, it is sent to
the browser (as part of the HTTP response) and is processed by the browser; the result is
then displayed by the browser on the monitor.
So we can complete the picture that we began in Chapter 1, by adding one more step to take
care of the client-side scripts:
As we'll see, server-side scripting and client-side scripting each offer their own advantages. We'll
have a look at client-side scripts later in the chapter, in order to put ASP (and other server-side
scripting techniques) into context. First, let's take a look at some more ASP and other server-side
scripts.
Identifying a Script
We've just been talking about how the web server identifies on which machine a script must be
processed. But how do we identify a script, when it is embedded in a small or large amount of
pure HTML? In fact, we already partially answered this question in Chapter 1 – because ASP
(which is destined to be processed on the web server) will be enclosed in special <% … %> tags,
like this:
In Webserverland, the time is exactly <%= Time %>
Here, everything contained within the <% and the %> is assumed to be ASP, and is sent to the
ASP script host for processing.
But there are other kinds of scripts, which are not ASP code, but which still need to be
distinguished from the HTML and text in which they are embedded. For this reason, HTML
provides a special tag – the <SCRIPT> tag:
<SCRIPT LANGUAGE=VBSCRIPT RUNAT=SERVER>
Response.Write Time
</SCRIPT>
Anything that lies between an opening <SCRIPT> tag and a closing </SCRIPT> tag is dispatched
for processing to the appropriate script engine, according to the instructions given by the SCRIPT
tag's attributes. We'll use this to identify both client- and server-side scripts, as you'll see in the
examples that follow.
Server-Side Scripting
As you've probably gathered by now, ASP is server-side scripting. However, it's not true to say
that all server-side scripting is ASP – as we'll see in this section.
Writing Server-Side Scripts
If we're going to place any kind of server-side script within our web page source files, then we
need to label the scripts – so that the server can identify them as server-side scripts and hence
arrange for them to be interpreted correctly. There are two ways to label server-side scripts:
Use the <% … %> server script delimiters, which denote ASP code
Use the HTML <SCRIPT> tag, specifying the RUNAT=SERVER attribute within the tag. If a
tag like this is found within an .asp file, then it is treated as ASP. If such a tag is found within
an .htm file, then it is treated as a non-ASP client-side script.
We must highlight an important difference here: namely, that the choice of .htm or .asp for the
suffix of your web page file is not trivial – it really does have a bearing on how your code is
processed. If you have any ASP at all, you can label it using either of the techniques used above.
However, in order to ensure that it's processed as ASP, then it must be included as part of an
.asp file.
Within an .htm file, it's only possible to use the <SCRIPT> … </SCRIPT> tags – script contained
within these tags will be interpreted as non-ASP scripts. If you try to include any ASP script within
these tags, or if you write <% … %> tags into an .htm file, then the script will not be executed and
your web page won't look the way you intended.
In this first snippet, the <SCRIPT> tag specifies the scripting language that's been used for this
script, and also indicates the target script host – the web server. The default (when using the
<SCRIPT> tag) is for the script to be executed on the browser, so if you're writing a server-side
script then you must specify this. By specifying the attribute of RUNAT as SERVER, we're making
sure that it's processed on the server.
Note Bear in mind that this will be treated as ASP if and only if it is contained within
an .asp file. That's especially useful later, when we come to write server-side
scripts which use the ASP objects. We'll focus in-depth on the ASP objects
from Chapter 7.
The processes involved are similar to those we explained for the first sample. Notice that the
script tag identifies the scripting language in which the enclosed scripts are written. You can use
JScript and VBScript within the same page if you like, as long as each piece of script is contained
within its own tags.
A <SCRIPT> section can be placed almost anywhere in the page. You'll often find scripts at the
end of the HTML document, because that makes the code easier to read.
If you remembered that the <SCRIPT> tag allowed us to specify the scripting language, you might
be wondering how you can specify the LANGUAGE attribute within the <% and %> tags. Well, it's
done using a special notation, which applies to all of the script on a page:
<% @LANGUAGE = VBSCRIPT %>
However, ASP takes VBScript to be the default scripting language, so you'll rarely see this line. In
fact, because this book chooses VBScript rather than other scripting languages, we won't need to
specify the @LANGUAGE line in our ASP code.
An Example
Now we've looked at all the possible permutations and combinations for inserting scripts, it's time
to try out an example.
As you can see it works – sort of. The page contains all the right content, but not necessarily in
the right order! The date comes after the period! There's a reason for this, which we'll explain in a
moment.
1. Now create a new file, called DateConf2.asp. Place the following code into this new file
(note that this code is similar to the code in DateConf1.asp, except for the lines that
have been highlighted):
2. <HTML>
3. <HEAD>
4. <TITLE>Writing the Current Date to the Page with ASP
Script</TITLE>
5. </HEAD>
6.
7. <BODY BGCOLOR=WHITE>
8. <H2>Date Confirmation</H2>
9. <P>Today's date is
10. <%Response.Write Date %>
11. , and this is the second example in Chapter 2.
12. </BODY>
</HTML>
13. Save DateConf2.asp and view this example in your browser by typing in the URL
http://my_server_name/BegASP/DateConf2.asp. You should get a similar result, however
this time it'll be correctly formatted (subject to the odd floating space):
The first thing to notice is that both of these examples use the .asp suffix – so that the ASP
script engine processes the server-side code.
In the body of the web page, we have a combination of some pure HTML, some plain text, and a
little server-side script. In the first case, we specified the following script (the highlighted lines)
that we wish to be processed on the server before the page is sent to the browser:
<P>Today's date is
<SCRIPT LANGUAGE=VBSCRIPT RUNAT=SERVER>
Response.Write Date
</SCRIPT>
, and this is the first example in Chapter 2.
As we explained earlier, the line Response.Write Date causes the date to be calculated and
written to the HTML stream. But why did the date appear at the end of the page, rather than when
it should have appeared after "Today's date is"?
The reason is this. When the web server finds a <SCRIPT RUNAT=SERVER> tag, it arranges for
the script to be processed but it appends the resulting HTML at the end of the HTML stream. In
other words, it takes no notice of the position of the <SCRIPT> tag relative to other elements of
the page.
By contrast, the 'in-line' script command works perfectly when we use the ASP tags to denote it:
<P>Today's date is
<%Response.Write Date %>
, and this is the second example in Chapter 2.
There's an important lesson to be learned here. Namely, that all script tags do not have the same
prevalence – some script tags are processed before others, depending on how we've identified
them in the source.
We'll return to the issue of the order of execution later in this chapter.
When it receives (by HTTP) a request for a web page, the web server first examines the file
extension of the requested page. If the file extension is .asp, then the web server arranges for
the ASP to be handled by the ASP script host. The ASP script host is present on the web server
machine in the form of the file asp.dll, which is run by the web server itself. In fact the
asp.dll can only be located on the web server machine.
Note You can find the file asp.dll on the hard drive of your web server machine –
try looking in the directory C:\WinNT\System32\InetSrv or similar. If you can't see
it, make sure you've turned the Show Hidden Files option on in Windows
Explorer. Be careful that you don't alter or delete the file!
The asp.dll file ensures that all the ASP code is interpreted. In fact, asp.dll is an Internet
Services Application Programming Interface (ISAPI) extension to IIS, and is compiled as a
Windows dynamic link library (DLL). Though this sounds daunting, it simply means that you can
access your web server's functions directly, via the asp.dll, just by writing ASP code into your
pages.
Now, asp.dll is a script host, so it farms out any scripts it finds to the appropriate script
engines. It sends VBScript scripts to the VBScript engine, and it sends JScript scripts to the
JScript engine. It's the responsibility of each script engine to interpret these scripts, and return a
string of HTML to the script host. Then, the script host pulls all this together and returns it to the
IIS web server, which in turn dispatches it to the browser – contained within the HTTP response.
That's what happens if the user requested an .asp page. It's worth noting again that, if the file
extension of the page is not .asp, then the web server will not use the ASP script host. Instead,
the web server itself acts as the script host. So in this case, the web server takes responsibility for
finding any server-side scripts, and sending them to the appropriate script engines (or to the
browser) for interpretation.
Caching
Just to make your life a little bit harder, a process known as caching can further confuse the
results that you get back from the web server. You may well be familiar with the process of
caching on the browser. A cache is temporary storage area on your hard drive that is used by the
browser to store web pages and graphics. This means that, if you browse a page that you've
browsed before, the browser has the option of reading the pages from the local cache instead of
connecting across the Internet to the web server.
Web servers also have caches and to save time when returning ASPs, they can cache the HTML
results of previously-processed ASP scripts for immediate return. This can sometimes result in
you receiving the output of a previously-processed ASP, even though you've updated it since. For
example, if you amend a page and then view it in your browser, it can sometimes show old
results. However, if you then try it again (perhaps by refreshing the browser) it will eventually
deliver the new set of results. We touch upon caching again in Chapter 7, as one of the ASP
objects makes use of it, but this is just a hint to make sure that when you run the examples you're
seeing the same as our screenshots indicate.
Now, to put the notion of server-side scripting into context, let's look at what we mean by client-
side scripting.
Client-Side Scripts
Client-side scripting is not directly related to ASP at all – it involves writing scripts that will be
processed by the browser. When a web page source contains a client-side script, it does not
attempt to process the script; instead, it simply downloads the script to the browser as part of the
HTTP response, and assumes that the browser will know how to deal with it.
When the browser receives the HTTP response, it needs to process the HTML contained within,
which describes how it is to display the page. The browser must also take care of the client-side
scripts that were downloaded as part of the page.
However, when weighing up the choice between client-side and server-side scripting, you must
also consider the disadvantages. The main disadvantage of client-side scripting is that we can't
depend on the browser having the functionality to support the scripts we write. If you have two
different client machines that host two different browsers, and you view a page that contains a
client-side script on each, then you can reasonably expect the results to be quite different.
In other words, client-side scripting is browser specific – because some browsers do not have
the capability to interpret certain scripting languages. For example:
Recent versions of Internet Explorer come with script engines for both VBScript and for
JScript – although older versions of the browser come by default with older versions of the
scripting engines.
Netscape Navigator browsers come with a JavaScript script engine only - there is no
support for VBScript. So, at best, any client-side VBScript in your page does not look quite
as intended – and at worst, it'll cause an error message.
Note It is possible to allow Netscape browsers to view the results of client-side
VBScript – but it requires a manually-installed third party add-in on the
client machine.
You'll find that JScript (or JavaScript) tends to be the language of choice
on the web, as far as client-side coding is concerned. This has been
further reinforced by the adoption of JavaScript as a standard, maintained
by ECMA (European Computer Manufacturer's Standards) and known as
ECMAScript – which sets a bottom line that both JScript and JavaScript
can adhere to.
Another potential disadvantage of client-side scripting is that the code in your client-side scripts is
completely visible to the user. Remember how we used the View | Source option in Internet
Explorer, or the View | Page Source option on Netscape Navigator, to view the HTML source for the
web page? Well, it also displays the code for any client-side scripts that were downloaded from
the web server. So, if you want to keep your client-side script code a secret then you'll have to
use complex encryption techniques – otherwise client-side scripting is not an option!
So the browser, like the web server before it, needs to be able to look at the source and make a
distinction between the pure HTML and the client script code. Once again, it is the HTML
<SCRIPT> tags that allow this. If the browser detects a pair of <SCRIPT> and </SCRIPT> tags in
the source, it assumes that anything that lies in between is script.
In order to illustrate this, we need a few samples. The following few snippets are fragments of
HTML source files. Here's the first sample:
...
<SCRIPT LANGUAGE=VBSCRIPT>
... VBScript code goes here
</SCRIPT>
...
This code looks much like the samples we saw when we were discussing server-side scripts.
There's one significant difference – we haven't specified the RUNAT attribute. Unless we specify
otherwise, any scripts contained within the HTML <SCRIPT> … </SCRIPT> tags are assumed to
be client-side scripts; so in fact, this code fragment is equivalent to the following:
...
<SCRIPT LANGUAGE=VBSCRIPT RUNAT=CLIENT>
... VBScript code goes here
</SCRIPT>
...
Note that if you were to include code like this in a page, it must be saved as a .htm or .html
page, and must not have the suffix .asp (because the RUNAT=CLIENT expression will cause an
error). To run a script on the client side, you can completely omit the RUNAT attribute. For
example, if we wanted to write a client-side script using JavaScript, we could use the following:
...
<SCRIPT LANGUAGE=JAVASCRIPT>
... JavaScript code goes here
</SCRIPT>
...
Again, you can write a page that contains both JavaScript scripts and VBScript scripts – you need
to use a new <SCRIPT> tag for each new script. And again, each <SCRIPT> section can be
placed almost anywhere in the page. In client-side scripting, some authors prefer to place most of
their scripts at the end of the HTML document, so that the rest of the page can be loaded and
rendered by the browser before the interpreter loads and runs the code. In this way, the whole of
the page can be loaded first – instead of having to wait while alternate chunks of HTML and script
are processed. Of course, if we were using 'in-line' scripts to write something into a particular
position in the page, then we'd need to place the <SCRIPT> section at the appropriate position in
relation to the surrounding HTML:
...
The local time in Browserland is exactly
<SCRIPT LANGUAGE=VBSCRIPT> Document.Write Time</SCRIPT>
<P>
...
In this snippet, the browser must execute the Time function (which is a VBScript function) and
pass the result to the Write method of something called the Document object. The Write
method writes the information into the page at the point where it's called, so the result is
something like this (don't worry about typing this example in – we'll give you a full working one
very shortly):
Note Don't worry – you're not expected to know about methods and objects just yet!
In particular, the Document object is not of great importance to us in this book
– all you need to know is that it is part of the browser (it's not an ASP object)
and that it allows client-side scripts to write information on the web page.
Before we move on, you might like to compare the code above with the equivalent line in the
Punctual Web Server example, from Chapter 1, in which we used the ASP <% … %> tags to create
a server-side script, and displayed the time as calculated by the web server:
In Webserverland, the time is exactly <% = Time %>
Comment Tags
As we've mentioned, successful execution of a snippet of script is dependent on whether your
browser supports the requisite scripting language. What happens if you're using an older version
of Navigator or Internet Explorer to browse a web page that contains script? The script will simply
be displayed as text on the web page, which is all very messy and not at all what you'd want to
happen.
So, the traditional way to prevent the code from being displayed as part of the page is to enclose
the contents of the <SCRIPT> section within a pair of comment tags. If the user's browser is not
script enabled, then it will ignore any code contained within comment tags; while browsers that do
support scripting will still be able to interpret and execute the script. (If, for example, we have a
browser that only supports JavaScript, then the comments will allow any VBScript to be ignored.)
Here's how we'd add comment tags to the above code fragment:
...
The local time in Browserland is exactly
<SCRIPT LANGUAGE=VBSCRIPT>
<!-- hide from older browsers
Document.Write Time
-->
</SCRIPT>
...
In order to 'comment out' a JavaScript script, the comment tags used are slightly different (and
you should be careful not to confuse them!). Here's an example:
The local time in Browserland is exactly
<SCRIPT LANGUAGE=JAVASCRIPT>
<!--
d = new Date();
document.write(d);
//-->
</SCRIPT>
Note Notice the positioning of the comment tags <!- - … - -> or <!- - … //- ->,
and in particular, that we've placed the comment tags and the script code on
different lines. Placing the tags on the same line as the script code can result in
some browsers being unable to interpret the enclosed script. If the opening and
closing tags both appear on the same line then the whole of that line acts as a
comment; if the tags appear on two separate lines, the lines containing the
opening and closing tags, and the lines contained within, are all considered as
comment lines.
Of course, many browsers will be able to deal with script like this – indeed support for a scripting
language is pretty much a prerequisite in many of the latest versions. But if you are writing client-
side scripts, consider safety first – it doesn't hurt to use comments to 'hide' client-side scripting
from any older browsers that might attempt to load your page.
The HTML output shows a single sentence, containing the current date. The date is generated by
the VBScript code, which is executed on the client, and is formatted in the default date format
used by the client computer.
How It Works
The first thing to highlight is that we've saved this file with the suffix .htm. We could have saved it
as an .asp file –and if we had, then the ASP script engine (on the web server, remember) would
be called into action at the time the page is requested. However, there's no ASP code in this page
so the ASP script engine would have been redundant.
In older versions of the IIS web server, just adding an .asp extension would actually have been
enough to impede the performance of the page, because the web server would automatically
send the page for processing. However, IIS 5.0 is intelligent enough to check your page for ASP
code before sending it to the ASP script host; and if there's no ASP code then it won't send it for
processing. There is still a small performance price to be paid for getting the page checked by the
server for ASP code first anyway. But the point is this: when there's no ASP code in the page, an
.htm or .html extension is normally sufficient.
In this case, the page consists of only text, some HTML and a client-side script – all of which are
interpreted by the browser. When the browser comes across a <SCRIPT> tag, it knows that it
needs to send the code to the appropriate script engine. In this example, we've specified the
LANGUAGE attribute in the <SCRIPT> tag, for clarity:
<SCRIPT LANGUAGE=VBSCRIPT>
...
</SCRIPT>
This attribute indicates that the script contained within is written in VBScript and must be
interpreted by a VBScript script engine.
This causes the browser's VBScript script engine to execute the built-in VBScript Date function,
which returns a value containing the current date; and tells an entity known as the Document
object to use its Write method to display this date value as part of the page.
Note Incidentally, the mysterious Document object is (quite literally) a representation
of the HTML document or web page that is being currently displayed in the
browser. However, as we said before, the Document object isn't part of ASP:
it's something that is created by the browser, and is held only on the client side.
You can use the Document object in Dynamic HTML to get information about
the document, to analyze and modify the HTML elements and text in the
document; and to process events.
We'll discuss the concept of objects more fully at the beginning of Chapter 6.
We won't be looking at browser objects like Document in any detail, since they
aren't part of ASP.
Let's see what happens if we try to run this example on a browser that doesn't support VBScript.
Here's what the page looks like on Netscape Communicator 4.6.
As you can see, the browser has detected the comment tags and recognized that it can't deal
with the VBScript – so it's left it out. Note also that there's no error message, so the page appears
to load smoothly. If your VBScript script only performs tasks that are non-essential to the overall
page (things like dynamic graphics, for example) then it ensures that the user still has a smooth
ride. But if, as in this case, the VBScript performs an essential task, then we still don't get the
output that we want.
How It Works
It works in much the same way as the client-side VBScript example that we saw a moment ago.
Here's the JavaScript:
d = new Date();
document.write(d);
This all looks rather frightening, doesn't it? The first line in this short two-line program basically
creates the built-in JavaScript object, Date, which is found on the client-side, in the browser, and
generates the current date. The program reads this date into something known as a variable
(we're not going to explain variables here, but they are covered in detail in Chapter 4). The
second line displays the contents of the variable.
On the client side, the more widely-supported scripting language is JavaScript. It's worth noting
that client-side script can help to ease your web server's load. We'll see an example of this in the
Wrox Classifieds ASP application that we'll build in Chapter 15 – which employs a couple of useful
little client-side scripts to perform data-checking functions on the browser, and hence reduce the
number of times the browser needs to communicate with the web server.
So ultimately, you may wish to learn about JavaScript too. However, VBScript is a great way to
get into scripting, and so it is the language that we'll be learning throughout the course of this
book.
Note If you do want to know more about JavaScript, you may be interested in
Beginning JavaScript (Wrox, ISBN 1-861004-06-0).
The Order of Execution
Now we've spent time understanding server-side and client-side scripts, and the role of the script
hosts and script engines, we can write a series of examples which will allow us to explore the
mysterious 'order of execution' a little further.
For the first of these examples, we'll create a script that writes a series of ten lines of text to the
browser. However, some of these lines will be written using pure HTML; some will be written
using ASP (as indicated by the <% … %> tags); and some will be written using server-side
VBScript (as indicated by the <SCRIPT LANGUAGE=VBSCRIPT RUNAT=SERVER> tag).
1. Open your HTML editor, create a new file, and type the following code:
2. <HTML>
3. <HEAD>
4. <TITLE>Testing the Order of Execution</TITLE>
5. </HEAD>
6.
7. <BODY BGCOLOR=WHITE>
8. Line 1: First HTML line<BR>
9. <% Response.Write "Line 2: First ASP line<BR>" %>
10. Line 3: Second HTML line<BR>
11. <SCRIPT LANGUAGE=VBSCRIPT RUNAT=SERVER>
12. Response.Write "Line 4: First server-side VBScript line<BR>"
13. </SCRIPT>
14. Line 5: Third HTML line<BR>
15. <% Response.Write "Line 6: Second ASP line<BR>"%>
16. Line 7: Fourth HTML line<BR>
17. <SCRIPT LANGUAGE=VBSCRIPT RUNAT=SERVER>
18. Response.Write "Line 8: Second server-side VBScript line<BR>"
19. </SCRIPT>
20. Line 9: Fifth HTML line<BR>
21. <% Response.Write "Line 10: Third ASP line<BR>"%>
22. </BODY>
23. </HTML>
As you can see, some of these lines will be interpreted by the ASP script host, and some directly
by the server-side VBScript script engine. We've numbered these ten lines of text, and each one
indicates the method that we've used to write the line.
1. Save the file as ExecOrder1.asp in your \Inetpub\wwwroot\BegASPFiles
directory. Since this page contains ASP code, we must save the file using an .asp
extension.
2. Go back to your browser and type the address
http://my_server_name/BegASP/ExecOrder1.asp into the address line. Don't forget to specify
the name of your web server. If you follow these instructions you should get something
that looks like this:
This is perhaps not exactly what you expected – after all, we did write lines 1 to 10 in the right
order in the code! But we can use this result to determine which parts of the code are being
interpreted in which order.
How It Works
As we noted at the start of the example, we've used three different methods to write different bits
of the page's content:
Pure HTML – for example, Line 1: First HTML line<BR>
ASP code – for example, <% Response.Write "Line 2: First ASP line<BR>"%>
Server-side script in a <SCRIPT> tag – for example,
<SCRIPT LANGUAGE=VBSCRIPT RUNAT=SERVER>
Response.Write "Line 4: First server-side VBScript
line<BR>"
</SCRIPT>
The unexpected order of the lines as they are displayed on the browser is due to the order in
which the server processes the different elements of the code. The first of these methods doesn't
require any processing on the part of the web server – it's pure HTML. The second and third parts
both use the ASP command Response.Write, which allows to us to write things into the HTML
stream that will be sent back to the browser. That's not the important issue in this example – in
fact, we'll discuss Response.Write in depth in a later chapter. What's more important here are
the differences between these two methods, and how they relate to the web server's order of
execution.
To appreciate what the web server has done, let's take a look at the HTML source for the page.
To do this in an IE browser, select View | Source from the browser's menu. In Netscape browsers,
select View | Page Source. Here's what I get:
<HTML>
<HEAD>
<TITLE>Testing the Order of Execution</TITLE>
</HEAD>
<BODY BGCOLOR=WHITE>
Line 1: First HTML line<BR>
Line 2: First ASP line<BR>
Line 3: Second HTML line<BR>
</HTML>
Line 4: First server-side VBScript line<BR>Line 8: Second server-side
VBScript line<BR>
There are two important lessons we can get from this. First, note that lines 1, 2, 3, 5, 6, 7, 9, 10
appear in the HTML stream in the same order that we coded them originally. These are the lines
that we wrote in pure HTML, and in <% … %>-delimited ASP. This tells us that we can mix our
pure HTML and our <% … %>-delimited ASP in the original code as much as we like – and the
web server will ensure that the order in which we code these things will be preserved.
In other words, we can write a mixture of bits of pure HTML and bits of 'in-line' ASP code, and the
web server won't mess with that order. That's an important point – and one that we'll use to our
advantage many times during this book.
There's a second lesson to learn here. It's pretty clear that, in the HTML source, lines 4 and 8 are
missing from their original order – instead, they are appended at the end of the HTML source.
Essentially, this is because the web server runs through the original code twice when it's
generating the HTML to be sent to the browser. The first time, it ignores any script of the form
<SCRIPT LANGUAGE=VBSCRIPT RUNAT=SERVER>, but handles other things (like pure HTML
and ASP code); the second time it revisits those scripts, interprets them in order, and appends
the resulting HTML onto the end of the existing HTML.
So, how is the order of execution affected if we try adding some JScript scripts? Let's try it out by
making a small change to the example above.
As you can see, it's quite similar to the previous example. All we've done is replace one of the
server-side VBScript scripts with a server-side JScript script.
1. Save the ExecOrder2.asp file, and view it on your browser. The result, even more
surprisingly, is this:
As you can see, the web server renders the JScript script first. Then it renders the HTML code
and any code contained within <% ... %> delimiters, again ensuring that the code is processed 'in-
line'; and the script contained within the <SCRIPT LANGUAGE=VBSCRIPT RUNAT=SERVER> tags
is rendered last.
1. Now make the following changes to ExecOrder2.asp, and save the these changes as
ExecOrder3.asp:
2. <%@ Language = JScript%>
3. <HTML>
4. <HEAD>
5. <TITLE>Testing the Order of Execution</TITLE>
6. </HEAD>
7.
8. <BODY BGCOLOR=WHITE>
9. Line 1: First HTML line<BR>
10. <% Response.Write ("Line 2: First ASP line<BR>"); %>
11. Line 3: Second HTML line<BR>
12. <SCRIPT LANGUAGE=VBSCRIPT RUNAT=SERVER>
13. Response.Write "Line 4: First server-side VBScript line<BR>"
14. </SCRIPT>
15. Line 5: Third HTML line<BR>
16. <% Response.Write ("Line 6: Second ASP line<BR>"); %>
17. Line 7: Fourth HTML line<BR>
18. <SCRIPT LANGUAGE=JSCRIPT RUNAT=SERVER>
19. Response.Write ("Line 8: First server-side JScript line<BR>");
20. </SCRIPT>
21. Line 9: Fifth HTML line<BR>
22. <% Response.Write ("Line 10: Third ASP line<BR>"); %>
23. </BODY>
</HTML>
In this version, we've specified the ASP scripting language to be JScript (rather than the default,
VBScript), and amended the syntax of the <% … %>-delimited lines accordingly.
Note We won't be choosing JScript for our ASP scripts again in this book – but it's
worthy of a mention. For the remainder of the book (after this example) we'll
stick to using VBScript, the default scripting language for ASP.
1. Now view ExecOrder3.asp in your browser:
As a result of specifying JScript as the scripting language for ASP, lines 4 and 8 have been
processed in a different order.
How It Works
ASP does not guarantee the order of execution of script written in different script blocks <SCRIPT
RUNAT=SERVER>. At present it seems that the web server first processes scripts written in the
language other than your ASP default language; and processes scripts written in your ASP
default language at the end. In between, script written within <% … %> delimiters are always
processed in-line – relative to their position in the surrounding HTML.
If you write a script within a <SCRIPT RUNAT=SERVER> script block, it will be processed either at
the beginning or at the end – it won't get processed in-line. Therefore, such script blocks are
generally reserved for writing structures called procedures and functions – pieces of code that
are separate from the main code for the page, and which are "called" into the page. We'll meet
these later in the book.
In general, we'll be specifying server-side script using the <% … %> delimiters – so these scripts
are processed through the ASP script host, and are processed in-line.
What does ASP do for us?
Having looked at how the web server handles ASP, we've now studied its physical workings in as
much detail as we will need for the purposes of this book. We've ascertained that ASP is actually
an ISAPI DLL loaded onto your web server that allows server-side scripting. But this doesn't tell
you about what ASP does, or how it manages the interaction between server and browser. Now
let's take a look at all the different pieces that ASP provides us with and start considering what
they may be used for.
The ASP 'objects' can be manipulated by scripting languages. Take a look at the following
diagram:
ASP functionality is divided up into seven intrinsic objects, each of which manages its own part
of the interaction between the web client and web server. When we write ASP code, we use six of
these objects – as you'll see over the course of the book, they are fundamental elements of our
code, because they're what we use to program ASP actions. The seventh, the ASPError object,
is used by IIS to handle errors and generate appropriate information about them. The
components, by contrast, are self-contained pieces of functionality that are written specifically for
ASP – each is a special add-on tool that allows the ASP author to achieve particular tasks, using
ASP capability.
Let's take a quick tour through all of the different objects and components that come as part of
ASP.
The Request and Response objects are, in a sense, the most self-explanatory. They represent
the movement of information from client-to-server, and from server-to-client:
The Request object is used to deal with a request that a user makes – that is, when they
ask the browser to see a particular web page or web application. The 'request' might be
made in the form of input from an HTML form, or just by typing in a URL.
The Response object is used to deal with the server's response back to the browser.
We've already seen an example of this, when we used the syntax Response.Write
"Hello!". When we write this, we're asking the web server to write the characters Hello!
into the HTML that is to be returned to the browser. The functionality for writing into the
HTML output is encapsulated into the method called Write – which, logically, is contained
within the Response object.
The other objects work more in the background to further enhance the functionality available in
ASP:
The Server object is used represent the web server itself. Thus, it provides several
commonly-used functions relating to things that the web server might do – such as creating
new objects and setting timeout properties for scripts. There are also methods for translating
character strings into the correct format for use in URLs and in HTML, by converting non-
legal characters into the correct legal equivalent.
The Application object is used to represent the web application (roughly speaking, a
collection of related web pages hosted by the web server). Thus, we can use it to manage
things like the contents of the application.
The Session object is used to represent the user's session (essentially, the 'experience'
that the user has while browsing a number of pages within the same web application), and to
store information about that session. We can use the Session object to manage things like
the maximum time that the web server will wait between user 'requests', before terminating
the session (and releasing the information relating to that session).
The ObjectContext object is used to manage transactions. It started out solely as part
of ASP, but now has been integrated into the Windows 2000 operating system as a whole
(along with Microsoft Transaction Server – a package with which it was closely related). It
encompasses all of the other ASP objects, and you can reference each of them through
ObjectContext. We'll talk about transactions and the ObjectContext object in Chapter
17.
The ASPError object contains details of any errors generated by an ASP script or by the
asp.dll itself.
We'll meet all of these objects formally, and learn how to use them, later in the book.
Active Server Components
Active Server components are components or DLLs that come freely with ASP (as opposed to
components that are vended by third parties). They have a wide range of purposes, as you can
see by the descriptions below. There are ten common components provided by Microsoft with IIS
5.0 (although different versions of the installation can add or remove components), and many
more are available from third parties. We'll be looking at the most important ones in Chapter 11.
For now, here's a brief summary of the components and what they do:
The Ad Rotator component does exactly what you might expect – it's a rotator for the
ads that appear on your page. More specifically, we use this component by supplying it with
a list of images; it will arrange for one of the images to be displayed on the page each time
the page is requested. It selects the images randomly, but takes account of a weighting
factor, which reflects what proportion of the total number of requests should see that image.
It also allows you to associate each image with a separate hyperlink, which takes the user
straight to a related connected site by clicking.
The Browser Capabilities component references a file called browscap.ini, which
details every version of every Microsoft and Netscape browser ever created. It uses this
information to determine whether or not the browser currently used supports frames, tables,
and so on.
The Content Linking component uses a text file to manage (and provide links for) a
sequential set of web pages. It allows the administrator to provide extra information about
each page in the sequence, and keeps the links in an orderly list so that they can be easily
maintained. For example, it can be used to guide a visitor through a sequence of pages in a
predetermined order.
The Content Rotator component is a slimmed-down version of the Ad Rotator
component, which just displays text.
The Counters component creates an object that persists for the lifetime of an
application and can be used to store, increment or retrieve a value. Counters are manually
set, unlike page counters for example, which are set automatically, and persist until deleted.
The Logging Utility component allows your applications to be able to read from your IIS
log files which monitor who has been connecting to your site.
The MyInfo component is used to store personal information about the server
administrator.
The Page Counter component provides a page counter, which increments by one each
time a page is accessed. This is an automatic process, rather than a user-defined one.
The Permission Checker component can be used to monitor whether a certain user
has been given permission to read or execute a file.
The Tools component provides a set of properties that are loosely grouped under the
catch-all heading of 'miscellaneous utilities'. Includes checks to see if a certain file exists or if
a certain user is the site owner.
ADO enables data access, and allows data to be viewed, manipulated and updated via web
pages (and, indeed, in other ways too). At the time of writing, the latest version is ADO 2.5 –
which comes as part of MDAC 2.5. We look at data access via MDAC and ADO in detail in
Chapters 12, 13 and 14.
The ASP Scripting Objects
Finally, there are three objects called Dictionary, FileSystemObject and TextStream,
which are provided in a separate library file, and known as Microsoft Scripting Runtime:
The Dictionary object is essentially a 2-column array, in which we might store a
name/value or name/description pair on each row of the array.
The FileSystemObject object is a tool for manipulating drives, folders and files on the
web server.
The TextStream object allows us to read the contents of a file as plain text.
We'll see more about these objects and some potential uses for them, in Chapter 10.
The Alternatives To ASP
Having explained what ASP is, you might be left with one or two nagging questions. For example:
"What other technologies could do the same job as ASP?" Or, "If Microsoft provides ASP, then
what are the non-Microsoft alternatives?"
Throughout these first two chapters we've avoided the temptation of describing ASP as
something 'new', or as your 'only alternative', quite simply because neither of these statements
would be true. ASP is only one of several technologies that can be used to create more dynamic
and interactive web pages. Now that you have an idea of how ASP works, you'll be better able to
understand how it stands in relation to other technologies and its contemporary competitors.
Microsoft is behind much of the drive towards the next generation of web technologies. However,
Microsoft isn't the only organization pulling in the direction of interactive web sites: many of its
competitors are also chipping away at the boundaries of interactive web capability.
Interactive web sites can be built with a combination of languages and technologies – you can
use any one of these alone, or any number of them together, and they're all interdependent (in
the sense that you don't have to learn one technology before you can learn another). Some exist
on the client side while others (like ASP) work on the web server.
Client-Side Alternatives
There are four major client-side technologies, which can be used to augment your web pages.
The technologies are a mish-mash of cross- and single-browser solutions that have persisted
through the years. We'll look at them in roughly chronological order (the exception being that,
although Java existed before JavaScript, they were implemented at the same time in Netscape
Navigator 2.0).
Scripting languages provide dynamic capabilities; for example, we can write a routine that is
executed each time the user clicks on a particular button on the page. The main disadvantage, as
we mentioned earlier, is that client-side scripts are dependent on the browser's implementation of
the language, so not all browsers support all scripting languages; and even when they do, there
are often marked differences between each browsers implementation and usage of different
features.
Java
Java is a cross-platform language for developing applications. When Java first hit the Web in the
mid-1990s, it created a tremendous stir. The idea is to use Java code in the form of applets,
which are essentially Java components that can be easily inserted into web pages with the aid of
the <APPLET> tag.
Java enjoys better functionality than scripting languages, offering better capabilities in areas such
as graphic functions and file-handling. Java is able to provide these powerful features without
compromising security because Java applets run in what is known as a sandbox – which
prevents malicious programs downloaded from the web from doing damage to your system. Java
also boasts strong database support through JDBC (Java Database Connectivity).
Microsoft and Netscape browsers both have built-in Java support, and there are several standard
<OBJECT> and non-standard <APPLET> tags which are used to add Java applets to a web page.
These tags tell the browser to download a Java file from a server and execute it with the Java
Virtual Machine built into the browser. Of course, this extra step in the web page building phase
means that Java applets can take a little while to download, and they can take even longer to
process once on the browser. So while smaller Java applets (that provide features such as drop-
down menus and animations) are very popular on the Web, larger ones are still not as
widespread as scripted pages.
Although the popularity of Java today isn't quite what some people expected, it makes an ideal
teaching tool for people wishing to break out into more complex languages; and its versatility
makes it well-suited for programming web applications.
ActiveX Controls
An ActiveX control is a self-contained program (or component), written in a language such as
C++ or Visual Basic. When added to a web page, an ActiveX control provides a specific piece of
client-side functionality, such as a bar chart and graph, timer, client authentication, or database
access. ActiveX controls are added to HTML pages via the <OBJECT> tag, which is now part of
the HTML standard. ActiveX controls can be executed by the browser when they are embedded
in a web page.
There is a catch. ActiveX controls were developed by Microsoft, and despite being compatible
with the HTML standard, they are not supported on any Netscape browser prior to version 5
(which, at time of writing, was still in beta) without an ActiveX plug-in. Without this, they will only
function on Internet Explorer, although there are plug-ins available if you want ActiveX
functionality with Netscape browsers. Consequently, they still can't really be considered a cross-
platform way of making your pages dynamic.
Note ActiveX technology is also applicable to server-side functionality, in the form of
ActiveX components.
Dynamic HTML
Dynamic HTML (or DHTML) is really nothing more than a buzzword – it was introduced by both
Microsoft and Netscape with their version 4 browsers, to advertise additional scripting features
such as the ability to animate pages and graphics without a page refresh, and to position text
precisely by using (x, y)-type coordinates. At the time, scripting was seeing a lower uptake than
either company would have liked, so this move was intended to create a greater appeal to the
masses, by dubbing it 'DHTML' and cashing in on HTML's familiarity and simplicity.
At the end of the day, the main innovation introduced in Dynamic HTML was the ability to
manipulate any feature on a web page directly using client-side scripting. This was made
available via the Document Object Model, but even together with the extra integration with style
sheets, you're still creating your web page from client-side script and HTML. The main downside
of DHTML was the fact that Microsoft and Netscape chose to implement these features in
methods that were incompatible with one another. The advent of the version 5 browsers sees
much tighter links with the standards, and hopefully a more cross-browser technology.
Server-Side Alternatives
There are also several more direct competitors to ASP. We're going to look at what we consider
to be four important technologies, in chronological order starting with the oldest. They don't
necessarily all perform the same tasks as ASP, but they all allow the user to achieve the same
end-result – that of dynamic web applications. If ASP is not an ideal solution to your problems,
then you might want to consider these following technologies, taking into account the following
questions:
Are they supported on the platform you use?
Are they difficult to learn?
Do they have extra capabilities, such as being able to parse XML?
We're not going to favor one option over another, but give a quick overview of what each one
does.
CGI
The Common Gateway Interface (CGI) is a mechanism for creating scripts on the server, which
can then be used to create dynamic web applications. It has been around for quite a bit longer
than ASP, and right now the majority of dynamically-created pages on the web are created using
CGI and a scripting language. However, it's incorrect to assume that CGI does the same job as
ASP. Rather, CGI allows the user to invoke another program (such as a Perl script) on the web
server to create the dynamic web page, and the role of CGI is to pass the user-supplied data to
the this program for processing. However, it does provide the same end-result – a dynamic web
application.
However, CGI has some severe shortcomings. The major one is that it adds an extra level to our
browser–server model of interaction: namely, it's necessary to run a CGI program to create the
dynamic page, before the page is processed on the server. Also, the format in which CGI
receives and transmits data means that this data is not easily manipulated by many programming
languages, so you have to use a programming language that has good facilities for manipulating
text and communicating with other software. The most able programming languages that can
work on any operating system for doing this are C, C++ and Perl. While they can adequately do
the job for you, they're some of the more complex languages to learn. Visual Basic doesn't offer
sufficiently adequate text-handling facilities, and is therefore rarely used with CGI.
ColdFusion
ColdFusion (http://www.allaire.com/products/ColdFusion) also enables servers to access data as
the server builds an HTML page. Like ASP, ColdFusion pages are readable by any browser.
ColdFusion also utilizes a proprietary set of tags, which are processed by the ColdFusion Server
software. This server software can run on multiple platforms, including Microsoft IIS, Netscape
Enterprise Server and Unix/Apache. The major difference is that while ASP solutions are built
primarily with VBScript and objects, ColdFusion utilizes the tags, which encapsulate functionality.
ColdFusion lacks some of the internal ASP objects; however it sports its own set of solutions to
common problems, including access to ADO functionality.
Java Server Pages
JavaServer Pages (JSP) is a new technology that allows you to combine markup (HTML or
XML) with Java code to dynamically generate web-pages. The JSP specification is implemented
by several web servers, and plug-ins are available that allow you to use JSP with IIS 4.0. One of
the main advantages of JSP is the portability of code between different servers. JavaServer
Pages isn't directly related ASP, but it does boast the ability to embed Java code into your web
pages using server-side tags, in the same way that ASP script can be embedded into web pages.
More details can be found in the JSP FAQ at
http://www.esperanto.org.nz/jsp/jspfaq.html.
PHP
Personal Home Pages (PHP) is a new server-side scripting language for creating dynamic web
pages. When a visitor opens the page, the server processes the PHP commands and then sends
the results to the visitor's browser, just as with ASP or ColdFusion. Unlike ASP or ColdFusion,
PHP is open-source and cross-platform. PHP runs on Windows NT and many Unix versions, and
it can be built as an Apache module and as a binary that can run as a CGI. When built as an
Apache module, PHP is especially speedy. A downside is that you have to download PHP
separately and go through a series of quite complex steps to install it and get it working on your
machine. Also PHP's session management was non-existent until PHP 4, and still inferior to
ASP's even now.
In addition to manipulating the content of your pages, PHP, like IIS, can also send HTTP headers.
You can set cookies, manage authentication, and redirect users. It offers good connectivity to
many databases (and ODBC), and integration with various external libraries that let you do
everything from generating PDF documents to parsing XML.
PHP, like ASP, can also go right into your Web pages. You can start a block of PHP code with <?
php and end it with ?>. (You can also configure PHP to use ASP-style <% … %> tags or even
<SCRIPT LANGUAGE="php"></SCRIPT>.) The PHP engine processes everything between
those tags. PHP's language syntax is similar to C and Perl. This might prove a barrier to people
with no prior programming experience, but if you have a background in either language then you
might want to take a look. PHP also has some rudimentary object-oriented features, providing a
helpful way to organize and encapsulate your code.
Although PHP runs fastest embedded in Apache, there are instructions on the PHP Web site for
set up with Microsoft IIS and Netscape Enterprise Server. If you want to try PHP, you can
download it at http://www.php3.net. You'll also find a manual that documents all of PHP's
functions and features.
Summary
So now, we hope you have a strong idea of the differences between the client and the server,
client-side scripting and server-side scripting, and JavaScript, VBScript and ASP. We also hope
you have a good idea about why you'd want to use ASP, and the other kinds of options available
for dynamic web pages. We haven't actually showed you how to program in any way – that
comes in the chapters that follow – but it's important, before you start programming, to be able to
appreciate the differences between programming on the server side with ASP, and programming
on the client side.
Briefly, we have learned that the browser and web server have the relationship of 'client and
server'. The browser submits a URL to the web server, in the form of a request, using HTTP. The
web server fetches the web page that matches the request: if there is any ASP script present, the
web server will identify and process the ASP, generating the HTML that will define the content of
the page. This new web page is returned to the client as HTML, which the browser then
processes in order to create the page display that we see on the screen.
We learned that the most widely-accepted language among web browsers (i.e. on the client side)
is JavaScript, while the default language for server-side scripting with ASP is VBScript. We will be
using VBScript for our ASP code throughout the rest of the book: it's a less complex language
than JavaScript, it's more widely used for ASP, and it is impractical to learn two languages at
once.
We also learned that there is an 'order of execution'. The execution sequence of the program
depends on the method used to insert the script, and the scripting language used: by using
different methods we can produce different results on the screen display. And we prepared
ourselves for upcoming chapters by giving the briefest of introductions to the ASP object model;
and we and looked at the alternatives to ASP.
We've already programmed a few example pages, but we've avoided the details of their syntax
and structure until the necessary foundations were laid. In the next chapter we'll start working on
a few of the basic programming techniques we'll need in ASP, and get you to a point where you
can start writing your own pages.
Chapter 3: Basic ASP Techniques
Overview
When I first started to study Chinese my tutor, Lao Wang, gave me a Chinese–English dictionary,
a Chinese grammar book and a primer. But he placed all of these books in a basket and said that
they were not to be used until the next week. In the first week he instructed me just to listen and
memorize some phrases. Before beginning a rigorous and orderly study he wanted me to learn
how to repeat a few phrases. "Excuse me, where is a restaurant?" "Please sir, may I have some
rice?" "How much does this cost?" That week, I had to accept on faith the content, sentence
structure, pronunciation and grammar. But by Friday I could at least walk into a Chinese
restaurant, order a bowl of rice and pay for it. Those first few phrases, without theory or
explanation, gave me enough grammar and vocabulary to get by.
We will take the same approach in this book. After this chapter you will get an in-depth study of
the syntax and techniques of ASP, but to start, we want to give you three basic techniques. Here
we won't explain the theory, exceptions or intricacies; those will come later. But at the end of this
chapter you will be able to:
Create a web page that asks the user a question and returns the answer to another ASP
page
Instruct ASP to retain the information supplied by the user
Use ASP code to write a line of text back to the user
In each of these cases, we want to do some action beyond simply serving a page. We'll either
need to make a decision about what page to serve, or we'll need to create a customized page for
that particular request.
In order to make our decision we need to get some information from the user. In the first case, we
need to know what product the user wants to see. In the second case we need to know if the user
is a member or not. In the programming world this kind of information is considered as input.
ASP needs input from the user in order to make its decisions.
Suppose that our web site has a 'page 1', which asks the user whether they want 'retail
information' or 'wholesale information'. Once the user has selected from these options, how do we
get that information from 'page 1' into the ASP of the second page? The simplest technique is to
use a feature of HTML called the form. An HTML form performs four tasks:
<FORM> tags can ask the user for information and provide a text box or check box into
which the user can type or select the answer
The form has a button, for submitting information back to the server
The submission has instructions to open a new page (usually an ASP page)
The submission also carries the information that the user typed into the fields which is to
be used by the ASP code of the new page
We use forms to provide a means for users to input data to the server – the information that we
need in order to make decisions in ASP.
Note If you are unfamiliar with forms and their methods for submitting information,
then please refer to Appendix F for a comprehensive tutorial on forms.
The opening <FORM> tag here has two attributes. The first, ACTION, gives the name of the ASP
file that should be opened next, and which will use the information that we gather in the form.
(Actually, it doesn't have to be an ASP file; but in this book, it usually will be.) The second
attribute, METHOD, determines which of two ways (POST or GET) that the browser will use to send
the information to the server. This directly corresponds to the method sent in the HTTP request to
the server that we talked about in the previous chapter. In this chapter, we will always use POST;
we will see the difference between POST and GET in Chapter 7, when we discuss both in more
detail.
The ACTION attribute of the <FORM> tag tells the browser what page is going to be opened when
the submit button is pressed. After the user has submitted the data, the browser automatically
requests this file. The name of the file should be in double quotes, although it doesn't have to be.
Most browsers can parse the name without the quotes but including them ensures compatibility
with all browsers.
The code above will produce the following page on the browser:
The Submit and Reset button tags have two attributes in this example. The first attribute, TYPE,
tells the browser what kind of button to create. The second, VALUE, specifies the message that is
to be displayed on the button. Note that both of these attributes should be enclosed in quotes.
The TYPE attribute can be set to SUBMIT or RESET or even BUTTON; those are keywords to the
browser so you should not try to rename them.
The VALUE will appear to the user on the face of the button, and thus it can be anything you like,
as long as you type it in quotes. For example, consider the following form:
<FORM ACTION="Calendar.asp" METHOD=POST>
<P><INPUT TYPE="SUBMIT" VALUE="Click here to submit this
information."></P>
<P><INPUT TYPE="RESET" VALUE="Whoa, man. I need to start over."></P>
</FORM>
This code produces a web page that behaves exactly as before, but that now looks something
like this:
The Information Input Tags
OK, that's enough to give us the framework of a form: but now we need the item of real interest –
a place for the user to input data. The input spaces on a form are called fields and are created
with the <INPUT> tag. Now we need to get the field set up wherein the user will actually enter
information. One such field is a text box, as we've added here in the code in the shaded lines:
<FORM ACTION="Calendar.asp" METHOD=POST>
<P>Please type your name in the space below</P>
<P><INPUT TYPE="TEXT" NAME="LastName"></P>
<P><INPUT TYPE="SUBMIT" VALUE="Click here to submit this
information."></P>
<P><INPUT TYPE="RESET" VALUE="Whoa, man. I need to start over."></P>
</FORM>
The new input line has two attributes, TYPE and NAME. In this case, we have specified
TYPE="TEXT", which instructs the browser to give us a text box into which the user can type
some input.
The second attribute, NAME, is the more important: every input field must have a NAME. The data
(entered by the user) will be joined to the name of its field; these two attributes will be passed
together to the ASP page specified by the <FORM> tag's ACTION attribute.
For example, suppose we have a page containing a form, and that the form has three fields that
ask the user for his first name, middle name and last name as follows (of course, the form also
has two fields presenting the Submit and Reset buttons):
<P><INPUT TYPE="TEXT" NAME="FirstName"></P>
<P><INPUT TYPE="TEXT" NAME="MiddleName"></P>
<P><INPUT TYPE="TEXT" NAME="LastName"></P>
<P><INPUT TYPE="SUBMIT"></P>
<P><INPUT TYPE="RESET"></P>
When the user clicks on the Submit button, a second page (usually with an .asp suffix) will be
called: this new page will receive the three pieces of information from these three fields, in a
format such as FirstName="Alexander", MiddleName="The" and LastName="Great". If
the INPUT tag didn't contain a NAME attribute, then in the new page there would be no way to
know which piece of information was from which field.
We can now put this together into a few practice forms. You won't be able to do anything with the
information that you gather just yet, but it's important just to get the forms working first.
34. Type in the name of the department (for argument's sake, type Sales) and click on the
Click here to send this information button. The following page should be displayed:
Note In this chapter all of the files have the extension .asp. The form files
that do not have ASP code are not required to have the .asp extension:
they would work fine with .htm or .html. However, as your site grows
in complexity and you expand your repertoire of ASP skills, you will want
to include ASP code on almost every page. Naming all of your files with
the .asp extension from the start overcomes the problems of changing
the extension to .asp at the time you add code.
How It Works
The first few lines of SpringRetreatDepartForm.asp are the standard lines of the header.
The first line of the body creates the image and then we have five lines that format and display
some opening text. The action starts with the opening <FORM> tag:
<FORM ACTION="SpringRetreatNotice.asp" METHOD=POST>
Within that tag we have two attributes. ACTION tells the browser which page to call when the user
clicks on the Submit button. The METHOD tag tells the browser how to send the data the user
types to the server.
Note At this point in your study, always use the POST method.
Within the <FORM> and </FORM> tags we have one text line and three input lines:
Please type your department here:
<P><INPUT TYPE="TEXT" NAME="Department"></P>
<P><INPUT TYPE="RESET" VALUE="Reset data"></P>
<P><INPUT TYPE="SUBMIT" VALUE="Click here to send this
information."></P>
Let's have a quick look at these four lines. The first line simply displays text so that the user
knows what to type in the field. The second line actually creates the box in which the user will
type. Note the two attributes: TYPE="TEXT" tells the browser this should be a text box, as
opposed to a check box or options buttons. NAME="Department" gives the identifier,
Department, that is attached to the data that the user will type.
Important Never leave the NAME attribute out of an <INPUT TYPE="TEXT"> tag;
without it, the data can never be used by ASP.
The other two input lines provide the standard Submit and Reset buttons. In this case we have
used the VALUE attributes to change the words on the buttons from the default values, so that the
buttons contain text that is a bit more customized for our situation.
When the user sees this page, they can enter information in the box. If the user isn't happy with
what they've entered, they can click on Reset data and enter different information in the text box.
What happens when the user clicks on the Click here to send this information button? First, the
browser takes the information that was typed into the text box (this might be "Sales", if that's what
you wrote in the text box) and assigns it to the input box name to make Department="Sales".
Then, the browser sends this to the server, along with a request for the page called
SpringRetreatNotice.asp. When the server finds the .asp page, it will run it through the
ASP DLL and it will have the information that the user typed into
SpringRetreatDepartForm.asp available to it. In the next few pages we will learn how to
use that information. The server then sends the SpringRetreatNotice.asp page back to the
browser.
At this point, we have not used the information from the visitor; however, we have demonstrated
how the <FORM ACTION="page.asp"> is activated by the Submit button to send a request to
the server for the next page. Now, let's create a form that gets several items of information from
the user.
41. Enter some details, and click on the Save my Preferences button to send the information to
the server. The browser then automatically opens up the page called
SpringRetreatJacketConfirmation.asp:
How It Works
This code is similar to the last except that we are now working with three fields of data instead of
one. The first few lines are the page header. Within the body we first present three lines of simple
HTML text giving the user some instructions for the page.
<HTML>
<HEAD>
<TITLE>Form for Spring Retreat Jacket</TITLE>
</HEAD>
<BODY>
<H1>Company Spring Retreat<BR>
Jacket Order Form</H1>
<H3>Please fill in this form and click on Save My Preferences</H3>
Then we begin the important part with the <FORM>. The <FORM> tag must contain at least the
attribute to tell the browser what page to call when the Submit button is pressed. In this case that
is the ACTION=SpringRetreatJacketConfirmation.asp. At this point in our study, we are
only concerned with using the METHOD=POST attribute:
<FORM ACTION="SpringRetreatJacketConfirmation.asp" METHOD=POST>
In this next section, the code alternates between lines of plain HTML text and input fields (of type
TEXT). The HTML text in this case is asking the user for information, and for each question asked
there is a related input field – "gender", "size", and "color":
<P>Please type your gender</P>
<P>("male" or "female"):
<INPUT TYPE="TEXT" NAME="gender"></P>
<P>Please type your preference of size</P>
<P>"S" "M" "L" "XL":
<INPUT TYPE="TEXT" NAME="size"></P>
<P>Please type your preference of color</P>
<P>"Argent" or "Azure":
<INPUT TYPE="TEXT" NAME="color"></P>
As always, we add Submit and Reset buttons and close off the form as follows:
<P><INPUT TYPE="RESET" VALUE="Start Over on This Page">
<INPUT TYPE="SUBMIT" VALUE="Save my Preferences"></P>
</FORM>
In this section, we're going to show how ASP holds the incoming information at two levels. First,
any information from the form is passed to the new ASP page and is automatically captured and
held by ASP in its Request object. Second, we can write code in our script to harvest the
information from the Request object, and put it into a script variable. In any programming
language, variables can be used to store, test and manipulate information.
Chapter 4 explains in detail the subtleties of variable types and duration, and Chapter 7 will talk
about the Request object in depth. But for now we'll look at the quick and dirty technique – a
brief overview of variables.
When the user types their name and clicks on the Submit button the browser sends two pieces of
information to the server. The first is a request that the server should get the file named
SpringRetreatNotice.asp. The second is the list of the data that the user typed in to the
field – in this case, the word Sales was typed into the field named Department.
So what does this do? Well, the first line tells the server to begin a section of ASP code. The
second line dimensions (or declares) a variable called strDepartment. Then in the third line
we set the contents of strDepartment to a value that ASP is holding in the Request object –
the piece of information that the user entered into the form and submitted to this page. Once that
information is saved within a variable, we can use it any way needed in our ASP code until the
page requested has finished processing. We will see this used in our next Try-it-Out.
Once the variable has been created with the Dim keyword, it can be filled with some information.
The line that populates the variable must begin with the name of the variable. The variable name
is followed by a space and the equals sign ( = ) and then another space. On the right of the
equals sign, we must use the exact words Request.Form then open parenthesis, a double
quote and the name of the input field from the page. Finish the line with another double quote and
close the parenthesis. The right hand side of this "equation" instructs our ASP processor to look
in the Form collection of the Request object; and from within that collection, to give us the data
that has been assigned the name Department.
Note Don't worry about understanding the details behind the Request object and its
Form collection just now – we'll be looking at both in Chapter 7.
In our second Try-it-Out for forms, the request for information on the jackets, we would use the
following code in the action page:
<%
Dim strGender, strSize, strColor
strGender = Request.Form("Gender")
strSize = Request.Form("Size")
strColor = Request.Form("Color")
%>
The variables strGender, strSize and strColor would be created (on the second line
above) and then filled with the data from the Form collection of the Request object, which holds
the data typed in by the user into those fields.
Common Errors
We will be using this technique in our next few Try-It-Outs, but before we use this, let's just take a
quick look at some common sources of errors. The most common errors are in typos. If variable
assignments don't work you should check:
Exact spelling of the name that you gave to the input tag. It is also a good habit to stick to
using the same case throughout, because some scripting languages (like JavaScript) are
case sensitive – so a Rose is different from a rose is different from a ROSE (contrary to the
wisdom of William Shakespeare!)
Exact typing of the objects, methods and properties: Request.Form
Keep your variable names very simple for starters: all letters, no symbols. Never use
spaces in variable names
The field name must be in double quotes, and that within parentheses
Be sure to put the variable name first (to the left of the equals sign) and the source of
data second (to the right of the equals sign)
The assignment of the variable must be within ASP code delimiters (between <% and %>)
Be sure that in your form page you added the attribute METHOD=POST in the <FORM> tag
The second technique is to instruct ASP to display the text on the screen – and to do that we use
the Response.Write syntax. The Response.Write command instructs ASP's Response
object to write the requested text to the browser. For example, if we want the words Autumn
Sweaters to appear as before, but this time from within a section of ASP code, we could use the
following construction:
<HTML>
...
<%
Response.Write "Autumn Sweaters"
%>
...
</HTML>
You can, of course, use the first technique on a page that contains ASP code, provided you make
sure that you do not enter the text within an ASP code section, or within <SCRIPT> blocks. If we
had accidentally forgotten that we were working within the ASP delimiters (the <% and %> tags)
and not included the keywords Response.Write and the quotes, we would receive an error.
That 's because ASP would have tried to find a command with the name Autumn with a
parameter Sweaters – since no command exists with this name, the user who requested the
page would have an error page sent back to them.
On the other hand, if we had typed in Response.Write "Autumn Sweaters" from outside of
the ASP code block, we would have a page with the text Response.Write "Autumn Sweaters"
appearing on it.
So we can see that Response.Write is great for writing text into the page. But there's more: we
can also use Response.Write to write HTML tags into the page. For example, to have ASP add
a horizontal line to the page, we can use Response.Write to put the <HR> tag into the HTML
page as follows:
<%
Response.Write "<HR>"
%>
The first line indicates the start of the ASP code. Then we create a variable called strItem. The
third line takes the data that the user typed into the form field called ItemChoice, and it copies
that data into the variable called strItem. This line makes the user's input available for ASP.
The fourth line writes the contents of strItem onto the HTML page and then the fifth line closes
off the ASP section of code.
It's important not to underestimate the power of what we have just done! With this simple
technique, the text that is written on this page can potentially be different for each different user.
With static HTML, we can only send the exact same page to every visitor; but with ASP
techniques like this we can customize our pages to reflect the exact needs of each user.
Important Note that Response.Write uses a slightly different syntax for variables
and for text or tags. When using Response.Write for variables, the
variable name should not be in quotes; when using Response.Write for
text and tags, the text and tags should be in quotes.
In the above example the first line consists of straight text and HTML tags, just as if you had
never heard of ASP. The second line starts an ASP block, which writes some more text. (Inside
the block we first create the strItem variable; then we populate our new variable with the user's
information; then we direct ASP to write the contents of strItem onto the HTML page.) So if the
user had typed the phrase Autumn Sweaters into the ItemChoice field and submitted that value,
the result of the above code would be a page in the browser as shown below.
This intermingling of HTML and ASP-written text applies to the tags as well. If we wanted to make
some of the words in our display bold (using the <B> and </B> tags), we could insert the tag from
HTML like this:
<P>Next week's featured item: <B>
<%
Dim strItem
strItem = Request.Form("ItemChoice")
Response.Write strItem
%>
</B></P>
In the above example the paragraph starts with some tags and text, including an opening <B>
tag, before the ASP code begins. ASP writes the contents of the strItem variable in bold type.
Then ASP finishes and the HTML page adds the closing </B> tag. Once the ASP has calculated
the value of strItem (assuming the user typed in the value Autumn Sweaters), the result is the
same as if you had just written straight HTML as follows:
<P>Next week's featured item: <B>Autumn Sweaters</B></P>
Alternatively, we could have written those <B> and </B> tags to the page from within the ASP
code, like this:
<P>Next week's featured item:
<%
Dim strItem
strItem = Request.Form("ItemChoice")
Response.Write "<B>"
Response.Write strItem
Response.Write "</B>"
%>
</P>
This time, we've removed the <B> and </B> tags from the pure-HTML lines. Instead, we've got
three Response.Write statements, to write three different things to the browser. The ASP
Response.Write command doesn't care if it is writing text, tags or a mixture. You can put text
plus one or more tags in a single Response.Write (although this wouldn't look too pleasing to
the eye):
<P>Next week's featured item:
<%
Dim strItem
strItem = Request.Form("ItemChoice")
Response.Write "<B>"
Response.Write strItem
Response.Write "</B>. <I>Suits you, Sir!</I>"
%>
</P>
Of course, the power that ASP's Response.Write brings to our pages is that it allows us to
write the values of variables and functions to the page. The exact display shown in the page
happens because the user typed the string Autumn Sweaters into a form, and submitted that value;
and it was then captured by the new page's Request.Form collection and used to write the new
page. We simply can't do that using plain HTML.
If the user had typed in Fleecy Trousers, then the resulting page above would have been different:
OK, this page isn't much different to the previous one – but imagine what we can do once we've
got a few variables together, and made some decisions dynamically based on the values of those
variables. We'll soon have some exciting, complex, dynamic pages that will get the users coming
back for more.
A Shortcut for Response.Write
Now let us finish with a shortcut. We're likely to use the Response.Write many times in order to
write the values of all our variables to the page – so for that very reason, ASP provides a special
shorthand form of Response.Write. We can use the syntax <%=strName%> you can write the
contents of the variable strName onto the page.
For example, suppose a customer is using a web site to buy a rectangular piece of carpet, and
they have submitted values for the length and width of the piece of carpet they want to buy. In the
page that handles these values, we might handle the inputted data like this:
<%
intLength = Request.Form("CarpetLength")
intWidth = Request.Form("CarpetWidth")
%>
Your piece of carpet will have length
<% Response.Write intLength %>
and width
<% Response.Write intWidth %>
The two Response.Write calls are rather clumsy, but we can rewrite this code using the
shortcut instead, which makes the code much easier to read:
<%
intLength = Request.Form("CarpetLength")
intWidth = Request.Form("CarpetWidth")
%>
Your piece of carpet will have length <%=intLength %> and width <
%=intWidth %>
Note that you can only use this shortcut to abbreviate a Response.Write, and it doesn't work if
you mix it with other ASP commands in the same ASP code block. For example, in the following
sample the highlighted lines will fail:
<%
intLength = Request.Form("CarpetLength")
intWidth = Request.Form("CarpetWidth")
Response.Write "Your piece of carpet will have length "
=intLength ' this line is
illegal
Response.Write " and width "
=intWidth ' this line is
illegal
%>
So, in this section we have talked about how to direct ASP to write characters to the HTML page
before it goes out to the user. Here are the take-home points:
These characters can be of three types: text, HTML tags, or the contents of a variable
The command to write characters to the HTML page from within ASP code is
Response.Write
The syntax for text is: Response.Write "text goes here"
The syntax for variables is: Response.Write strName
We also covered a shortcut to put the contents of a variable into a line of HTML text using
the equals sign, straight after the opening ASP delimiter: <%=strName%>
Let's now improve the confirmation page that we return after asking the user for their department
in our example.
So let's amend this second page to show text that not only confirms the visitor's registration, but
also confirms which department they have registered. Keep in mind that it won't be until later in
the book that you will learn how to actually make a registration in a database. But for now, we will
just focus on returning a message to the user.
1. This solution needs you to change and add the highlighted lines to
SpringRetreatNotice.asp.
2. <HTML>
3. <HEAD>
4. <TITLE>Spring Retreat Notice</TITLE>
5. </HEAD>
6.
7. <BODY>
8. <H1>Spring Retreat</H1>
9. <H2>Thank you for registering as a member of the
10. <%
11. Dim strDepartment
12. strDepartment = Request.Form("Department")
13. Response.Write strDepartment
14. %>
15. department.</H2>
16. </BODY>
</HTML>
17. Save the page with the same name as previously.
18. Now, open SpringRetreatDepartForm.asp in the browser; fill in the text box and
click the button marked Click here to send this information. This will cause our revised
version of SpringRetreatNotice.asp to appear on the browser. If you typed the word
Sales into the input field and submitted that, then you'll see the following confirmation page:
How It Works
This code starts with the same initial information as we used in the first Try-It-Out for forms.
<HTML>
<HEAD>
<TITLE>Spring Retreat Notice</TITLE>
</HEAD>
<BODY>
<H1>Spring Retreat</H1>
<H2>Thank you for registering as a member of the
But at this point we kick in ASP with a <%, and our first ASP statement creates a variable named
strDepartment. ASP populates this variable with the data sent from the browser and labeled
as Department – which, in the case of the screenshot above, is the word Sales. The third ASP
statement writes the contents of the variable strDepartment onto the page. Then the ASP
code section is closed:
<%
Dim strDepartment
strDepartment = Request.Form("Department")
Response.Write strDepartment
%>
In order to make the page look neater, we've add one line of simple HTML text to finish off the
sentence. Then we close up the body and the page:
department.</H2>
</BODY>
</HTML>
OK, now our SpringRetreatNotice.asp page is a bit more useful – because it tells the user
some useful information, based on what action they took in the first page.
What about our jacket registration form? That page sends three fields of data to the server. Now,
since we now know how to put that information into variables, let's produce a more functional
confirmation page that reads this information back to the user.
How It Works
We know how the HTML form works, so let's just look at how our revised
SpringRetreatJacketForm.asp code handles the submitted data.
The first few lines of the page's body contain a block of ASP logic. First, we create the three
variables, and assign to these variables the values that were entered by the user in the previous
page:
<BODY>
<%
Dim strGender, strSize, strColor
strGender = Request.Form("Gender")
strSize = Request.Form("Size")
strColor = Request.Form("Color")
%>
Then we shift out of ASP and into HTML, to put some text on the page:
<H1>Company Spring Retreat</H1>
<H3>Jacket Order: Confirmation</H3>
For you, we will order a jacket in
The next line instructs ASP to write the contents of the variable named strColor on the HTML
page. ASP respects the position of this command within the surrounding HTML and text, and
writes the contents of strColor to just the right place in the page:
<%Response.Write strColor%>
Now we shift out of ASP again, to write a little more pure text. Then we shift back into ASP, to
write the user's input for Size into just the right point in the page:
and size
<%Response.Write strSize%>
Then some more pure text, one last variable value (in an ASP block) and one more line of pure
text to finish of the output:
for a
<%Response.Write strGender%>
employee.
There's less shifting into and out of ASP here – the whole body section is ASP code. Each block
of ASP code, enclosed in a pair of <% … %> delimiters, requires another call to the ASP DLL. This
means that the more times you use <% … %>, the longer your code will take to execute. Holding
all our code within a single block of ASP can give you a noticeable run-time speed increase over
using many blocks of ASP. There are more Response.Write statements, which can make the
code look a little bulky, but the performance issue is am important one to consider.
Note We could reduce the number of Response.Write calls in the above sample
by using a technique called string concatenation. This involves fixing two or
more smaller strings together to make one big string. After concatenating all the
small strings into one big string, we'd just call Response.Write once – to
write the big string to the browser. There's more about string concatenation in
Chapter 4.
A third solution is easier to read, but uses more ASP blocks. In this case, we'll use the
Response.Write shortcut when writing the output to the browser:
<BODY>
<%
Dim strGender, strSize, strColor
strGender = Request.Form("gender")
strSize = Request.Form("Size")
strColor = Request.Form("Color")
%>
<H1>Company Spring Retreat</H1>
<H3>Jacket Order: Confirmation</H3>
For you, we will order a jacket in <%=strColor%> and size <%=strSize%>
for a <%=strGender%> employee.
</BODY>
All three of these solutions produce the result on the browser. I recommend the second of the
three solutions, because it is the fastest to execute – it requires fewer trips to the ASP DLL.
You travel to various offices around the world, presenting seminars to your colleagues on the new
products that your company is about to introduce. Paris in April, Tokyo in September; it is a very
glamorous job. But you do have some paperwork. At each seminar you need to make a sign-in
sheet with the company logo, date, and so on; and a number of horizontal lines that each of the
delegates of your seminar must sign to prove that they attended the seminar.
You want to make up a template of the sign-in sheet on your web site. The idea is that, wherever
you are in the world, you can browse to your web server back home, type in the location and date
of the seminar, and then have the web server create a sign-in sheet on the browser (with the
location and date information written in). Then you can just print the page from the browser and
have a beautiful sign-in sheet.
For this example, pick any piece of clipart for your company logo.
1. You've guessed it…crank up your trusty web page editor and type in the following:
2. <HTML>
3. <HEAD>
4. <TITLE>'Where Am I' form for Sign-in Sheets</TITLE>
5. </HEAD>
6. <BODY>
7. <H1>Sign-in Sheet template for my 'New Products' Seminars</H1>
8. <BR>
9. Just fill in the following details:
10. <FORM ACTION="SignInSheet.asp" METHOD=Post>
11. <P>City:<INPUT TYPE="TEXT" Name="City"></P>
12. <P>Date:<INPUT TYPE="TEXT" Name="Date"></P>
13. <P><INPUT TYPE="SUBMIT" VALUE="Click here to submit the
information"></P>
14. <P><INPUT TYPE="RESET" VALUE="Click here to start over"></P>
15. </FORM>
16. </BODY>
17. </HTML>
18. Save this file as WhereAmI.asp, into your \inetpub\wwwroot\BegASPFiles
directory.
19. Close this page down and start another new one; type the following into it:
20. <HTML>
21. <HEAD>
22. <TITLE>Sign In Sheet for New Products Seminar</TITLE>
23. </HEAD>
24. <BODY>
25. <H1>On-Line Clothiers <IMG SRC="Bizrun.jpg" WIDTH="105"
HEIGHT="111"></H1>
26. <H1>Welcome to the New Products Seminar</H1>
27. <%
28. Dim strCity, strDate
29. strDate = Request.Form("Date")
30. strCity = Request.Form("City")
31. Response.Write "Held in "
32. Response.Write strCity
33. Response.Write " on "
34. Response.Write strDate
35. %>
36. <P ALIGN="left">please sign in by printing your name</P>
37. <HR>
38.  <HR>
39.  <HR>
40.  <HR>
41. </BODY>
42. </HTML>
43. Save this file as SignInSheet.asp, in the same directory.
44. Open the WhereAmI.asp page in your browser, type in a city and a date and click on the
Submit button:
45. If you submitted the name Beijing, and the date December 16, 1999, then you'd see the
following:
How It Works
The solution is contained in two pages. The first is a page that doesn't use ASP but does contain
a form with input fields for the user to submit data. That submission calls an ASP page,
SignInSheet.asp, in which the submitted data is available to be written at appropriate points in
the page.
The form page (code for the body shown below) is not dissimilar to things we've seen in the other
exercises in this chapter. The <FORM> tag must always have the action attribute – in this case
pointing to the page named SignInSheet.asp. There are two input fields in this form, with the
names City and Date, each reflecting the type of information expected and each very important
in allowing us to distinguish the different pieces of information in SignInSheet.asp:
<H1>Sign-in Sheet template for my 'New Products' Seminars</H1>
<BR>
Just fill in the following details:
<FORM ACTION="SignInSheet.asp" METHOD=POST
<P>City:<INPUT TYPE="TEXT" VALUE="Name of City" Name="City"></P>
<P>Date:<INPUT TYPE="TEXT" VALUE="Date of Seminar" Name="Date"></P>
<P><INPUT TYPE="SUBMIT" VALUE="Click here to submit the
information"></P>
<P><INPUT TYPE="RESET" VALUE="Click here to start over"></P>
</FORM>
Let us look at the form tags more closely, to review a few ideas. The open form tag <FORM> must
have the attribute of ACTION="filename.asp". The value for that attribute should be the ASP
file that will be opened when the form is submitted – in this case it's SignInSheet.asp.
Then form itself contains four fields. The first two are both input tags of type TEXT – they are the
places where the user types in the city and date. It is important to name each of these fields,
using the NAME attribute, so we can identify them within the page that is called by the ACTION.
The last two fields are the simple and standard Submit and Reset buttons. Remember that when
the user clicks on the Submit button, the browser will gather the data typed into the input fields,
give them the appropriate identifying labels (from the NAME attributes of the corresponding
<INPUT> tags), and then send the data and label to the web server with a request to open the
page indicated by the ACTION attribute of the <FORM> tag.
SignInSheet.asp contains the ASP that handles this input, and it's where the action happens.
The objective of this page is to take the sign-in sheet template that we've designed, and add in
the city and date details for this particular seminar (as the user entered in the first page). When
the web server has done that, and has sent the result to the browser, we can print out the page
from the browser and hand it round the lecture room during the seminar.
After the normal <HEAD> material, the <BODY> begins. Two heading level 1 lines splash the
company name and logo across the top of the page, followed by a Welcome… line:
<BODY>
<H1>On-Line Clothiers <IMG SRC="Bizrun.jpg" WIDTH="105"
HEIGHT="111"></H1>
<H1>Welcome to the New Products Seminar</H1>
In the next line we use <% to shift gears into ASP. The first line of script creates two a variables,
named strCity and strDate:
<%
Dim strCity, strDate
We use these two variables to hold the values that the user submitted to this page, via the form:
strDate = Request.Form("Date")
strCity = Request.Form("City")
So we haven't done any Response.Write-ing yet, but we have taken the time to prepare for it –
by setting up all our variables in advance. This makes our code nice and tidy.
Now we use a sequence of Response.Write lines to write the location and date information to
the page. There are four Response.Write lines in total, alternately writing a little plain text and
then a variable value:
Response.Write "Held in "
Response.Write strCity
Response.Write " on "
Response.Write strDate
%>
The closing %> delimiter marks the end of the ASP. Then we write some more text and HTML to
the page, to generate a few lines for the delegates to write on:
<P ALIGN="left">please sign in by printing your name</P>
<HR>
 <HR>
 <HR>
 <HR>
</BODY>
</HTML>
If you'd wanted to use the shortcut for Response.Write, then the code in SignInSheet.asp
would have contained a block of code like this:
<%
Dim strCity, strDate
strCity = Request.Form("City")
strDate = Request.Form("Date")
%>
Held in <%=strCity%> on <%= strDate%>
Once again, note that the <%= … %> shortcut is a very useful syntax, when you need to use ASP
to drop variable values into HTML output like this. Remember that each <% … %> or <%= … %>
pair means another trip to the ASP DLL, which means that your ASP page will take a little longer
to execute. If you only use it occasionally then it can be a boost to your code's readability, with no
significant loss in performance. But if you use it very many times, then you will start to notice
slower delivery times for your web pages.
This finishes our basic ASP examples. You might not have understood all of the working behind
the code, but you should at least have an idea of how to send information in a form and display it
on a separate page.
Summary
In this chapter, we first covered HTML forms and then we practiced making forms. We focused on
the importance of the <FORM> tag's ACTION attribute, and the <INPUT> tag's NAME attribute.
Once the user has submitted the form (by clicking the special SUBMIT button in the form), the
browser submits the user's information to the web server, along with a request for a new web
page (specified by the ACTION attribute).
In the new web page, we can access the submitted information using the
Request.Form("field_name") syntax. One of the things we can do with that syntax is use it
to assign the assocated value to a local variable in the page, like this:
strName = Request.Form("field_name")
Finally, we talked about using the Response.Write "Text" syntax to get ASP code to write
text onto an HTML page that's being sent out to the browser.
And we looked at the powerful technique of writing the contents of a variable onto the page, using
the syntax Response.Write strName or simply <%=strName%>. This is a crucially important
technique in ASP, which we will use to dynamically add content to our pages many times in this
book.
We haven't included much detail about the theory, logic or exceptions to these ASP techniques –
all that will come in the remaining chapters. But at least now you have some basic tools that allow
you to:
Create an ASP page
Get some information from the user
Retrieve the submitted information, and store it in a variable
Write the submitted information back to the browser within a subsequent ASP page
Now, as my professor of Chinese told me, you are ready to begin your study.
Chapter 4: Variables
Overview
One of the most important concepts in any programming language is the ability to store
information. Suppose a user is required to input their name: where do you store this information,
so that it can be used later? How do you store other types of data, such as numerical data and
dates? In addition, what if several users have all provided similar pieces of data – how does the
computer know how to match up the information provided to the user who provided it? This can
all be done using variables.
Your programming language gives you the power to create variables, to assign values to them, to
test the contents, and to reuse them in your program. They will enable you to perform
mathematical functions, calculate new dates, disassemble text, count the length of sentences,
and so on. Variables are fundamental to programming – they'll form the foundations of nearly
every business solution you'll come to program.
CapitalCityOfUK = "London"
NumberOfStates = 50
IndependenceDay = #7/4/1863#
Any variable is empty until you put information into it (although the memory space is reserved
while the script runs). You can then look at the information inside the variable, get the information
out, or replace the information with new data. In fact, variables are essential for storing data in
any computer language; in this book, we'll discuss variables in the context of VBScript and ASP.
Creating a Variable
In the above code example, two things are happening. Firstly, three variables are created and
secondly they are given values to store. We'll discuss more about the latter a bit further on in this
chapter, but for now, let's concentrate on the first three lines of this example:
Dim CapitalCityOfUK
Dim NumberOfStates
Dim IndependenceDay
This does exactly the same thing as the three lines above – it creates three separate variables as
before, which are empty and ready to accept data.
We'll discuss creating (or declaring) variables again later in this chapter.
This does have a couple of downsides, there is a performance hit, and sometimes VBScript
serves up a different type for the data than you want. However, you can normally go far by just
using the variant and letting it automatically assign a type – it usually works. Hence, you could
declare a variable like this:
Dim LengthOfAPieceOfString
Later on, you might decide that you want to replace this value with a string value:
LengthOfAPieceOfString = "Not very long"
This means that we need a mechanism for keeping track of the type of data that is being stored in
each variant. VBScript assigns a subtype to the variant that reminds the variant of the type of
data being stored. A subtype can be any of the following.
Numeric Subtypes
You can assign almost any number to a variable. We can assign whole numbers, fractions, and
even negative floating pointing numbers:
IntegerNumber1 = 76
DecimalNumber2 = 2.5356
FloatingPointNumber3 = -1.4E06
Note Basically, we use floating point numbers to represent very small or very large
decimals such as 0.00000123 or 1.14E-6 or 1.87x10 -6.
In VBScript, there are five different numeric subtypes, which are outlined below.
Integer
Integers are simply whole numbers. Examples of integers are 3, 12, and -5127. The integer data
type can handle whole numbers within the range -32,768 to 32,767. For numbers that are outside
this range then the long type is used, which we'll see shortly.
Byte
Bytes are integers within the range 0 to 255. They're used for very basic arithmetic. It's a useful
type, because the method in which data is made available means that a variable can be easily
stored by the computer within a single byte, which is the computer's basic storage unit. As
VBScript uses variants, VBScript requires an extra byte on top of this, but the principle of bytes
taking up very little memory space remains the same.
Long
The long type is very similar to the integer type, but supports a much larger range. A long variable
can contain a value in the range -2,147,483,648 to 2,147,483,647.
Single
The single type can hold single precision floating point numbers, within the range
Double
The double type holds double precision floating point numbers. This means that it will support a
much larger range than the single type. In reality, this range is -1.79769313486232E308 to
Currency
The currency subtype accepts numbers with up to four decimal places. It can support a range of -
922,337,203,685,477.5808 to 922,337,203,685,477.5807.
String Subtype
Variants with the string subtype hold textual information or words. The string subtype will identify
everything you store in it as text, even if you supply it as a mixture of text and numerical data,
numerical data alone or even date information. For example, the following code creates a variant
called CarType, with the value "Buick", a variant called CarEngineSize, with the value
"2.0", and a variant called DatePurchased, with the value "July 4, 1999":
CarType = "Buick"
CarEngineSize = "2.0"
DatePurchased = " July 4, 1999"
String values are usually put in between double quotation marks, so that they can be
differentiated from numerical values. Note that you can't perform mathematical functions on
strings, even if the content of the strings involved are purely numerical. Hence, if you try to add
the two strings "12" and "14" together, as shown in the following example, you won't get the
result "26" that you might have anticipated:
Number1 = "12"
Number2 = "14"
Number3 = Number2 + Number1 'Will produce "1412"
This is because while the string subtype itself seemingly accepts different types of data, it is only
holding textual representations of these types, such as Integer or Date. Therefore, while it might
appear to you that Number1 contains a number, the presence of quotation marks indicates that
this is to be treated as text.
We'll cover the process of joining, or concatenating, strings later in this chapter. VBScript also
provides a number of special functions with which you can manipulate strings. These functions
allow you to measure the length of a string, truncate a string at the beginning or end, return
certain characters from a given string, or even convert a string into its numerical equivalent. We'll
look at string manipulation functions later in this chapter. A basic rule of thumb is that strings are
normally used for storing words or textual information – numeric information is normally stored in
the appropriate numeric subtype. The exceptions to this rule are numbers such as telephone
numbers and social security numbers, which are usually better stored as strings.
Date Subtype
VBScript also supports a subtype, date, that can be used to hold dates and times. Variants of this
subtype use a predefined format. The same subtype is used to record both date and time. For
example, we can use the variant DateTime to store a given date:
DateTime = #12/10/2001#
A date must be surrounded by the # symbol (and not the " symbol): if it isn't surrounded by any
symbols, it will be evaluated as a numeric expression. (If it were surrounded by "" then it would
be interpreted as a string.) Later in the same program, we could use the same variant to store a
given time:
DateTime = #11.03#
The predefined format of the date type prevents 'regular' numerical arithmetic from being carried
out on variants of subtype date. Once the computer is aware of their special format, it's possible
to carry out simple numerical arithmetic on them. For example, if you subtracted July 20, 2001
from July 24, 2001, you'd get the answer 4.
Boolean Subtype
Boolean variants can be set to one of two values, namely TRUE or FALSE. In VBScript, if you
convert these values to the integer type then they convert to the values –1 and 0 respectively.
They can be used to record the state of the variable, in that if a variable isn't TRUE then it must
be FALSE. They can be set when a user performs a certain action, i.e. runs a certain form, and
they can then be used to determine a certain course of action:
blnVariant = FALSE
If blnVariant = FALSE Then do this
...
Else do that
...
Special Subtypes
We should briefly outline some other subtypes here.
Empty
Empty variants are variables that have yet to be assigned a value (known as uninitialized
variables). Note that you shouldn't confuse these variables with variables that have been
assigned the value 0 (zero)! This is because 0 is a valid value (which also implies that the
variable is likely to be a numeric type or a Boolean FALSE), while an empty variable has no value
at all (because no value has yet been assigned to it).
NULL
NULL is an unusual subtype that is used in conjunction with databases, when we talk about fields
that contain no data. NULL does not mean 0, or empty, it means that there is no data, nothing.
The concept of NULL is a big stumbling block for the experienced programmer and novices alike.
There is also a set of things that NULLs are most commonly confused with. These are things that
most definitely are not NULL:
Zero
A blank string
A blank space
A string of length zero
None of these values are "nothing" – they are all "something". Since NULL has no data type or
value, it is only a NULL if it is nothing, contains no data, and no data type.
Object
The Object subtype refers to objects, which are block of codes, which can carry out jobs such as
accessing a database or keeping track of advertisements. Since objects are more complex than
simple data they are treated in a special way. We won't worry about this just yet, as we'll be
taking a closer look at them in Chapter 6.
Error
The Error subtype is very rarely used and for that reason we're not going to cover it. Occasionally
you'll come across a component or function that uses it, but we don't recommend using it in any
circumstances, as there are no conversion functions that allow you to create or convert a variant
with the Error subtype.
Determining Subtypes with TypeName()
So, if VBScript treats all these different subtypes as basically the same type, how can we find out
which variant is of which subtype? Sometimes it is useful for you, as the programmer, to know the
subtype of a variant, and we can find this out by using the VBScript function TypeName(). If you
feed a variant into the TypeName() function, as follows:
LengthOfAPieceOfString = 5
WhatTypeOfVar = TypeName(LengthOfAPieceOfString)
Then depending on what you placed in LengthOfAPieceOfString then one of the following
text descriptions of variant types is returned:
Empty
Null
Integer
Long Integer
Single
Double
Currency
Date
String
Object
Error
Boolean
Variant
Byte
Array
Note If some of these variant sub types are unfamiliar to you, don't worry: we'll be
looking at arrays later in this chapter, objects in Chapter 6 and Data Access
objects in Chapters 12 to 14.
We placed the number five in this example, so integer is returned. We'll now take a quick look at
how you can use TypeName() to return the subtype that VBScript is storing for a particular
variant.
How It Works
This example is very simple. We dimension four variables and assign them values with different
subtypes, and four corresponding variables to record the subtypes of the variables. We start by
declaring all of the variables we're going to use and then we allocate the value of pi, 3.142, to the
variable dblPi. Then we create a variable varWhatIsPi, which contains the TypeName value
(of type string) of the dblPi variable:
<%
Dim dblPi, varWhatIsPi, datToday, whatIsDate, strText, whatIsText
dblPi = 3.142
varWhatIsPi = TypeName(dblPi)
We then assign a variable datToday with today's date (provided by the VBScript Date function),
and then assign TypeName(datToday) to the variable whatIsDate:
datToday = Date
whatIsDate = TypeName(datToday)
We assign a string value to the variable strText, and then assign TypeName(strText) to the
variable whatIsText:
strText = "Hello World"
whatIsText = TypeName(strText)
When passed to the function TypeName, dblPi returns double, datToday returns date, strText
returns string and emp returns empty. This confirms the fact that when you allocate data to a
variable, VBScript also assigns a subtype to the variant. The subtype of the variant can be
determined by using the VBScript TypeName function.
Naming Variables
As we go, we'll look at the different types of variables, how to assign values to them, and how to
use them in expressions. We'll also talk about the kinds of names that you should give to your
variables. For example, while the variable names above reflect their contents in a relatively self-
explanatory way, the meanings of the variables in the following expressions are less obvious:
a = 1*x+73
varBoolean = true
They're not particularly helpful, are they? It's really up to the programmer to find a suitable name
for his variable when he creates it. Ideally, you should find a name that is meaningful to a
developer who subsequently reads your code. At the same time, excessively long variable names
are unwieldy and easy to mistype, so you should avoid these too. If the variable names are
chosen well, then the thinking behind the apparent gobbledygook in expressions like those above
will become clearer. It's a good idea to make variable names descriptive even if this means
making them longer. Here's some quick tips:
DateStart and DateEnd are better then StartDate and EndDate, as these two
related functions will then come next to each other in a search
Variables like Date, Price, Name and Number are confusing because there are usually
more than one of these, like NameFirst, NameLast, NameSpouse, PriceBuy, or
PriceSell
Avoid confusing abbreviations
Never use the same variable name for two different variables, no matter how sure you
are that there will not be a conflict
Within a procedure or function you may want to start names with a code letter like p_ or
f_
Create name with multiple words, the first letter of each word in uppercase
In most languages, the name of a variable can be almost anything you choose, but there are
usually a few restrictions:
There's usually a practical limit to the length of your variable names. In VBScript, the limit
is 255 characters – this should be more than ample for most people!
There are usually restrictions on which characters you can use in your variable names. In
VBScript there are restrictions, such as all variable names must begin with a letter, and
variable names must not contain an embedded period/full-stop. In fact you're better off
avoiding symbols altogether, other than dashes and underscores, to keep your code
readable and to guarantee it will work as intended.
Case-sensitivity is another important issue. VBScript is case-insensitive, which means
that you can use upper- and lower-case characters to refer to exactly the same variable. For
example, VBScript will interpret counter and COUNTER as one and the same. On the other
hand, JScript/JavaScript is case sensitive and would interpret counter and COUNTER as
two entirely different entities.
Naming Conventions
If we have many variants in a program, we need a way to keep track of which variants contain
which subtype. The fact that we can convert variants from one type to another makes this
'tracking' even more important. The sensible answer is to use a good naming convention. By
doing so, you can tell at a glance whether you're using an integer, a string, or date and can
manipulate it in a consistent way.
Naming conventions aren't compulsory, can't be enforced and generally it's up to the programmer
as to which convention to apply, but the most common one, known as Hungarian notation, is to
use the first three letters of a variant's name to distinguish the sub type. The fourth letter of the
variant is then typed in upper case, to indicate that this is where the actual variant name starts.
Here's the suggested naming convention: we'll be using it in our applications throughout the rest
of the book:
For example, the first line here declares a variable with the name CarType; the second line
assigns a string value to that variable:
Dim CarType
CarType = "Buick"
Declaring a variable in this way is known as explicit declaration, because we are explicitly telling
the computer what the name of our variable is, before we use it.
Many programming languages require you declare your variables explicitly, before you can use
them. This isn't strictly necessary in VBScript: indeed, you can just as easily assign a value to a
variable without having first declared it. This process is known as implicit declaration of
variables. As an example of implicit declaration, we can create another variable, CarType2, and
assign the value to it, without using the Dim command:
CarType2 = "Pontiac"
It's generally good practice to explicitly declare a variable before you use it. This will allow you to
keep track of all of the variables in VBScript that you've created. In fact, many programmers
declare all the variables they intend to use at the beginning of the code. It should help you to
avoid creating unnecessary variables, as you won't be creating them on the off chance that you
might need them later in the program. Further, you can use explicit declaration together with the
keywords Option Explicit to help debug your programs.
Here's an example of how Option Explicit is useful in keeping errors down. Consider the
following program:
Option Explicit
Dim intHeight, intWidth, intTotal
intHeight = 150
intWidth = 140
intTotal = (intHeight * (intWidth +40))/(intHeigth+intWidth)
In such a short code fragment, you can probably spot pretty quickly that we've misspelled
intHeight in the second usage on the last line. However, imagine that this fragment of code is
buried in a list of 100 variables, about 50 lines into the page. Would you be able to spot it so
quickly then?
With the Option Explicit line, this error would be detected immediately, when the script is
run, as an illegal implicit declaration. Without Option Explicit, the implicit declaration is
entirely legal, so your page would execute without an error. ASP will execute the page thinking
that in fact you wanted to create a intHeigth as a second variable, completely different to
intHeight. In fact, the only clue to the error is the value assigned to intTotal, which is
calculated with the value of intHeigth (i.e. 0) instead of intHeight (i.e. 150). Indeed, if you
didn't know what value to expect for intTotal, then you might not even notice the mistake at all!
When you place Option Explicit in a script, you must make sure it is the first line and not as
follows:
Dim intHeight, intWidth, intTotal
Option Explicit
intHeight = 150
intWidth = 140
intTotal = (intHeight * (intWidth +40))/(intHeigth+intWidth)
The position of Option Explicit in this script will actually cause a syntax error. Moreover, on
the server-side, in ASP, Option Explicit must come even before the first line of HTML:
<%Option Explicit%>
<HTML>
...
When you think about it, where else could it go? Scripts are run from top to bottom. If you want it
to check for Dim statements, you had better tell it to do that before it starts reading the script. In
ASP, the ASP is processed before the HTML, so it has to come before the HTML.
Arithmetic and Comparison Operators
Of course, variables aren't much use unless you can manipulate them in some way. In the
descriptions above, we have already seen one or two examples of basic data manipulation, but in
this section, we'll introduce the concepts more formally.
Assignment Operator
The familiar 'equal' sign (=) is probably the most common operator in computing. You've already
seen it used several times to assign values to our variables. The variable name goes on the left;
the variable value goes on the right:
Number1 = 2
VBScript doesn't enforce spaces either side of the 'equal' sign, but you may prefer to include
some to make your code easier to read.
Mathematical Peculiarities
You can also use the assignment operator to increase (or decrease) the value of variables using
the following, mathematically unsound, formula:
Number1 = 2
Number1 = Number1 + 1
Mathematicians will be scratching their heads, wondering how Number1 can be equal to
Number1 plus 1: it's similar to saying 2 = 2 +1, which is impossible. The answer is, of course, that
it can't. In this example, the equals sign takes on the role of an assignment operator, as it is
assigning a new value to Number1. It's a way of saying whatever the old value of Number1 is,
take that and add it to 1 and this value comprises the new value of Number1.
Comparison Operators
The comparison operators are used slightly differently. The comparison operators available in
VBScript are:
Equality = Inequality <>
Less than < Greater than >
Less than or equal to <= Greater than or equal to >=
We've just seen the 'equal' sign (=) in its guise as the assignment operator. In this case, the
'equal' sign is used as the equality operator, to test for equality:
If Number1 = 2 Then
This statement says, "If the value inside Number1 is already equal to 2 then (perform a certain
operation)". It depends upon the context in which the equals sign is used. If it's used on its own,
then it assigns one value to a variable, if it's used as part of an If ... Then statement, then it's
being used as a comparison operator. You can also use these operators to compare the values of
two operands – such as variables or expressions. The result of the comparison is a Boolean
value – that is, either TRUE or FALSE.
Note We'll be looking at exactly how the If ... Then structure works in the next
chapter.
Arithmetic Calculations
The arithmetic operations available in VBScript are:
Addition + Exponentiation ^
Subtraction - Negation -
Division /
Here is a very simple example: we'll assign values to the variables Number1 and Number2
before adding them together, and assigning the result to a third variable, Number3:
Number1 = 14
Number2 = 12
Number3 = Number1 + Number2
First, the computer evaluates the contents of the brackets, following normal mathematical
procedure: Number2 divided by 6 yields the result 3. This is added to the value of Number1, and
the result of this – 17 – is assigned to the variable Number3.
Even if this didn't generate an error, it certainly wouldn't hold the value 32. Let's have a go at a
quick example that performs a simple calculation.
OK, we've seen lots of theory, so now it's time to try out an example. We're going to perform a
simple calculation of tax. To do this we need to declare three variables: one for the earnings, one
for the tax percentage, and one for the total. We're going to deduct the earnings by whatever
percentage the tax rate is set at and display the final income in an ASP page.
1. Type the following program into your ASP editor:
2. <%Option Explicit%>
3. <HTML>
4. <HEAD>
5. <TITLE>Declaring Variables</TITLE>
6. </HEAD>
7. <BODY>
8. <%
9. Dim intEarn, intTax, intTotal
10. intEarn = 150
11. intTax = 20
12. intTotal = intEarn - ((intEarn/100)*intTax)
13. %>
14.
15. <B><P>Your total earnings after tax are $<% = intTotal %> </P></B>
16. </BODY>
17. </HTML>
18. Save it as taxcalc.asp in your inetpub\wwwroot\BegASPFiles folder.
19. Now start your browser and run taxcalc.asp.
Note Please note that all of the Try-It-Outs in this chapter are available on our
web site at http://www.wrox.com. You can either execute the
examples directly or download them from here, whichever you prefer.
How It Works
There are only six lines of ASP code in this program. The first, of course, is our Option
Explicit statement. The next declares three variables, intEarn for the earnings, intTax for
the tax rate and intTotal for our final amount:
Dim intEarn, intTax, intTotal
In the next line, we set the value of the earnings to 150, and the tax rate to 20:
intEarn = 150
intTax = 20
The intTotal variable is where the calculation is performed. To gain our percentage, we first
have to calculate what 20% of the earnings are: this is done by dividing the earnings by 100 and
multiplying the result by the tax rate. Brackets are used to indicate the order of calculation within
this expression:
intTotal = intEarn - ((intEarn/100)*intTax)
You could calculate tax deductions for any percentage rate and any earnings, by altering the
values of variables intEarn and intTax.
Logical Operators
There's also a set of logical operators you can use in your code:
AND
OR
NOT
Actually there are more than three logical operators, but they're only required in specialist
situations, so we won't be using them in this book. The logical operators are used in the same
way as comparison operators and also return a Boolean value:
If intNumber1 = 1 AND intNumber2 = 2 Then
They are used to determine a particular course of action. When using AND, both of these
conditions have to be TRUE for the condition to be fulfilled. This differs from OR where only one
out of the two conditions has to be TRUE for the condition to be fulfilled. If both conditions are
true, the condition will also be true
If Number1 = 1 OR Number2 = 2 Then
The third logical operator NOT, simply implies the reverse of the condition. If Number1 isn't equal
to 1 then the condition is fulfilled:
If NOT Number1=1 Then
There is a precedence for these logical operators in the way that they are calculated. This is:
NOT
AND
OR
Here the second part of the expression would be calculated first. You would check to see if
Number2 wasn't equal to one and Number3 wasn't equal to one, before going back and checking
to see if Number1 is equal to 1.
Concatenating Variables
It makes sense to add integers together, using expressions such as 2 + 3, or
Number1 + Number2 (as we showed above), but what happens if you wish to 'add' strings
together? It doesn't make much sense to add them in the arithmetic sense – "Beans" plus
"Rice" doesn't have a tangible meaning. However, VBScript allows us to 'add' strings together
in a different sense – using a process known as concatenation.
When two strings are concatenated, the second string is attached at the end of the first string,
creating a new string. In order to concatenate two strings we use the ampersand operator (&).
Let's run through a few examples. We can concatenate the strings "Helter" and "Skelter",
as follows:
strConcatenate = "Helter" & "Skelter"
Here, the result of the concatenation is the string "HelterSkelter", which will be assigned to
the variable strConcatenate, of type string. You should note that VBScript won't automatically
put in spaces or commas, though. You can also concatenate a number of strings within the same
expression. Here, we'll concatenate three strings, one of which is a space (which is also a string
since a space is a character):
strConcatenate = "Helter" & " " & "Skelter"
Now, strConcatenate will contain the string "Helter Skelter". You can concatenate as
many string variables as you like (within the maximum length of 255 characters in VBScript):
strFirst = "Never "
strLearline = strFirst & strFirst & strFirst & strFirst & strFirst
Then strLearLine will contain the line "Never Never Never Never Never ".
Comparing Variables with a String SubType
Although you can't add or subtract strings numerically, this doesn't account for the comparison
operators. These can be used to help compare and sort text into alphabetical order. So if you
wished to find out which came first in the alphabet aardvark or anteater, the following code could
be used:
strAnimal1 = "Aardvark"
strAnimal2 = "Anteater"
If strAnimal1 < strAnimal2 Then Response.Write "Aardvark"
If strAnimal2 < strAnimal1 Then Response.Write "Anteater"
It doesn't take a genius to figure out that the program will return Aardvark, however, if you were
comparing the contents of variables, which depend on input from users, then this can
alphabetically sort data for you without the need to refer to a database. We will look at sorting in
databases in detail in chapters 12 to 14.
Conversions
As we now know, when you assign a value to a variant, a subtype is also automatically assigned.
Sometimes, however, the assigned subtype is not the one that you intend, and in such cases, a
little force is required to explicitly convert the value into the type you actually wanted. VBScript
provides plenty of functions to do this for you. In fact, it provides so many of these functions that
you'll probably never use many of them. In a moment, we'll exercise the most common of these
functions in an example. First, here's a complete list of them:
Function Description
Abs Returns the absolute value of number.
Asc, AscB, Returns the ANSI character code of the first letter in a string. AscB is used
AscW on byte data while AscW is used on 32-bit platforms that use UNICODE
data (a format that falls outside the scope of this book).
Chr, ChrB, This is the opposite of Asc, and returns the character of a specific
ChrW character code. ChrB is used on byte data contained in a string while ChrW
is used on 32-bit platforms that use UNICODE data.
CBool Returns an expression that has been converted into a variant with the
subtype Boolean.
CByte Returns the variable that has been converted into a variant with the
subtype Byte.
CCur Returns the variable that has been converted into a variant with the
subtype Currency.
CDate Returns the variable that has been converted into a variant with the
subtype Date.
CDbl Returns the variable that has been converted into a variant with the
subtype Double.
CInt Returns the variable that has been converted into a variant with the
subtype Integer and rounds it up to the nearest whole number. When
rounding a number like 0.5, 1.5, 2.5 (where the rounding could be up or
down), Cint rounds to the nearest even number.
Function Description
CLng Returns the variable that has been converted into a variant with the
subtype Long.
CSng Returns the variable that has been converted into a variant with the
subtype Single.
CStr Returns the variable that has been converted into a variant with the
subtype String.
DateSerial Returns the variable that has been converted into a variant with the
subtype Date for given year, month, day. e.g. DateSerial (1999, 11,
1) would return 1st November 1999
DateValue Returns a value representing a variant of subtype Date.
TimeSerial Returns the variable that has been converted into a variant with the
subtype Date for given hour, minute, second.
TimeValue Returns a variant of subtype Date, representing the specified time. (Note
that the VBScript Date subtype can represent both date and time values.)
Hex Returns a string containing the hexadecimal value of a number.
Oct Returns a string containing the octal value of a number.
Fix Returns the integer portion of a number using truncation. e.g. 7.2 becomes
7 as does 7.8.
Int Also returns the integer portion of a number using truncation. e.g. 7.2
becomes 7 as does 7.8.
Sgn Returns the sign of a number, i.e. positive or negative.
Note If you assign a fraction or decimal value to an
integer, normally the integer is rounded up or
down to the next closest whole number. This
process is known as an implicit conversion.
These functions all work in much the same way. As a demonstration, we'll have a look at how to
use a couple of them now.
In this example we're going to take the value of pi=3.142, read it in as a string, convert to a single
data type, convert it into an integer and then finally convert it back to a string. We're also going to
display the value and subtype of each variant after the conversion.
1. Type the following program into your favorite editor:
2. <%Option Explicit%>
3. <HTML>
4. <HEAD>
5. <TITLE>Converting Variants</TITLE>
6. </HEAD>
7. <BODY>
8.
9. <%
10. Dim strPi, dblPi, intPi,strPi2
11. Dim varWhatIsPi1, varWhatIsPi2, varWhatIsPi3, varWhatIsPi4
12.
13. strPi = "3.142"
14. varWhatIsPi1 = TypeName(strPi)
15. dblPi = CDbl(strPi)
16. varWhatIsPi2 = TypeName(dblPi)
17. intPi = CInt(dblPi)
18. varWhatIsPi3 = TypeName(intPi)
19. strPi2 = CStr(intPi)
20. varWhatIsPi4 = TypeName(strPi2)
21. %>
22. <P><B>Pi is a <%= varWhatIsPi1 %> and Pi returns <%= strPi %>
</B></P>
23. <P><B>Pi is a <%= varWhatIsPi2 %> and Pi returns <%= dblPi %>
</B></P>
24. <P><B>Pi is a <%= varWhatIsPi3 %> and Pi returns <%= intPi %>
</B></P>
25. <P><B>Pi is a <%= varWhatIsPi4 %> and Pi returns <%= strPi2 %>
</B></P>
26. </BODY>
27. </HTML>
28. Save it as convert.asp.
29. Run the page on your preferred browser.
How It Works
This program explicitly declares all of the variants we will use and then assigns the variant strPi
a value of "3.142", which is a string. It then reads the subtype of this variant into another variant,
varWhatIsPi1:
<%
Dim strPi, dblPi, intPi,strPi2
Dim varWhatIsPi1, varWhatIsPi2, varWhatIsPi3, varWhatIsPi4
strPi = "3.142"
varWhatIsPi1 = TypeName(strPi)
Next, we declare three more variants; the value assigned to each is generated by converting the
subtype of the preceding one. Hence, the value assigned to dblPi is the conversion of strPi
from subtype string to subtype double. In addition, the TypeName of the variant dblPi is
assigned to varWhatIsPi2:
dblPi = CDbl(strPi)
varWhatIsPi2 = TypeName(dblPi)
The value assigned to intPi is the conversion of dblPi from subtype double to subtype integer.
The TypeName of intPi is assigned to varWhatIsPi3:
intPi = CInt(dblPi)
varWhatIsPi3 = TypeName(intPi)
Last, the value assigned to strPi2 is the conversion of intPi back to subtype string. The
TypeName of intPi is assigned to varWhatIsPi4:
strPi2 = CStr(intPi)
varWhatIsPi4 = TypeName(strPi2)
%>
The final lines simply display the subtype and value of each of these variants:
<P><B>Pi is a <%= varWhatIsPi1 %> and Pi returns <%= strPi %> </B></P>
<P><B>Pi is a <%= varWhatIsPi2 %> and Pi returns <%= dblPi %> </B></P>
<P><B>Pi is a <%= varWhatIsPi3 %> and Pi returns <%= intPi %> </B></P>
<P><B>Pi is a <%= varWhatIsPi4 %> and Pi returns <%= strPi2 %> </B></P>
Notice that because of the conversion from subtype double to subtype integer, the fractional part
of the value of pi is lost. Further, when you convert back to subtype string, you don't regain the
information! (This is because the final conversion is from type integer to subtype string: the value
to be converted is 3, not 3.142.) It's easy to lose information in this way, so you should be sure to
control your conversions carefully. Don't be put off, though: data conversions are a useful tool to
have, and they're not difficult to use.
Constants
There will be occasions when you want a value assigned to a variable to remain constant
throughout the execution of the code. A good example is statewide sales tax, this value will
rarely, if ever, change, yet when calculating the total of a shopping basket, you'll probably need to
refer to it several times. Even if the tax is changed, you'd still only need to refer to one value, and
you'd only need to update one value. To represent the sales tax you can you use something other
than a variable, namely a constant.
Constants are like variables except that, once they have been assigned a value, they don't
change. Many programming languages provide an explicit facility for constants, by allowing the
programmer to assign an initial value to the constant, and subsequently forbidding any alteration
of that value. The main reason you'd assign a value to a constant is to prevent accidental
alteration of that value.
VBScript supports constants with the Const keyword being used to define them. By convention,
constants are named in upper case:
Const ABSOLUTEZERO = -273
The change would be rejected and an error message would be produced. Constants remain in
force for the duration of the script, just like variables do, but no longer. While it isn't possible to
amend their value once it has been set, it is possible to set them to something different in the
page, when you run a page again. Constants make your code easier to read and maintain, as
they require less updating and if you choose a self-explanatory name, then they make your code
easily understandable.
VBScript Constants
VBScript also presents a list of its own type constants, with values that you can use but will be
unable to change. These constants correspond to the list of subtypes that we looked at earlier:
Constant Value
VbEmpty 0
VbNull 1
VbInteger 2
VbLong 3
VbSingle 4
VbDouble 5
VbCurrency 6
VbDate 7
VbString 8
VbObject 9
VbError 10
VbBoolean 11
VbVariant 12
VbDataObject 13
VbByte 17
VbArray 8192
You can use these constants in the same way that you use variants; each of them represents the
particular constant value indicated, although you cannot alter their values in any way.
There are literally hundreds of constants, which we can't cover here, and commonly they are
stored in a separate file and then included in a page when needed. We look at the concept of
using included information in chapter 10.
Variable Scope
When we mentioned some of the restrictions on naming variables at the beginning of this chapter,
we neglected to mention one rather odd requirement that you might have taken for granted. That
requirement is the following: that when you name a variable, it should have a unique name.
The reasons for avoiding this in the early stages of this chapter are the complications that arise
when a piece of code contains two variables of the same name, because under certain
circumstances it is allowed. This doesn't mean, contrary to what we said earlier, that this is a
practice you should be indulging in, because it still isn't. However, the idea that a variable need
not have influence over the whole contents of a page, but rather only directly over the part of a
page you want to use it for, is a powerful and confers performance enhancements. ASP only has
to track it for a short amount of time, rather than the whole lifetime of a page, it uses less memory
and when the procedure that uses the variable is over, then the memory is freed up.
Local Variables
On the surface, it seems to defy logical explanation, but I promise you that there is logic here:
let's look at it more closely. Consider two variables, both with the name strDifferent. We'll try
to assign different values to these two variables:
strDifferent = "Hello I'm variable one"
strDifferent = "Hello I'm variable two"
If you returned the value of strDifferent, you'd find that is contains the string "Hello I'm
variable two", thus overwriting the first value assigned to strDifferent. That's because
we haven't created two variables at all; we simply created one variable, strDifferent, and
then changed its value.
The exception to this rule is when we try using a similar structure in a Subprocedure. If you have
a program that performs the same task repeatedly, it is sometimes more efficient to write the
code that performs this task once, and access the same lines of code whenever the task is
required. This is where Subprocedures come in. Subprocedures hold blocks of code that can be
"called" from other parts of the program to run a specific set of actions whenever necessary. We'll
discuss Subprocedures (and Functions, which are a similar concept) in detail in Chapter 5.
If you defined strDifferent twice within two different procedures, then you'd see a different
result:
Sub Procedure_Number_1
strDifferent = "Hello I'm variable one"
End Sub
Sub Procedure_Number_2
strDifferent = "Hello I'm variable two"
End Sub
If you return the value of strDifferent in Procedure_Number_1, then you'd get "Hello I'm
variable one". If you return the value of strDifferent in Procedure_Number_2, you'd get
"Hello I'm variable two". However, if you then go back and run Procedure_Number_1
again, you'd get "Hello I'm variable one" again. Hence, they are effectively two different
variables, although they share the same name.
These variables are known as local variables (in the Microsoft documentation, they're called
procedure level variables), because they are local to the procedure that created them. Outside
the procedure, the local variable has no value: this is because the lifetime of the variable ends
when the procedure ends. As these local variables are only in effect for a lifetime of a procedure,
ASP doesn't have to worry about keeping track of them over the whole page. When a local
variable is created, it only exists while the procedure that invokes it is running; once the program
exits the procedure, the variable's lifetime is over, thus freeing memory and resources, increasing
performance. Also you cannot create a variable twice within its scope.
Now, let's take a look at how to use two local variables. To demonstrate that they are local we will
make them share the same name during our ASP program.
Sub Procedure_2
strDifferent = "Hi I'm strDifferent in Procedure 2"
Response.Write strdifferent
End Sub
%>
However, each of the variables called strDifferent is defined within the confines of its
respective procedure, and so doesn't exist outside the context of the procedure. If you asked the
ASP program to print the value of strDifferent from outside either of these procedures, it
would in fact return no value at all.
The lifetime of a script level variable begins at the start of the script (or scripts in one page) and
ends at the end of the script, and spans any procedures created within the script. In comparison,
local variables contained within a procedure are destroyed when the procedure is exited, and
hence the memory space is saved. It's a good idea to use local variables where possible,
because it makes things easier to read and helps to avoid bugs in code and above all improves
performance.
Now let's see how we can amend our previous program to include a script level variable:
Try It Out – Using Script Level Variables
We're going to simply add a new script level variable to this program and then display it from
inside and outside the procedures.
1. Load up the previous program, local.asp, in your preferred ASP editor and add the
following lines in the following locations:
2. <HTML>
3.
4. <HEAD>
5. <TITLE>Using Script Level Variables</TITLE>
6. </HEAD>
7.
8. <BODY BGCOLOR="white">
9. <%
10. strGlobal = "I'm a persistent script-level variable"
11. Response.Write strGlobal
12.
13. Sub Procedure_1
14. strDifferent = "Hi I'm strDifferent in Procedure 1"
15. Response.Write strdifferent
16. Response.Write "<P>" & strGlobal & "</P>"
17. End Sub
18.
19. Sub Procedure_2
20. strDifferent = "Hi I'm strDifferent in Procedure 2"
21. Response.Write strdifferent
22. Response.Write "<P>" & strGlobal & "</P>"
23. End Sub
24. %>
25.
26. <P>Calling Procedure 1 ...<I><%Procedure_1()%></I></P>
27. <P>Calling Procedure 2 ...<I><%Procedure_2()%></I></P>
28. <P>Calling Procedure 1 ...<I><%Procedure_1()%></I></P>
29. </BODY>
</HTML>
30. Save it this time as global.asp
31. Open this page on your browser.
How It Works
We've not changed our original program much, other than adding new text the sole addition is a
script level variable, strGlobal. This variable is assigned the text "I'm a persistent script-level
variable" and is then called at various points in the program:
strGlobal = "I'm a persistent script-level variable"
We first display this string from outside both procedures; the second time, from within procedure
1; the third time from within procedure 2; and the final time from within procedure 1 again. Each
time, the variable is displayed using the following code, so there's no trickery of any sort:
Response.Write "<P>" & strGlobal & "</P>"
The output shows how script level variables and local variables can be used side by side in any
ASP program
String Manipulation
Having outlined all of the different subtypes of variant available, let's take a look at some of the
functions you have available to you, which allow you to operate on the information contained
within. String manipulation is a completely new ball game that allows you to glean information
from text. For example, if a user's full home address were stored in a string, how would you go
about extracting just the house number and street name from the string, while removing all the
other extraneous information? Have no fear: VBScript provides a set of functions, which allow you
to chop, prune and order strings in any way you like. There are several functions provided; we'll
only look at the major ones. These are:
UCase and LCase
Len
Left
Right
Mid
InStr
LTrim, Rtrim and Trim
Note Note that if you are familiar with Visual Basic or VBA then you might be
familiar with version of the functions LTrim, RTrim and Trim, which are
LTrim$, RTrim$ and Trim$. However these variations do not work in
VBScript and therefore not in ASP either.
Changing the Case of a String
The two functions that allow you to alter the case of text are LCase and UCase. You can use
them in the following way:
LCase(string) or UCase(string)
So if you applied LCase to "Hello" it would become "hello" and if you applied UCase to "Hello"
then it would become "HELLO". If you applied either of these functions to a string that was
already in the specified case then it wouldn't have any effect. These functions simply alter the
case of text that is held in your variable, without actually altering the contents of the variable itself.
You could use these functions as follows:
strText="HeLlO"
Response.Write UCase(strText)
Response.Write LCase(strText)
This function only takes one argument, namely the string that you are measuring:
intHowLong = Len("HowLongIsAPieceOfString?")
In this example, there are 24 characters in this string, and so intHowLong is assigned the value
24. Any symbols, punctuation, and spaces within the string are valid characters, and therefore
they are counted in the length of the string. You can also supply a variable name to the Len
function; in this case, it will measure the contents of the named variable:
strText = "HowLongIsAPieceOfString?"
intHowLong = Len(strText)
In this example, the Len function would again return the value 24. Later on, we'll use this function
in an example that loops through a string, character by character – the function is used to tell us
when we've reached the end of the string.
And
Right(string, number_of_characters)
The Right function works in the same way, except that it extracts characters from the (right-
hand) end of the string:
strRightChars = Right("HowLongIsAPieceOfString?",7)
Here, the variable strRightChars is assigned the string "String?". Again, this function
supports the option of extracting characters from a string variant; and once again, symbols,
spaces, and punctuation count as valid characters.
Once again, the first parameter is the source string itself. The second parameter is the position of
the first character in the substring that you want to extract, and the third is the number of
characters in the substring. In our little example, we can isolate the word "Piece" by setting the
second parameter to 11 (corresponding to the letter "P") and the third parameter to 5 (indicating
the number of characters in the desired substring). The following example extracts the word
"Piece" from our original string, and assigns it to the variable strMiddleChars:
strText = "HowLongIsAPieceOfString?"
strMiddleChars = Mid(strText,11,5)
In our example, we can use it to isolate a particular word and return its position in a string.
strText = "HowLongIsAPieceOfString?"
intwhere = InStr(strText,"Long")
This example would return the number 4, as the word "Long" starts on the fourth character.
However, InStr is also case-sensitive, so if you searched for "long" instead of "Long", you
wouldn't find it at all. In this case InStr returns the value 0.
Giving it a Quick Trim
Finally, in this section, we'll have a look at the functions Trim, LTrim, and RTrim. These three
related functions give us three different ways to remove extraneous space characters from a
string. They're particularly useful when dealing with user input. Each function takes a single
parameter – a string – and they are written as follows:
Trim(string)
LTrim(string)
RTrim(string)
The function LTrim removes spaces from the left-hand side of a string, while RTrim (you
guessed it) removes spaces from the right-hand side of the string. The Trim function combines
the two, by removing all of the spaces from the beginning and end of the string.
As an example, let's consider the following string, which has three spaces at each end:
strSpace=" feeling kinda spaced out "
Would return the string "feeling kinda spaced out ". Meanwhile,
<%= Rtrim(strSpace) %>
Would return the string " feeling kinda spaced out"; and of course,
<%= Trim(strSpace) %>
This is quite a complex example. We're going to store a name, address, and date of birth in one
line, in the form of a string. We are then going to identify and break out a name, address, and
date of birth from our string separately Then we will return each item of information to the screen
on a new line as each one is 'cut out' of our string. We'll do this by assigning our one line with the
information to a variable, and then by detecting the occurrence of a 'bookmark' character in the
sentence, we can identify which is the name, address and date of birth. This kind of code can be
very useful if, for example, you were confronted with an address, and you might want to identify
each part separately and store it individually.
1. Start your editor and type in the following code:
2. <HTML>
3. <HEAD>
4. <TITLE>String Manipulation Example</TITLE>
5. </HEAD>
6.
7. <BODY >
8. <%
9. Dim strText, intEndOfName, strName, strTemp, intEndofAddress,
strAddress, strDOB
10. strText ="Vervain Delaware;42 Chelworth Gardens;1st October
1901"
11. intEndOfName = Instr(strText,";")
12. strName = mid(strText, 1, intEndofName-1)
13. strTemp = mid(strText, intEndofName+1, len(strText))
14.
15. intEndofAddress = Instr(strTemp,";")
16. strAddress = mid(strTemp, 1, intEndofAddress-1)
17. strDOB = mid(strTemp,intEndofAddress+1, len(strTemp))
18.
19. Response.Write "<BR>" & strName
20. Response.Write "<BR>" & strAddress
21. Response.Write "<BR>" & strDOB
22. %>
23. </BODY>
24. </HTML>
25. Save the file as string.asp.
26. Execute the file on your normal browser.
27. This program is very flexible, and will perform this operation on any sentence you feed in.
So, go back to the source code and change the line that assigns a sentence to the
variable strText to:
28. Dim strText, intEndOfName, strName, strTemp, intEndofAddress,
strAddress, strDOB
29. strText ="Sherlock Holmes;221B Baker Street;6th January 1854"
intEndofName = Instr(strText,";")
30. Now run the program again.
How It Works
Take a deep breath before we look at this one: there's a lot to absorb. The first line defines the
seven variables we'll need:
Dim strText, intEndOfName, strName, strTemp, intEndofAddress,
strAddress, strDOB
strText is used to store the whole line, while strName, strAddress and strDOB is used to
store the name, address and date of birth separately. intEndOfName is used to store the
position of the end of the name in our strText, and intEndOfAddress is used to store the end
point of the address. strTemp is used to store a temporary version of our line that we can
perform operations on.
We generate the position of the end of the name, by using the Instr function to find the first
occurrence of the semi-colon character. This is then stored in the intEndOfName variable, since
we know that this character is the first one to appear after our name:
intEndofName = Instr(strText,";")
Next, we know that we have encountered a semi-colon, so that everything that lies before it in
strText is part of the name. Now we can use the Mid function to extract the name.
strName = mid(strText, 1, intEndofName-1)
In this line we need to supply the Mid function with three parameters to do this, the first being the
text we are performing the operation on, the second being the character at which we start our
extraction, the third being the position of the character before the semi-colon (the one which is 1
character before it, or intEndofName-1). Obviously, we don't want to include the semi-colon, so
we have to go back to the position one before it in the text. We then store the result of the Mid
function in strName.
Next we populate a temporary variable, called strTemp, which contains everything in our data
line that comes after the name and semi-colon:
strTemp = mid(strText, intEndofName+1, len(strText))
To do this we again use the Mid function, but this time the parameters we supply are different.
The first is our data line once again; the second is the position of the semi-colon in our line, plus
one (indicating the first character to appear after the semicolon). The third parameter is the one
that will look a little strange to you. This is because we're nesting a function inside a function. If
you remember, the third parameter is the length of the string. We need to calculate the position of
the end of our string. We can do this by using the Len function to calculate the length of the string
strText and return it as the third parameter.
After that we're basically repeating the same process we've been through already. We use Instr
to search for the next semi-colon:
intEndofAddress = Instr(strTemp,";")
Hopefully, you can see the purpose of extracting the name from the first part of our string. This is
because when we use InStr again to find a semi-colon, we want the second occurrence of a
semi-colon. As Instr only returns the position of the first semi-colon, we need to have removed
the first occurrence. Next we can go on with the process of extracting the address:
strAddress = mid(strTemp, 1, intEndofAddress-1)
The same principle applies as with the name. Lastly, whatever we're left with must be the date of
birth. So we use the Mid function to copy the last part of the string, starting one character after the
position of our second semi-colon:
strDOB = mid(strTemp,intEndOfAddress+1, len(strTemp))
Once these three variables are stored then we can display the contents of our variables:
Response.Write "<BR>" & strName
Response.Write "<BR>" & strAddress
Response.Write "<BR>" & strDOB
The program continues, displaying each separate item of information in turn on the screen, until
we reach the end of the line. Of course this is slightly artificial – if we wanted to store more than
three items of information, then our program wouldn't be able to cope. To achieve this kind of
flexibility, we need to make use of loops and branching structures. We look at these in the next
chapter.
String manipulation functions are among some of the most useful features of VBScript. In order to
apply them properly, they can take a little thought. However, they can be put to some very
practical uses, such as identifying items of information within an address or a form, and being
able to save only the pieces that we want while discarding the rest.
Arrays
Variables are fine for storing individual items of data. However, they're not so good if you wish to
store many items of similar information. In this case, you'll need to use an array. Arrays are used
to store a series of related data items, which are related by an index number at the end. You
could use them to store the names of the Marx brothers, for instance:
strMarx(0) = "Groucho"
strMarx(1) = "Harpo"
strMarx(2) = "Chico"
strMarx(3) = "Zeppo"
strMarx(4) = "Gummo"
strMarx(5) = "Karl""
However, you don't have to store something in each item of the array, and you don't even have to
store it sequentially:
strHouse(1) = "Mr Jones"
strHouse(3) = "Mr Goldstein"
strHouse(4) = "Mrs Soprano"
Arrays are particularly useful if you want to manipulate a whole set of data items as though they
were all one item. For example, if you want to adjust the pay rates for a set of five employees,
then the difficult way is the following:
intExtraPay = 10
intEmployeePay(0) = intEmployeePay(0) + intExtraPay
intEmployeePay(1) = intEmployeePay(1) + intExtraPay
intEmployeePay(2) = intEmployeePay(2) + intExtraPay
intEmployeePay(3) = intEmployeePay(3) + intExtraPay
intEmployeePay(4) = intEmployeePay(4) + intExtraPay
The following much simpler code utilizes your array structure, and has exactly the same effect:
intExtraPay = 10
For intLoop = 0 to 4
intEmployeePay ( intLoop) = intEmployeePay(intLoop) + intExtraPay
Next
Arrays are much more versatile than plain regular variables when dealing with sets of data.
Note We will be discussing For ... Next loops in the next chapter.
Declaring Arrays
Arrays are declared in the same way as variables, using the Dim keyword. However, an array
declaration needs a parameter at the end, which is used to specify the size of the array. We could
set up an array to have 50 entries for each of the states in the US, with the following statement:
Dim StatesInUS(49)
The index number 49 isn't a mistake. It is just that arrays count from zero upwards in VBScript,
rather than from one. So in this case the 50 states are indexed by the 50 different parameter
values 0, 1, …, 49. This type of array is known as a fixed-size array, because you fix the
maximum number of items that the array can contain. If you don't know the number of items there
are to be in your array, or don't wish to specify, you can create a dynamic array instead:
Dim BritishAthlecticsWorldChampionshipWinners()
You can then go back and specify how many items there should be at a later point, using the
keyword Redim, which is short for re-dimension, although there is a performance penalty for
using Redim, so try to find the number if possible first.
Redim BritishAthleticsWorldChampionshipWinners(0)
Redeclaring Arrays
Sometimes there are situations when you've already specified the number of items in a dynamic
array, but then you have to amend that amount. If, for example, you'd estimated the number of
items but then found that you'd needed a larger array than first envisaged, you could use the
Redim keyword once again, to set up the array with the new amount:
Dim amoeba()
Redim amoeba(1)
amoeba(0) = "Geronimo"
amoeba(1) = "Geronimee"
'amoebas divide...
Redim amoeba(3)
However, you'd lose the information already held in the existing array, so you'd have to reenter
the information. Fortunately, help is at hand, in the form of another keyword, Preserve, which
can be used together with Redim, to ensure that the existing contents of your array are not
irretrievably lost:
Redim Preserve amoeba(3)
amoeba(2) = "Geronimo Mk II"
amoeba(3) = "Geronimee Mk II"
Now we're ready to a look a small example utilizing some of these concepts.
How It Works
This program starts by setting up a dynamic array strMarx, and then declaring that it contains
five items (in a zero-based array – hence the count from 0 to 4):
<%
Dim strMarx()
Redim strMarx(4)
We then populate our array with five values, the names of the Marx Brothers:
strMarx(0) = "Groucho"
strMarx(1) = "Harpo"
strMarx(2) = "Chico"
strMarx(3) = "Zeppo"
strMarx(4) = "Gummo"
To display the contents of our array, rather printing out each one individually, we have to use a
special construct known as a loop. We're going to look at loops in detail in the next chapter. For
the time all you need to know is that it is an automatically incrementing counter that sets the value
of intCounter to 0, in the first instance and runs the Response.Write line. Then the key
aspect of the loop is once it reaches the word Next, it goes back to the beginning of the loop and
runs the lines in between For and Next again, but this time setting intCounter to 1. It
continues this repetition until the value of intCounter reaches the end of the range assigned in
the For statement:
For intCounter = 0 to 4
In this way the loop increments the variable intCounter to go through each item in our array.
The loop goes through five values, 0, 1, 2, 3 and 4 and substitutes the value in intCounter on
each pass of the loop:
Response.Write strMarx(intCounter) & "... "
Next
The first item in the array, strMarx(0), contains the string "Groucho", and this is displayed on
the screen. The next statement tells the loop to go back and add another 1 to the value of
intCounter. This time strMarx(1) is displayed, which contains the string "Harpo", that we
assigned earlier. It's repeated for strMarx(2) and strMarx(3), and on the final iteration of the
loop, the value of 4 is assigned to intCounter which enables the contents of the variable
strMarx(4) to be displayed.
However, the program doesn't end there. Next, we display some more HTML, before going back
into ASP and resizing the array so that it may hold six items. The keyword Preserve ensures
that none of the original items are lost:
Response.Write "<P>Whoops! Nearly forgot the bearded leftish one...
</P>"
Redim Preserve strMarx(5)
Then we go around the loop again, outputting the contents of the array, except that this time the
loop is iterated six times, not five:
For intCounter = 0 to 5
Response.Write strMarx(intCounter) & "... "
Next
If we were to omit the keyword Preserve when resizing the dynamic array, we would lose the
names of the five original Marx brothers, so the array would be empty again before adding their
political namesake. Try running the program again without the word Preserve, just to see.
Multi-Dimensional Arrays
If you need to keep information of a two-dimensional nature, then you can do it by declaring a
two-dimensional array. For instance, if you wanted to store a set of related information separately
such as a first and last name, a normal array would probably be unsuitable. Instead, you achieve
it by adding another parameter to your array declaration. Thus:
Dim str2Darray(3,3)
Would set up a two-dimensional array of size 4 by 4, which could hold a total of 16 values. You
can assign values to a multi-dimensional array by referencing each element of the array through
its two-value index. For example, you could use such an array to store first and last names and
phone number:
str2Darray(0,0) = "John"
str2Darray(1,0) = "Doe"
str2Darray(2,0) = "111-111-1111"
str2Darray(0,1) = "Jane"
str2Darray(1,1) = "Doe"
str2Darray(2,1) = "222-222-2222"
The first dimension stores the information that is related to one person, such as first, last name
and phone number, while the second dimension holds data of the same type for different people,
such as a set of phone numbers. In fact, VBScript is not limited to arrays of one or two
dimensions; you can declare an array with up to 60 dimensions, should you so require. However
using anything more than three dimensions is impractical and you'd be better off pursuing other
solutions such as databases if you require more than three. This takes us as far as we need to go
with arrays and variables, we'll be using them throughout the rest of this book, so take care to
understand them.
Summary
In this chapter, we have considered how we can go about storing the data collected by the
computer, both from the user and from its own calculations. We looked at what a variable is and
how VBScript treats each of the different subtypes of data as just one type, the variant. We
looked at how you would go about declaring variables, and how the Option Explicit
commands can be used to lessen the chance of making a typing mistake in the variable name.
We then looked at various functions that VBScript provides to manipulate variables.
We looked at why certain variables are only effective within certain scopes. We also considered
why some variables keep their value throughout the whole script, while others keep their values
only within the procedures that created them.
Finally, we looked at how you could store a series of related data items with an array, and
referring to the elements of the array using an index. We looked at how you can size and
populate arrays, and we finished off by looking briefly, at how you could make them multi-
dimensional.
In the next chapter, we'll expand on a number of very useful programming tools, the control
structures
This chapter covers the three ways that you can use VBScript with your ASP scripts to sequence
the execution of your lines of code. Respectively, these are:
Deciding which of two or more sections of code to run.
Repeating a section of code as many times as needed.
Jumping out of the code sequence and executing sections of code in another part of your
script.
Note Note that this chapter refers to using VBScript with ASP, and hence some
of the syntax shown in the following examples is specific to VBScript. If
you were to try and use, for example, JavaScript, the structure of the
examples would have to be changed appropriately. If you want to know
more about JavaScript, you may be interested in Beginning JavaScript
(Wrox, ISBN 1-861004-06-0)
Before we look at these processes in detail, let's step back and think in more general terms for a
moment
An Example in Plain English
Before we begin to look at ASP code let's think about these ideas in simple English for a business
situation that you can easily picture in your mind. Let's consider the type of instructions a
mechanic might be given by their boss. The code you have written so far is similar to the
mechanic's boss telling them to:
Take truck number one to the fuel pump.
Fill up the tank with gasoline.
Bring the truck back to its parking spot.
Although these instructions are very clear and easy to follow in order of line one, line two, line
three, they do not cover some possible problems. What if the truck is already full of gasoline?
What if the truck needs oil? What about providing service for the other trucks?
In order to get the job done the boss needs to provide a more complex set of instructions, for
example:
1. Check if truck number one needs gasoline.
2. If truck number one needs gasoline, then take it to the fuel pump and fill it.
3. If the truck does not need gasoline then skip the filling and go to the next step.
4. Check if truck number one needs oil.
5. If truck number one needs oil, then get out the truck's manual and follow the instructions
for adding oil. After you are done adding oil come back and finish these instructions.
6. If the truck does not need oil then skip the oil step and go to the next step.
7. When truck one is full of gas and full of oil, bring it back to its parking spot.
8. When you are done with truck one, repeat the above steps for the rest of the trucks.
The second set of orders will work better for three reasons. First, it covers a range of possibilities
such as the gasoline and/or oil not needing refilling. Second, it takes advantage of existing
instructions – the truck manual's pages for how to add oil. Third, it gives sufficient instructions to
get the job done on the whole fleet of trucks.
This simple example illustrates the three types of constructs that you will use as an ASP
programmer:
Branching statements such as lines 2 and 3: If the truck needs gasoline then fill it up,
but if it does not need gasoline then skip the filling up step.
Jumping statements such as line 5: if you need oil, then stop executing these
instructions and instead follow the instructions for adding oil. When you have finished
following those adding oil instructions, then come back and continue with these instructions.
Looping statements such as line 8: when you are done with truck one, go back and
repeat these same steps for truck two, then truck three and so on until all of the trucks have
been serviced.
In our Motorpool example, instructions such as "Fill the tank with gasoline" and "Add the oil" are
action statements. The rest are control statements, such as "If the truck needs gasoline then..."
or "Repeat the above steps for the next truck."
This idea of controlling which lines of code execute in what order, and how often, has several
names. Some people refer to these techniques as flow control or execution order, and the sets
of code that we use to control the flow of execution are called, logically enough, Control
Structures.
Definitions
There are several new terms introduced in this chapter. Be particularly careful since some of
these are interchangeable: for example, most programmers consider the terms line, code,
command and statement to mean just about the same thing – that is, one line of programming
instruction. Normally, however, code refers to one or more instructions.
Branching Structures
Branching controls perform some type of test. Based on the test results, a set of code will be
executed and other sets of code will be skipped. From our Motorpool example, this is like testing
if the gasoline tank is full. If it is not full then we perform the steps to fill it. If the tank is full then
we skip the filling steps and move on to the next lines of code.
Jumping Controls
Jumping controls allow the programmer to pause the execution of the current code and jump to
another named block of code. For example, we may have written a block of code called
ShowOrder that produces lines that show the customer the goods that they ordered. Whenever
we want VBScript to show those lines we don't have to re-write or copy all of that code. Instead
we just have VBScript jump out of our current code, execute ShowOrder and then come back
and continue executing our original bit of code. There are two types of jumping controls:
Subprocedures can be called with the Call keyword, or alternatively you can just provide
the name of the subprocedure, which will run the statements in the subprocedure, and then
return control to the main procedure.
Functions can be used to execute some statements and return an answer to the main
body of code. They can also be preceded by the keyword Call.
I want ASP to show page A or Branching We want to perform only one of two
page B. possible events.
I want ASP to list each member Looping We will be performing the same set of
of the club. The data about code (that retrieves a member's name)
each member is held in many times (once for each member, until
essentially the same manner, we list all members).
with a name, photo, address
and other contact information.
I want to build a table. Looping We will perform the same code (make a
row for a table) again and again until we
have built all of the rows needed.
I need to calculate prices in Call using a We will pause building the page, execute
Situation Solution Why?
several places on each page. function code to calculate the price of an item,
The prices will be set according then return to building the page. Since
to input from a user form. we will calculate many prices it is best to
write the formula once and have it called
when needed.
I need to show the user which Branching We want to write to the page only one
of several meetings they out of several possible meeting
should attend. The meeting locations.
displayed is based on which
department they belong to.
After every item that I describe Call using a We want to pause the main code and
in a catalog page, I want to put subprocedure perform several lines of another set of
in a few lines of information code that describes 'How to Order'. Then
about 'How to Order'. we want to resume the main code. Since
the 'How to Order' set of code will be
performed at various times across the
page, it is best to write it once and call
that one piece of code as needed.
Let's recap on what we've discussed so far. There are three kinds of statements that control the
flow of our code's execution:
Branching statements that perform a test and then execute some lines of code but not
others
Looping statements that execute a set of code again and again
Jumping statements that pause the execution of the current code, jump over to another
set of code, and then return
Now let's have a closer look at branching statements, and what we can do with them.
Branching Statements
ASP offers two techniques for branching. If...Then is used when there are only a few choices of
outcome. Bear in mind that the more lines you use in If...Then, the more difficult your code can
become to follow. Select...Case is used when there are a lot of outcomes.
For example, if you are asking the user "Do you want a confirmation by telephone?" the outcome
is either Yes (True) or No (False), so you would perform the branch using If...Then. But if you
ask the user "Do you want confirmation by telephone or Fax or FedEx or E-mail or Voicemail or
Telepathy?" given the amount of outcomes, then it is probably better to use Select...Case. We
will start with If...Then.
The first part is the expression, which can be a combination of keywords, operators, variables,
and constants that yield a result as a string, number, or object. We came across them first in the
last chapter. It must answer either true or false. If the test answers true, then the lines of code in
the "if true" section are executed. If the test answers false, then the lines of code in the "if false"
section are executed. After the 'true' or 'false' section is executed the execution jumps down to
the ending statement and continues with the next line of code. There is never a situation where
both the true and the false sections would be used in a given case.
There are four ways of building If...Then statements. To select the proper syntax you must
answer two questions:
Do I want to do anything if the test is false?
Do I want to execute more than one statement if the test is true?
The first and most simple way is used if you only have one statement to perform in the case of a
'true' test. You want to execute no statements at all if the test is false. For example, If
varFaxConfirm = "Yes", then you want to print the fax number. If the expression evaluates to
'no' then you don't want to do anything. In this most simple case, you can use the one-line syntax:
<%
If varFaxConfirm = "Yes" then Response.Write "Please enter your fax
number."
%>
The next, more complex level, is where you want to perform more than one statement in the case
of truth, but still nothing if the test is false. For example, if varFaxConfirm = "Yes", then
ask for the fax number and jump over to the fax entry page. In this case we must write the
If...Then with two changes from case one above: the statements must go on their own lines, not
the same as the simple If...Then example above. And, since there is now more than one line for
the If...Then code, we must use a closing line of End If:
<%
If varFaxConfirm = "Yes" Then
Response.Write ("Please click below and provide your fax number.")
Response.Write "<A HREF=http://www.On-LineClothier.com/FaxForm>Click
here</A>"
End If
%>
The third level is where you want to perform more than one statement in the case of 'true', and
also one or more lines of code if the test is false. For example, if varFaxConfirm = "Yes"
then ask for the fax number and jump over to the fax entry page. If varFaxConfirm is anything
other than "Yes" then show a line that says that a fax will not be sent. In this situation we must
write the If...Then with a line called Else to separate the code that is run in the true case from
the code that will run in the false case.
<%
If varFaxConfirm = "Yes" then
Response.Write "Please enter your fax number."
Else
Response.Write "No fax confirmation will be sent."
End If
%>
There is a fourth level, which we don't recommend using unless you have a specific set of
constraints. It allows you to continually nest statements using ElseIf. To use ElseIf, you need
to separate each new case with the keyword ElseIf (one word), closing the condition as
normally with End If. For example, it can be structured a bit like this:
<%
If varConfirm = "Fax" then
Response.Write "Please enter your fax number."
ElseIf varConfirm = "Email" then
Response.Write "Please enter your email address"
ElseIf varConfirm = "Voicemail" then
Response.Write "Please enter your voice mail number"
Else
Response.Write "No confirmation will be sent."
End If
%>
Here we test the data to see if it meets condition 1; if it doesn't, we test it to see if it meets
condition 2; if it doesn't, we test it to see if it meets condition 3, and so on. When our data meets
one of the criteria, or the criteria isn't met, the branch is decided and we arrive at a suitable
outcome.
There is an alternative structure that provides a simpler solution to this same problem, and we'll
be looking at this shortly.
End If
End If
ElseIf
strAge>18 And
strAge<65 Then
Response.Write
"The fee for
this service
is $59."
End If
Important When using the one-line form of If...Then you do NOT use
End If. When using any multi-line form of If...Then you MUST
use End If
So let's now look at an example where you're responsible for notifying your colleagues of the date
and location for the Corporate Spring Retreats that we introduced in Chapter 3.
50. If you type in March and West and submit your query, you'd come up with the following:
How It Works
The response page is where we use the control structure If...Then. The page starts with the
basic <HEAD> tags, then immediately starts the ASP VBScript code. The first job is to pass the
information typed by the user into variables named varMonthPref and varLocation using the
technique covered in the last chapter.
<%
varMonthPref = Request.Form("MonthPref")
varLocation = Request.Form("Location")
The first control structure checks the contents of varMonthPref to see if it contains the word
"March". Since that is a proper expression, it can be answered with a true or false. If it is true then
two things happen:
The true section of code is executed to put " …March 15th" onto the HTML page
VBScript skips the false line of code ("…April 16th") and jumps down to after the End If
If the expression is false (if the value entered isn't "March" we assume it to be "April") then
VBScript will do the following:
Skip the true section of code ("…March 15th")
Perform the false section of code ("…April 16th") and continue on down through the End
If
If varMonthPref="March" Then
Response.Write "Your meeting will be held on March 15th "
Else
Response.Write "Your meeting will be held on April 16th "
End If
The second If...Then works the same way. If it is true that varLocation is "East" then
VBScript executes the writing of the city – Myerstown, Pennsylvania – and then jumps down to
the line after End If. If VarLocation="East" is false we assume that the user typed "West"
and so we skip the Myerstown line and go to the Malibu line.
If varLocation="East" Then
Response.Write "in Myerstown, Pennsylvania"
Else
Response.Write "in Malibu, California"
End If
Select Case
One problem of If…Then is that it can start getting unwieldy after more than two possible
outcomes. What happens if you want to show a different page to visitors from each of five
departments? What happens if you want to do a calculation based on the user providing one of
twelve salary grades? What happens if you have different procedures for confirming an order by
telephone, fax or E-mail? Your code is going to start looking pretty messy with all of those
ElseIfs. Select Case is the alternative control structure we mentioned earlier for handling
branching and it caters much more neatly for these type of situations, by providing a better
structure and extra readability: anytime you need to make a choice among several answers (more
than just True and False) then use Select Case.
The first example, below, carries out one of three actions depending on what is contained in the
variable varConfirmation.
<%
Select Case varConfirmation
Case "Fax"
Response.Write "<A HREF='FaxConfirmation.asp'>Fax</A>"
Case "Telephone"
Response.Write "<A HREF='telephone.asp'>Telephone</A>"
Case "EMail"
Response.Write "<A HREF='EMail.asp'>Email</A>"
End Select
%>
VBScript will know from the first line that you want to compare answers to the contents of the
variable varConfirmation. Next, VBScript will begin testing the contents of the variable
against the values shown in the Case lines. When VBScript finds a match it will execute the
following code up to the next Case line, and will then jump down to the first line after the End
Select statement. You may have noticed in the last Try It Out that if the user typed "march" in
lower case then the program failed: it displayed April on the Spring Retreat Response page. This
was because we only coded for the possibility of the user typing "March", and printed April in all
other cases. You can get by this by accounting for more possibilities, for example march, March,
april, April. You can even include a "catch-all" case – for example, what if the user decided to
type Saturday? VBScript allows you to write a special test called Case Else, which will run some
code if all other conditions fail. For example:
<%
Select Case varMonthPref
Case "march"
Response.Write "Your meeting will be held on March 15th "
Case "March"
Response.Write "Your meeting will be held on March 15th "
Case "april"
Response.Write "Your meeting will be held on April 16th "
Case "April"
Response.Write "Your meeting will be held on April 16th "
Case Else
Response.Write "your request for " & varMonthPref
Response.Write " is not recognized."
Response.Write "Please click the back button on your browser "
Response.Write "and reset then refill the form again.<BR>"
End Select
%>
In this example, the user who decided to type Saturday will receive our own custom error
message, telling them to try again.
We can further refine this code by having VBScript test for more than one result on one case line.
As an example, for both "march" and "March" we would do the same action
(Response.Write "...March 15th"). This syntax is simple, just line up the possible answers
separated by commas as demonstrated in the code below:
Select Case varMonthPref
Case "march", "March", "mar", "Mar", "MAR"
Response.Write "Your meeting will be held on March 15th "
Case "april", "April", "apr", "Apr", "APR"
Response.Write "Your meeting will be held on April 16th "
Case Else
Response.Write "your request for " & varMonthPref
Response.Write " is not recognized."
Response.Write "Please strike the back button on your browser "
Response.Write "and reset then refill the form again.<BR>"
End Select
This will work fine, but what if you wanted to test for many different possible inputs? You could
end up with having case statements to catch several kinds of input (mar, Mar, march, March,
MARCH, etc). VBScript offers a completely different way to solve the case problem. If you change
all input text to uppercase before testing, you can reduce the number of tests needed. The Ucase
() function will convert a string to all uppercase as follows:
Select Case Ucase(varMonthPref)
Case "MARCH", "MAR"
Response.Write "Your meeting will be held on March 15th "
Case "APRIL", "APR"
Response.Write "Your meeting will be held on April 16th "
Case Else
Response.Write "your request for " & varMonthPref
Response.Write " is not recognized."
Response.Write "Please strike the back button on your browser "
Response.Write "and reset then refill the form again.<BR>"
End Select
So, we've managed to cut down the number of test statements that we need to worry about.
However, in some situations, it may become very difficult to account for all the possible answers
the user may try to type into the entry field. One alternative is to provide the user with a fixed set
of options to choose from, and we can do just that with a special type of input box called a 'drop-
down' or 'combo' box. These appear in input forms as follows:
<SELECT NAME="dropbox">
<OPTION VALUE="Option1">One</OPTION>
<OPTION VALUE="Option2">Two</OPTION>
<OPTION VALUE="Option3">Three</OPTION>
<OPTION VALUE="Option4">Four</OPTION>
</SELECT>
By including this on a simple form page, within the <FORM> and </FORM> tags, this would
produce a box that looks like this:
The NAME attribute of the select tag contains the name of the box – this name is used in the same
way as we have seen with the text input boxes we've been using so far. What you may notice is
that the text displayed can be different to the data that gets used on a result page. In this
example, selecting One from the drop box will actually select the value Option1, so if we were
using our Select Case structure, you could use the following in a result page:
varDropBox = Request.Form("dropbox")
In this way, you can have a more detailed list of options for your users to choose from, but a
simplified list of values that can be used with our Select Case statements
This next Try It Out will use a drop-down box to reply to two questions, and then we'll use
Select Case to display appropriate results.
We will modify the two pages we used in the If...Then example to use the Select Case
structure
1. Open up IfThenOneForm.asp and change the following highlighted line:
2. <HTML>
3. <HEAD>
4. <TITLE>Spring Retreat Form</TITLE>
5. </HEAD>
6.
7. <BODY>
8. <H1>Corporate Retreat Registration</H1>
9. <H3>To get the logistics information for your meeting please
answer these two questions.</H3>
10. <FORM ACTION="SelectCaseResponse.asp" METHOD="post">
11. Which month would you prefer?<BR>
12. <SELECT NAME="MonthPref">
13. <OPTION VALUE="Jan">January</OPTION>
14. <OPTION VALUE="Mar">March</OPTION>
15. <OPTION VALUE="May">May</OPTION>
16. <OPTION VALUE="Jul">July</OPTION>
17. <OPTION VALUE="Sep">September</OPTION>
18. <OPTION VALUE="Nov">November</OPTION>
19. </SELECT>
20. <BR><BR>
21. What is your preferred location?<BR>
22. <SELECT NAME="Location">
23. <OPTION VALUE="East">East</OPTION>
24. <OPTION VALUE="West">West</OPTION>
25. </SELECT>
26. <BR><BR>
27. <INPUT TYPE="submit">
28. <INPUT TYPE="reset">
29. </FORM>
30. </BODY>
</HTML>
31. Save this as SelectCaseForm.asp
32. Open up the page IfThenOneResponse.asp and change the following highlighted
lines:
33. <HTML>
34. <HEAD>
35. <TITLE>Corporate Retreat Response</TITLE>
36. </HEAD>
37. <BODY>
38. <%
39. varMonthPref = Request.Form("MonthPref")
40. varLocation = Request.Form("location")
41. Response.Write "<H1>Corporate Retreat Registration <BR> Your
Details</H1>"
42. Select Case varMonthPref
43. Case "Jan"
44. Response.Write "Your meeting will be held on January 14th "
45. Case "Mar"
46. Response.Write "Your meeting will be held on March 15th "
47. Case "May"
48. Response.Write "Your meeting will be held on May 16th "
49. Case "Jul"
50. Response.Write "Your meeting will be held on July 17th "
51. Case "Sep"
52. Response.Write "Your meeting will be held on September 18th
"
53. Case "Nov"
54. Response.Write "Your meeting will be held on November 19th "
55. End Select
56.
57. If varLocation="East" then
58. Response.Write "in Myerstown, Pennsylvania"
59. Else
60. Response.Write "in Malibu, California"
61. End If
62. %>
63. </BODY>
</HTML>
64. Save this as SelectCaseResponse.asp
65. Now if you open SelectCaseForm.asp in your browser, you'll find that the example
prevents our user from typing in variations of the same month (march, MARCH, March
etc.) or location, by supplying them with a list to choose from. After submitting the
information, the user will see the corresponding result.
How It Works
The essential change is the replacement of an If...Then structure with a Select Case
structure. When VBScript reads the Select Case it knows that ensuing tests will be against
varMonthPref. The first possible answer it tries is "Jan" and if that is a match then it does the
first Response.Write line, otherwise, it moves on to the next comparison which is for "Mar". If
this does not produce a match, then VBScript will drop down to the next Case line and try "May"
and so on.
Note Easy mistakes: Since this form and response are similar to the last Try It Out
be careful not to accidentally call the wrong one up in you browser when
testing. If you copied the last Try It Out response page and modified it here, be
sure you changed the <FORM> ACTION attribute.
Fortunately, one last trick of Select Case allows you to save your fingers. You can create
Boolean expression in the Case part of the expression and depending on whether it evaluates to
true or false when supplied with a value, it will choose one of the cases.
Select Case True
Case varDatePref <= 15
Response.Write "Your meeting will be held on March 15th "
Case varDatePref >15
Response.Write "Your meeting will be held on March 30th "
...
End Select
However, you must make sure that it is only possible for one of your expressions at any one time
to evaluate to true.
We've covered a lot of ground so far in looking at branching controls. Now we'll take a look at our
next type of controls: looping controls.
Looping Controls
ASP has two types of looping structures: Do While and For...Next. When you require one of
these structures, it's advisable that you decide which structure to use, depending on whether you
know in advance how many loops you want to do. If you can determine the number of loops and
have this number stored in a variable, then use For...Next. If you do not know ahead of time
how many loops you want to do and will have to decide after each loop whether to continue, then
I suggest using Do While.
For…Next
The For...Next structure has three parts. The first is a line that describes how many times to
repeat the loop. Second come a set of lines with action statements that carry out the task you
want repeated. Last is a line that indicates the end of the action statements and tells ASP to go
back and repeat the action statements again.
Here is a simple case to get started: imagine that we have a sales rep. on the road that wants to
print a sign-in sheet for attendees to seminars. The sales rep. would like to log in to the
company's web site, get the sign-in sheet, and print it. In this first case we know that the seminars
always have five attendees:
<%
Response.Write "Sign-In sheet for Attendees"
Response.Write "On-Line Clothing Store - Fashions of Spring<BR><BR>"
For varCounter = 1 to 5
Response.Write "Attendee Name ___________________ <BR><BR>"
Response.Write "Attendee EMail __________________ <BR><BR><HR><BR>"
Next
%>
VBScript will execute the first two lines to display some introductory text for the sheet – this part is
not inside the loop. On the third line, we begin the process of running the loop five times. In order
to keep count we provide a variable called varCounter for VBScript to keep its count in. Here
the variable is implicitly declared within the loop, and doesn’t have to be declared prior to this.
The lines that will be repeated five times are all of the lines in between (but not including) the
For... line and the Next line. In this case, the two Response.Write statements create a line
for an attendee to write their name and e-mail address.
That simple first example assumed that we would always have five attendees. What if that
number varies? We would like to have a first web page that asks for the number of attendees,
and then use that number to determine how many lines to print. We can use the For Next
(instead of Do While) because when we start the loop, the loop structure is fed the number of
loops to execute.
If we assume that the text field called NumberAttendees contains the number of attendees
input by the user, then the responding page (below) can grab the user's data from the field named
NumberAttendees and stuff the data into the variable named varNumber. Then we can begin
the For...Next loop. But this time we don't go up through exactly five cycles. Rather, we go
through the number of cycles specified in the varNumber. Our sign-in sheet is now usable for
any number of attendees:
<HTML>
<HEAD>
<TITLE>Example For Next Two Response</TITLE>
</HEAD>
<BODY>
<H2>Welcome to the Seminar<BR>Please Sign in below</H2><BR>
<%
varNumber=Request.Form("NumberAttendees")
For varLineCounter = 1 to varNumber
Response.Write "Attendee Name ___________________ <BR><BR>"
Response.Write "Attendee EMail __________________ <BR><BR><HR><BR>"
Next
%>
</BODY>
</HTML>
48. Click on Submit and you will see an entry on the Response page for each day:
How It Works
The form page is nothing new to you, but we have started slimming the code down to fewer lines
so that you get used to seeing some good coding practices.
Note Note that we have specified the Action= ForNextOneResponse.asp. If you
are copying pages from earlier Try It Outs be sure you update this line.
We have two pieces of user data going back to the server: the first and last dates of the week
we're interested in. This data is used to stuff the variables that ASP will use to determine how
many times to loop.
The response page actually uses the For...Next loop. The first few lines receive the first and last
data from the browser. The data coming from a form's text input type will always be text, but we
want to work with dates. So we convert the text by saying the new value of varStart will be
equal to the result of doing a CDate conversion on the old value. This means ASP will read the
contents of varStart, take that text and convert it into a real date, then use that real date to
replace the text in varStart. The initial loading of the variables is on the first two lines below,
and the conversion from text to true dates is on the final two lines:
varStart=Request.Form("start")
varEnd=Request.Form("end")
varStart = cdate(varStart)
varEnd = cdate(varEnd)
Then we calculate the number of days by subtracting the start date from the end date as below.
varNumberDays=(varEnd-varStart)
Then we can begin our loop. We need a counter, which we called varLineCounter, and we say
"run the loop starting with the counter set at zero, and stop when the counter gets to the value
held in varNumberDays (in this case, the value is 4)". The lines of code to be executed on each
cycle are simple: write the word Clients, a few underscores, and then some line breaks. The
last line, Next, indicates to ASP when to go back and repeat the cycle.
For varLineCounter = 0 to varNumberDays
Response.Write "Clients: ___________________"
Response.Write "<BR><BR><BR>"
Next
A Few Improvements
We can tighten the first four lines by putting the conversion of text to dates in two commands as
follows:
varStart=cdate(Request.Form("start"))
varEnd=cdate(Request.Form("end"))
A further improvement would be to number the days by actually printing the varLineCounter
(taking note to increment the day by one):
For varLineCounter = 0 to varNumberDays
Response.Write "Day " & varLineCounter+1
Response.Write " Clients: ___________________"
Response.Write "<BR><BR><BR>"
Next
More useful would be to print the actual date such as 09/12/99 at the beginning of the line. In the
code below we add a line to the loop, which creates a new variable called varDateThisLine.
Into that variable we stuff the result of a calculation which takes the start date and adds to it the
number of the varLineCounter, that is, the number of the cycle we are on.
For varLineCounter = 0 to varNumberDays
varDateThisLine = varStart + varLineCounter
Response.Write varDateThisLine
Response.Write " Clients: ___________________"
Response.Write "<BR><BR><BR>"
Next
It looks almost identical to For...Next, doesn't it? The only difference is that you don't have to
specify the number of items you want to loop through, VBScript will simply start with the first item
in the array and repeat the loop until it reaches the last item in the array. In this case, each item in
the array is listed on the screen, so, if there were one hundred cities in our array, each one would
be displayed by this loop. The For Each…Next statement is also used to loop through the
contents of collections of ASP objects. We'll look into this in more detail in the next four or five
chapters. For the moment, we'll take a closer look at another looping control: the Do...While
loop.
Do…While
We briefly mentioned the Do...While loop earlier in the chapter: you may remember that it's used
to repeat a loop when we are not sure how many loops to perform. So on each loop it performs a
test and continues looping as long as a specified condition exists (you can also use the construct
Do...While NOT, i.e. loop while a specified condition does not exist). The Do...While loops and
For...Next loops have two significant differences in syntax. First is the difference in key words:
For...Next has only one word (For) in the opening statement and ends with the word Next.
Do...While has two words in the opening statement (Do...While) and ends with the word Loop.
The code between Do...While and Loop continues to run for as long as your specified
condition exists.
For... Do While...
... ...
Lines of code to repeat Lines of code to repeat
... ...
Next Loop
The second difference is the nature of the test to end the looping. For...Next has a variable for
counting, a start point, and an end point. Do...While has an expression test: at the beginning of
each loop ASP checks the expression and if it is true then it runs the loop. If the expression is
false, then the loop is not run and ASP jumps down in the code to the line after Loop.
There is a serious trap that every beginning programmer falls into: if you start a loop and do not
provide a means for it to stop then it will continue forever as an infinite loop.
Note Most servers will eventually cut off a given ASP page, since the server needs to
attend to other visitors and tries to optimize its resources.
The Do...While form of looping is perfect if we have to make a row in a table for each month of
the year up to the present month. However, we don't know – at the time of writing our ASP code –
in which month the user will be viewing the page. In May, the fifth month, we would like five rows.
We can find the number of the current month (May = 5) using the code month(now()).
<HTML>
<HEAD>
<TITLE>Example Do While</TITLE>
</HEAD>
<BODY>
<H1>Year To Date: Monthly Sales Calls</H1><BR>
<%
varMonthCount = 0
varMonthNow = Month(now())
The first line takes care of 'administrative' HTML matters, and then in the body of the code we
have a line to write the opening title on the page. Next, the VBScript begins and we establish two
variables: varMonthCount will track our progress in creating the rows (it starts with a value of
one since we have no rows printed so far). The second variable is varMonthNow, which we will
use to hold the current month. This is obtained by running the month(now())functions
mentioned earlier.
Now we can begin the Do...While loop. The trick is to use the right expression to stop the loop.
In this case we will test at the beginning of each loop whether the varMonthCount is still less
than or equal to the current month. For example, if we run this in May, the first loop will be done if
the varMonthCount is less than the varMonthNow (which would be 5 for May). We then print
some text from within the loop. The next line takes the old value of varMonthCount and
replaces it with a new value exactly one increment higher. Now ASP loops back up and tests
again. Since varMonthCount (which now = 2) is still less then MonthNow (which now = 5)
the loop runs again: and this will happen five times. After the fifth loop varMonthCount will equal
six and when ASP tests for the Do...While condition the answer is false. ASP immediately jumps
down to the first line after the Loop statement and prints the signature line.
This code starts by setting up two variables. The first is the counter, which we set at zero. The
second holds today's date which we get from the function day() applied to the function now().
The result of these functions is to provide the text "5" when this page is requested on the fifth day
of the month.
Then the loop begins, with VBScript testing whether the varRowCount is still less than or equal
to today's date. (We have to start varRowCount on one as the months always start on the first.)
If that expression is true then VBScript goes through the cycle. There are two steps in the cycle:
the first simply puts the text and HTML on the page. The second line takes the old value of
varRowCount, adds one to it and uses that new number as a replacement for the old contents of
varRowCount. We need to change some condition on each cycle of the loop so we can, at some
point, return a 'false' value for the Do...While expression and end the loop.
Improvements
Since we are getting and using the actual date it would improve the product if we put the date on
each line.
<%
varRowCount = 1
varTodayDate = day(now())
varTodayMonth = MonthName(month(date()))
Do...Loop While
You're not restricted by having to place the condition at the beginning of your loop. If you wanted
your condition at the end of the loop, then that is also possible. So, if you went back to the
previous example, it could be amended as follows:
Do
...code here...
Loop While varRowCount <= varTodayDate
The difference here is that your block of code would be executed automatically, before we check
our condition. So in this way, you can deduce that this loop would execute at least once, before
exiting, while the first occurrence of the loop might never be executed at all.
Or even:
Do
...code here...
Loop Until varRowCount >= varTodayDate
Notice that we've amended the condition, for looping slightly, so that we loop until the condition
becomes true. The rules of when to use While and when to use Until are basically looking at
your example and see which makes more sense to use.
We've spent a lot of time looking at the important looping controls. Next, we'll turn our attention
to the equally important jumping structures.
Jumping Structures and the Art of Reusing Code
As you write more ASP code you'll find that you want to use the same code in more than one
place. ASP allows you to write some code once, then run it as many times as needed to support
the main body of code. These mini-programs are called procedures or functions. We will want
ASP to jump away from execution of the main body of code, run through the commands of a
procedure or function, before returning to execute the main body of code.
For example, you may have a few lines of code that insert some lines of text about how to contact
the sales department. You would like to have this show up in various places on the page, but
want to avoid having to rewrite the code separately each time. The solution is to put the lines of
code into a procedure (sometimes called a Sub Procedure or a Sub). Then every place that you
want that code to run you can invoke the subprocedure rather than rewrite the code.
A second example is to calculate the delivery date of a shipment. You may have to do this
several times for different items on a page. In this case you want to start with information such as
the ship date, and from that calculate the delivery date. There may be some branching in the
procedure to accommodate the fact that there is no delivery on Sunday.
Procedures
Procedures are easy to write – they have just three parts. First is the name, second is the code
that the procedure should execute, and last is an ending statement. In addition you will have to
direct your main body of code to run the procedure at the appropriate point.
In our first example we may want a procedure to put the contact information for the sales
department on the web page. This can be done with the following code:
Response.Write "Price quotes for this product are available from "
Response.Write "Joe at 201/555-1212.<BR>"
In order to avoid writing this same code in many places throughout the page we can put it into a
subprocedure:
Sub SalesContactInfo
Response.Write "Price quotes for this product are available from "
Response.Write "Joe at 201/555-1212.<BR>"
End Sub
This first line must start with the key word Sub, then a space and the name of the subroutine. The
name should start with a letter and contain only letters and numbers – no spaces or symbols. The
following lines hold the VBScript code to be performed in the subprocedure. The last line must be
the command End Sub.
This subprocedure can then be called from your code whenever needed, by using the statement
Call followed by the name of the procedure, as in the following example:
many lines about sweaters
Call SalesContactInfo
...
many lines about vests
Call SalesContactInfo
...
many lines about hats
Call SalesContactInfo
Although you can use the Call keyword to call the subprocedure, you could just as easily call the
subprocedure without this keyword:
many lines about sweaters
SalesContactInfo
...
many lines about vests
SalesContactInfo
...
many lines about hats
SalesContactInfo
The subprocedure does not have to be in the same set of ASP code as the calling statement
although it does have to be part of the same web page. The following two sets of code both work
fine.
Subprocedure in <%
separate section Sub SalesContactInfo
of ASP from Response.Write "Call Joe at 201/555-1212."
calling code End Sub
%>
<H3>Sweaters For Autumn</H3>
Warmest, woolliest sweaters now in stock.
New colors for autumn including Orange/Black and an
Autumn Medley.
<%call SalesContactInfo%>
Subprocedure in <%
same section of Sub SalesContactInfo
ASP as calling Response.Write "Call Joe at 201/555-1212."
statement End Sub
Response.Write "<H3>Sweaters For Autumn</H3>"
Response.Write "Warmest, woolliest sweaters "
Response.Write "now in stock."
Response.Write "New colors for autumn including "
Response.Write "Orange/Black and an Autumn Medley."
Call SalesContactInfo
%>
Parameters
You can improve your subprocedure by having more than one possible outcome. The result will
depend on variables sent from your main code over into the subprocedure. You do this by
sending parameters along with the subprocedure. A parameter or argument is in fact a piece of
data, which is passed to the subprocedure. It is transferred by placing it in parentheses after the
name of the subroutine.
Call SalesContactInfo(varParameter)
For example, let's say you have four sales specialists, each covering a different region, and you
want to display the appropriate sales representative for a given user. Let us also assume that you
have picked up from the user their region and stuffed it into varRegion by a simple form. You
may have created a subprocedure that writes the name of the appropriate sales representative.
The varRegion can be transferred from your main form over to the procedure by putting the
name of the variable in parenthesis as an argument. The calling code would now look like:
<HTML>
<HEAD>
<TITLE>Example Procedures Two</TITLE>
</HEAD>
<BODY>
<%
Dim varRegion
varRegion = Request.Form("region")
Sub SalesContactInfo(region)
Response.Write "Price quotes for this product are available from "
Select Case Region
Case "North"
Response.Write "Brian at 201/555-1212."
Case "South"
Response.Write "Rob at 719/555-1212."
Case "East"
Response.Write "Pat at 604/555-1212."
Case "West"
Response.Write "John at 312/555-1212."
End Select
End Sub
%>
<H2 align="center">On-Line Clothier<BR>
New Items for September, 1999</H2>
<H3>Sweaters</H3>
<P>New selections of warm and woolly Autumn Sweaters. Special line of
colors for fall festivities including Black/Orange and our new Autumn
Medley.
<%call SalesContactInfo(varRegion)%>
<H3>Vests</H3>
<P>Get ready to dance around the MayPole in this season's brightest
selection of Flowered Vests. We're in-swing with the retro look
featuring flowers from the 50's, 60's and 70's.
<%call SalesContactInfo(varRegion)%>
<H3>Ties</H3>
<P>No reason to look uncomfortable just because you're wearing a noose!
We have a new
line of ElastoTies<EM>™</EM> with lots of bungee in the middle to
allow you to <EM>Expand under Pressure</EM><SMALL>®</SMALL>.
<%call SalesContactInfo(varRegion)%>
</BODY>
</HTML>
If we called this code from a suitable form where we had declared that we were in the East for
example, it would display the following:
The above routine will put the appropriate SalesRep info into your code. There are three
changes from our last example. The most obvious change is that inside the Sub where we used
to have Response.Write "Joe at...", we now have a Select…Case structure that
determines which sales representative we should write onto the page.
Also notice two slight differences in the syntax. First is in the line that calls the Sub:
call SalesContactInfo(varRegion)
In order for the subprocedure to decide which sales rep to use it must know the regional location
of the user. This information is passed over to the subroutine by enclosing it in parenthesis after
the name of the subroutine.
Notice also that the subprocedure has a change in the first line. After the name of the
subprocedure there are parenthesis that specify the data that is being sent over from the main
body of code.
Sub SalesContactInfo(region)
You might be wondering why we're calling SalesContactInfo with the variable varRegion,
yet when we get to the subprocedure, we're using the variable region. Is it an absentminded
typo on the part of the author who has forgotten to adhere to a naming convention? The short
answer is no. In the above line, Sub informs us that we're about to define a subprocedure; where
SalesContactInfo is the name of the subprocedure, and region tells us that the subprocedure
has an argument. We're calling this argument region only throughout the body of the
subprocedure, as it describes what the subprocedure will do with the argument that's its been fed.
E.g. Select Case region... In other words, it's a local variable.
Here, we call the subprocedure that we defined above. When the subprocedure is called, there's
a value contained within the variable varRegion – this value is passed to the subprocedure as
an argument. Within the subprocedure, region adopts this value and allows the subprocedure to
execute. When the subprocedure finishes its execution, region ceases to exist, but varRegion
continues to exist; and we return to the main program execution.
A confusing point arises here on our definition of subprocedures and functions. Both
subprocedures and functions can receive information from the main code. They can both take
action on that information. But only the function can return a value to the main code. Notice that
the subprocedure performs its action (For example, Response.Write a line on the page)
without sending anything back up to the main code. In a function there can be some information
(such as the result of a calculation) sent back for the main code to use.
In the code below you can see how easy it would be to re-arrange the order of the subprocedures
or to substitute a new model of sweaters. In this example, the first three lines after the <% call the
three subprocedures that are coded in full further down into the code. You will find that these
subprocedure blocks will not be executed unless they are called. Therefore, after the last Call
(Call Tie98Autumn on line 11 of the following code) VBScript sees that the remainder of the
page mostly contains subs; and so it skips over these, then it stops when it reaches the %>
marker. (The browser will interpret the HTML tags that form the remainder of the page when the
page is sent to it.) You can understand procedures better when you understand that they are only
executed when (and if) called, not based on their position in the code.
<HTML>
<HEAD>
<TITLE>Example Procedures Three</TITLE>
</HEAD>
<BODY>
<H2 ALIGN="center">On-Line Clothier <BR>
New Items for September, 1998</H2>
<%
Call Sweater98Autumn
Call Vest98Autumn
Call Tie98Autumn
Sub Sweater98Autumn
Response.Write"<P>New selections of warm and woolly Autumn " & _
"Sweaters<SMALL>.</SMALL>"
Response.Write "Special line of colors for fall festivities "
Response.Write "including Black/Orange and our new Autumn Medley. "
End Sub
Sub Vest98Autumn
Response.Write "<P>Get ready to dance around the MayPole in "
Response.Write "this season's brightest selection of Flowered Vests.
"
Response.Write "We're in swing with the retro look featuring "
Response.Write "flowers from the 50's, 60's and 70's. "
End Sub
Sub Tie98Autumn
Response.Write"<P>No reason to be uncomfortable because you wear a
noose! "
Response.Write "We have a new line of ElastoTies<EM>™</EM> "
Response.Write "with lots of bungee in the middle to allow you to
<EM>Expand "
Response.Write "under Pressure</EM><SMALL>®</SMALL>."
End Sub
%>
</BODY>
</HTML>
Procedures Summary
To review, procedures have a name and contain some lines of code. Whenever you did a Call
of a procedure, its lines of code were run. Procedures are perfect for Response.Write or other
actions, but what if you want to have some lines of code that give you back an answer? For
example you may frequently calculate a 5% commission on a wholesale price. Or you may want
to find out the due dates of library books Note that these questions require lines of code to
generate an answer that you will use in the main body of your code. Functions fill this niche by
accepting some information from your code, performing calculations or decisions, then returning
an answer to your code.
Functions
Functions are written in a similar way to procedures, but with several special characteristics that
handle the returning of information. There are five ideas to master for writing and using functions.
First, when you write a function you use Function (instead of Sub) on the first line and End
Function (instead of End Sub) on the last line. The naming rules of functions are the same as
for procedures: start with a letter, no spaces, and avoid symbols.
In the next few paragraphs we will build a function to calculate the due date of a library book (two
weeks after the check-out date). The start and end of the function will look like this:
<%
Function FindDueDate()
...
lines of code
...
End Function
%>
The function usually receives some information from your main code in the form of a parameter
(subprocedures can also receive information from parameters in the same way, only they can't
return the values directly). This information is passed to the variable that you have named in the
parenthesis following the function's name. You do not have to declare this variable using Dim – it
is available for use automatically. For example, in our DueDate function we would expect to
receive the checkout date from the main body of the code – this would give the due date function
the starting point for its calculations. So now our code looks like this:
<%
Function FindDueDate(varCheckOutDate)
...
lines of code, some of which can use the variable varCheckOutDate
...
End Function
%>
Once the function has done its calculation, it has to report the result back to the VBScript engine.
The VBScript engine processes whatever value is assigned to the name of the function. This is a
little tricky for most beginners. Not only do you have available the variable which passed the data
into the function (varCheckOutDate); there is also a variable (usable only within this function)
that is automatically created with the name of the function, in this case FindDueDate. Whatever
value is stuffed in that variable will be is sent back to the to the VBScript engine when the function
is finished. For example:
<%
Function FindDueDate(varCheckOutDate)
FindDueDate = varCheckOutDate + 14
End Function
%>
The fourth concept is easy: how to use a function. You can call a function by just typing the name
of the function followed by double parenthesis (we'll discuss the contents of these parentheses in
a moment). You can then assign a variable to the return value of the variable as we do below:
<%
varOut = Date()
varDueDate = FindDueDate(varOut)
Response.Write "Your books are due on " & varDueDate
%>
varDueDate here contains the value returned by the calculations in our function. By assigning
the value to this variable, we are running the function. The last idea is how to send the
information from your main body of code to the function – in our case, the check out date of the
book. We put whatever data we want to send to the function in the parenthesis. For example:
<%
checkoutDate = date()
varDueDate = FindDueDate(checkoutDate)
Response.Write "Your books are due on " & varDueDate
%>
And so we get the final product; VBScript code that creates a function, and then some ASP code
which outputs the values that the VBScript function returns to ASP.
<HTML>
<HEAD>
<TITLE>Sample Function Page</TITLE>
</HEAD>
<BODY>
<%
Function FindDueDate(varCheckOutDate)
FindDueDate = varCheckOutDate + 14
End Function
%>
<H2>Thank you<BR>
for using the On-Line Library.</H2>
<%
checkoutDate = date()
varDueDate = FindDueDate(checkoutDate)
Response.Write "Your books are checked OUT on " & checkoutdate
Response.Write "<BR>Your books are due on " & varDueDate
%>
</BODY>
</HTML>
75. If you select City and press Submit Query, you'll see the following information.
How It Works
The two functions that perform our calculations are relatively straightforward.
Function CityCost(NumberDays)
varHotelTotal = NumberDays*175
varMealsTotal = NumberDays*75
varAirportTransport = 85
CityCost = varHotelTotal+varMealsTotal+varAirportTransport
End Function
The first function CityCost assumes that the hotel costs 175 dollars a day. So it takes the
number of days supplied by the user, multiplies them by 175 and the stores them in the variant
varHotelTotal. It does the same for Meals assume that the meals cost 75 dollars a day. The
cost of air transport is a one off and costed at 85 dollars. Then the variable CityCost is created,
adding the three values generated previously altogether. This is the value returned by the
function. The function that calculates the cost by suburb works in the same way, just applying
different prices.
Function SuburbCost(NumberDays)
varHotelTotal = NumberDays*85
varCarTotal = NumberDays*45
varMealsTotal = NumberDays*75
SuburbCost = varHotelTotal+varMealsTotal
End Function
We then detect whether the hotel is in the city of the suburbs using the Select Case statement.
For the City, we then call the CityCost function, and store the value returned by the CityCost
function. We then multiply the result returned by the CityCost function by 2, 4 and 6
respectively for the 2, 4 and 6 day courses:
Select Case (varLocation)
Case "city"
varCost = CityCost(2)
Response.Write "The two day course will cost $" & varCost & "<br>"
varCost = CityCost(4)
Response.Write "The four day course will cost $" & varCost &
"<br>"
varCost = CityCost(6)
Response.Write "The six day course will cost $" & varCost & "<br>"
We do the same for the suburbs, displaying the result for the 2, 4 and 6 day courses.
Case "suburb"
varCost = SuburbCost(2)
Response.Write "The two day course will cost $" & varCost & "<br>"
varCost = SuburbCost(4)
Response.Write "The four day course will cost $" & varCost &
"<br>"
varCost = SuburbCost(6)
Response.Write "The six day course will cost $" & varCost & "<br>"
Improvement to our Functions
We can live without the intermediate variable in our functions. The longer way we used is shown
below first:
Select Case varLocation
Case "city"
varCost = CityCost(2)
Response.Write "The two day course will cost $" & varCost & "<BR>"
Remember that both subprocedures and functions receive data in order to perform calculations or
perform one of several actions. The difference between a subprocedure and function is that a
function returns a value back to the main code.
Summary
The real power of programming comes from the ability to control and direct the order of execution
of statements. In this chapter we looked at three families of control structures:
Branching, where only one of several possible sets of code is executed
Looping, where a set of code is performed over and over
Jumping, where VBScript pauses execution of the current script block, jumps over and
executes a different script block, and then comes back to resume the execution of the
original script block.
When branching between just two choices we use the If...Then statement that makes its
decision based on an expression that evaluates to true or false. If we have more than one
outcome then we use the Select Case structure.
When we write a loop we must consider how many cycles we want to run through. If we know the
number of cycles before starting the loop we generally use the For...Next structure. If we don't
know then we use the Do...While structure.
When writing jumping structures we must ask if we expect VBScript to return some kind of
answer from the code that we jump to. If yes, then we write the other code as a function. If we
expect the other code only to perform actions then it is written as a sub procedure.
With these tools we can now orchestrate the order in which our lines of code are executed. In
some cases we may skip over lines while other times we may repeat code. Each of these control
construction techniques will provide a supporting role in the use of the ASP objects and
components that you will learn to use in the to use in the remainder of the book.
Chapter 6: Objects, Properties, Methods and
Events
Overview
In this chapter, we will be looking at objects. Some of you may have heard terms like "object
oriented programming", "object models", and similar. In order for us to understand what these
terms mean, we first need to look at the word found at the core of each of them: object.
In our study of objects, we will find that an object is a software representation of a real-world item,
or object. Software objects feature properties (that describe the object), methods (that allow the
object to do things for you), and events (code that is executed automatically when a certain
situation – an event – occurs).
Once we have looked at what an object is, we'll then be able to look at how objects are useful to
us in developing ASP applications. Developing web applications requires us to deal with both the
client-side and server-side programming, and therefore we'll take a look at how objects can be
used on both sides of the wire.
So, for our telephone's physical properties, we could say that it has a microphone, a speaker, and
a dialing pad. We could also say that it lets us do things, such as call someone and talk to them.
Our telephone will also tell us when certain events are happening: for example, if a friend is trying
to call you, your telephone will ring to let you know. This ringing will prompt you to take some
action, like picking up the handset and talking to your friend. As an abstract object, our telephone
has:
Certain properties that define and describe it
A set of things or methods that it lets us do
The ability to prompt action when events occur
We can use these three attributes to describe physical objects and abstract concepts. In a few
minutes we will describe how these real-world ideas are replicated in software objects, but for
now let’s go a little deeper into our real-world telephone. By learning about what objects are, we
can then look at how to use them in a way known as object-based programming. In the object-
based way of programming, the application is broken down into a set of objects. In doing this, you
can build the application in a two stage process. Firstly, you create the objects you will need in
your application and then you set up the relationships and interactions between objects. Later in
this chapter, we will see how the objects of Active Server Pages relate and interact with each
other and allow us to build our applications.
How It Works
The three categories that we have created in the left-hand column can be applied to any object.
In fact, the best way to describe an object is to break down its characteristics into these three
categories, and put information about your object into these categories. Any information that you
have about a particular object can be included in one of these categories.
If you have another telephone that features all these characteristics, except that its color is blue,
then we can describe your telephone by changing that one part of the description above.
Moreover, this technique works for any type of object, both real world and software.
Object Terms
So far, we have used verbose English terms to describe our three categories. In the world of
objects, we need terms that concisely describe each of these three categories. These terms are
properties, methods and events. In addition to these terms, we need to look at the term
instance as it relates to objects. In this section, we'll look more carefully at what each of these
mean in abstract terms.
So we've mentioned object descriptions or templates, but it's time to give them their proper name
in ASP; classes. We mentioned that each object can have different instances. For instance, my
telephone is small and white and has 12 buttons. Your telephone will probably be different to that,
but they're both recognizable as telephones and they both provide the same function. They both
conform to a set of rules for what a telephone does – they connect to the local telephone line,
they both have a numeric keypad and somewhere to speak into. A class in ASP is like a set of
design rules that an object must conform to. It would be no good if my telephone didn't have a
handset, or a numeric keypad, even if it did plug into the telephone socket on the wall. In a class
there should be a minimum set of functions that your object must be able to perform.
Properties
When talking about those characteristics that describe an object, we are talking about the
properties of the object. Each property of the object describes a particular aspect of the object.
The property is actually described as a name/value pair. This means that for every named
property, there is a single unique value that describes that property for this instance of the object.
If we go back to our telephone example, we can create a table that lists each of the property
names and the value of each property.
Color Grey
Material Plastic
Weight 6.5 ounces
NumberOfKeys 12
TelephoneNumber (714) 555-1523
Connected Yes
We now have a set of properties that describe this instance. The properties of an object are used
to represent a set of values associated with the object. A new instance of the object may have
different property values, but it has the same property names.
Even with different property values, these two telephones are instances of the same object
template. Since we know that all telephone objects have a 'Color' property, we can determine the
color of each of the phones by examining its 'Color' property value. We can use properties in two
ways. We can read from them or we can also write to them. So if we wanted, we could have
changed the cover of our telephone to a different color.
Now that we have a way of describing the telephone object, let's take a look at what we can do
with it.
Methods
Another characteristic of objects is that they can perform functions for us. For example the
PlaceCall method would perform several functions for you. It will connect you to the local
exchange, the exchange will route your call, and then when it reaches the destination, it will make
the destination phone ring. These built-in actions occur whenever you pick up the handset and
dial a number. This is a capability that has been built in to the machine.
However not all objects have functions like this. A chair object allows you to sit in it, so you could
say that it is functioning to support your body. Objects that perform tasks that are more 'functional'
are said to have methods. The tasks that an object can perform are called methods.
A method is defined as an action that an object can take. The code in a method is executed when
the method is called. This calling is done by a command you write in the script of your ASP page.
Once we have created an instance of an object, we can tell it to perform a certain task by calling
one of its methods.
Let's illustrate this using the telephone example. Our telephone object can carry out five methods.
Each of these methods will cause the telephone object to perform an action. Here is a list of
functions that the methods of the telephone object can perform:
These methods are used when we want our telephone object to perform a certain function; all we
need to do is tell it to execute the corresponding method.
Methods are actually blocks of code that are written by the designer of the object (Microsoft, for
example). The reason methods exist is because lots of programmers want to do the same job, so
it is worth it for the gurus at Microsoft to write the code to do that job, test it, optimize it, and get it
in great shape, then bundle it up in the object. We, as programmers, can then use that code pre-
made. Instead of re-inventing the wheel we spend our time on the unique parts of our project.
Parameters of Methods
You may have noticed that some of the methods can be executed directly, while others look like
they will need additional information. To contrast these two ideas, consider the following
examples:
Suppose that our telephone receives an incoming call (in the next section, we'll see how
we can tell that this is happening). All we need to do to answer the call is to use the 'Answer'
method of our telephone object.
Suppose that we want to place a call. Simply calling the PlaceCall method isn't enough
in this case: we need to supply more information (for example, the telephone number!) in
order to complete the action.
Let's look more closely at the second of these examples. The telephone object has a
TelephoneNumber property, and this is used to identify our telephone's own telephone number
(i.e. the number that other people use to call us). So, the TelephoneNumber property of our
phone isn't going to help us to make outgoing telephone calls.
So, how do we tell the phone which number we want to call? It's possible, I guess, for the
telephone object to have another property, called OutgoingTelephoneNumber, that would
identify the desired number; but that would be too cumbersome, because every time we wanted
to make a call we would have to:
Set the OutgoingTelephoneNumber property value to the desired phone number
Execute the 'Call' method of the telephone object to place the call
As you know, telephones just don't work that way. It would be much more elegant (and intuitive)
to have some way of passing the outgoing phone number to the 'Call' method, so that we can
place an outgoing call in a single step. This is done by passing a parameter to the 'Call'
method. With this in place, we can place an outgoing call by simply executing the 'Call' method
and telling it which number we want to call, like this:
Execute the 'Call' method of the telephone object, passing the outgoing telephone
number as a parameter. Parameters here are just the same as the arguments (parameters)
we passed to functions and subroutines in the last chapter.
If we look again at the methods of the telephone object, we can identify those methods that
require parameters, and what the values of those parameters mean to the object:
You can see that a method can have none, one, or more than one parameter. The
SendCardNumber method actually requires two parameters. You are required to pass in both the
calling card number and the personal identification number (PIN) in order for the method to
execute properly. Information passed as parameters of the method for execution by the method,
will only be executed if all parameters have been supplied.
Return Values
In addition to passing parameters to a method, the method can also return information to us. The
value returned by a method is (rather conveniently) called a return value. If a method has a
return value, then it will pass information back to us. This information could have a number of
purposes. For example, the return value might be an indication of whether or not the method
completed successfully. Alternatively, the method could also pass back the results of some
processing that it did for us. It can even return another object as a result.
As the user of an object, we can decide whether we want to do anything with the return value. If
the information is pertinent to what we are doing, then we can capture the return value and do
something with it later. If we do not care what the return value is, we can just ignore it and
continue with our work.
Just as the methods of the telephone object can have parameters, we can identify those methods
that pass return values (and these can be passed as parameters to other methods), and what
those values mean.
Events
We have now looked at two of the three characteristics of an object. The properties and methods
of an object are ways that the user of the object can communicate with the object. Now, what if
the object needs to communicate with the program that created it?
As an example, consider what happens when our telephone receives an incoming call. The fact is
that it needs some way of telling us to answer the call. How will the telephone communicate this
information to us?
Again, it's possible for the telephone object to have a property (called IncomingCall, perhaps)
that was set to 'True' whenever an incoming call was present. However, there are two
disadvantages to this. First, it would require the user of the telephone object to check this
property on a regular basis. Second, the user would require a great deal of knowledge of the
inner workings of the object, which isn't ideal.
What is needed is a way for the object to tell the user that something has happened. The
mechanism for this is called an event. An object generates an event whenever something of
interest happens. In our telephone example, when the telephone receives an incoming call it tells
us so in the form of an event – we'll call it the IncomingCall event. (On most telephones, this
particular event takes the form of the telephone ringing.)
The telephone object would generate an IncomingCall event every time an incoming call is
received. In a physical phone, the ringing sound is the phone notifying you of the IncomingCall
event. When the user receives this event, it can execute the 'Answer' method (pick up the
handset), and begin the call. This frees the user from having to check regularly for incoming calls:
the event is designed to notify the user just at the appropriate moment.
Just like methods, events can have parameters. These parameters can hold specific information
about the event. For example, if our telephone supports CallerID – a feature that reveals the
identity of the incoming caller – then the IncomingCall event could include a parameter that
contains the telephone number of the incoming caller.
Here is a list of the events that our telephone object will generate, along with their associated
parameters:
There are a couple of useful pieces of terminology that are often used in this context. When an
object generates an event, the object can be said to fire the event. When the object has fired the
event, we say that the user must handle the event.
Synchronous vs Asynchronous
One of the advantages of working with objects and events is that it awakens us to the concept of
asynchronous programming. First off, let's look at the definitions of synchronous and
asynchronous.
These terms refer to how two separate actions are related to each other. If the first action must be
completed before the second one begins, then these two actions are said to be synchronous. If
the second action can begin at any time, no matter what the status of the first action, then these
two actions are said to be asynchronous.
We've already discussed what it would be like if we our objects didn't support events. For
example, to detect an incoming call, you would need to constantly check the value of some
property to see whether an incoming call was waiting. While you're performing these frequent,
regular checks, you would be unable to perform other tasks dependent on the outcome of that
task. This is an example of synchronous activity. For example in the real world you might be
waiting for a telephone call from a friend to let you know what time you should meet them. You
can't go out until you've arranged a specific time. It's the same in programming. Your program
could be waiting on a WaitForAnIncomingCall method in a While ... Wend loop, and it could
be stuck there until the call was detected, refusing to return control to the main body of your
program.
With events, we can have asynchronous activity. By having an event handler that is called when
the object fires the 'IncomingCall' event, we can perform other tasks (for example, making an
outgoing phone call) without having to devote any effort to monitoring the incoming call status.
Our event handler code will be dormant until such a time as it detects the 'IncomingCall' event,
and then sets about dealing with the incoming call.
Note This is not to say that all synchronous is bad and all asynchronous is good. You
will see many instances in the real world where it makes sense to use a
synchronous activity to perform a certain type of processing. Likewise, we will
also see instances where an asynchronous activity is not an optimal way of
dealing with an event.
Encapsulation
One great thing about objects is that you don't have to understand what's going on underneath
the shell, to know how to operate them. With our telephone we don't need to know how our voice
is projected from the phone, down the wires to the nearest exchange, and how from there it gets
to our intended destination. This is all hidden from us. It's the same in ASP – you don't need to
know how the object was programmed, for example, in C++ or VB (objects can be created in
many languages), to be able to interact with it. The concept of a user of an object not being
concerned with the inner workings of the object, is known as encapsulation. For example, when
you use a telephone to answer an incoming call, all you need to do is pick up the handset. You
don't need to know how the transistors are connected to each other inside the telephone. This is
the equivalent of executing the Answer method. You do not need to know what's going on
underneath – that's all encapsulated within the Answer method. This is an example of
encapsulation.
One advantage of encapsulating the workings of an object within a method is that the
implementation of the method can be changed without having to adjust the client. For example,
suppose the phone company decides to change the way that an incoming call is answered.
Without encapsulation, all of the users of the telephone object would have to be adjusted to
support this new way of answering the phone. Instead, by encapsulating these new steps within
the Answer method, the actions of the client never need to be changed: with either system, all
the client needs to do is execute the 'Answer' method. Not only does encapsulation make the
telephone user's life easier; it allows the developer of the telephone object to change the
implementation at any time.
Moving On to Programming
Now that we have a basic understanding of what an object is, we can move on to looking at how
programming concepts have changed from traditional methods by using objects. When working
with objects in software development, we will create objects that have properties, events and
methods. We can use these three attributes to describe physical objects and abstract concepts.
Either way, the programmatic object will allow us to interact with it through its properties, events
and methods.
Programming with Objects
To begin our look at programming with objects, let's use our trusty telephone object again. Being
a technophile and always needing to have the latest and greatest, you have even hooked up your
telephone to your computer. Now you want to be able to do something with it. If we want the
computer to interact with the telephone, we need a programmatic object that will allow us to
control the physical telephone.
It is this representation of a physical object that gives programmatic objects their power.
Representation is literally the process of taking our real world object and turning it into a software
object. In our telephone object example the color of the phone is represented by a color property,
the weight of the phone is represented by a weight property. The ability to pick up the receiver
and dial a friend is modeled by the PlaceCall method. The real world telephone has a set of
features or characteristics that can be broken down into properties, methods and events. Once
we have identified these features it makes it easier, to represent them in our software object.
However, as described in the previous section, the concept of encapsulation means that the
actual workings of the telephone object are hidden from us. When a phone rings, you don't need
to know how the signal was transmitted to the exchange, you only need to interact with the
interface (talk into the handset). This is what we're going to consider with our software object; the
interfaces we use to communicate with it, rather than the software object itself. So when we use
our computer to control our object, it's not going to use the inner workings of the objects, rather
it's going to communicate with the object and control it using its interfaces (the methods and
events).
This book will not cover how to create the software object itself (you will be able to download this
from the Wrox website – details of this are in the Try It Out coming up): rather, we will take a look
at the programmatic object, and then look at how we can use the object's properties, methods,
and events.
Property Name
Color
Material
Weight
NumberOfKeys
TelephoneNumber
Connected
As you can see, we have used the same names that we used when discussing the physical
telephone object. The methods of the telephone object are the same as well. In this case, the
methods that have parameters will have the same parameters as well:
Method Name Parameters
PlaceCall NumOutgoing
Answer No Parameters
HangUp No Parameters
SendCardNumber NumCCN, NumPIN
Disconnect No Parameters
Finally – as you will expect by now – events that the object will support are the same events that
are supported by the physical telephone object:
Now that we have defined the interfaces of our telephone object, we can take a look at some
code examples that will show you how to use these interfaces. For these examples, we will be
using VBScript, which is the language that is being used throughout the book. Since there are
three types of interfaces, we will look at three code samples – one for each type.
The problem is easy enough to rectify, all you need to do is manually register the DLL yourself.
To do that, once you've run setup.exe you'll find that a file called MyTelephone.dll has been
created. It's also a good idea to stop and restart the web application manager before you use the
DLL in any examples. It can be placed in any directory - the only important thing is that the file be
registered in that directory. To do that, there is a file (provided by the machine's operating
system) called REGSVR32.EXE. If you run that file and pass the name of the .dll as a
parameter (e.g. regsvr32 MyTelephone.dll), it will register the file in that location. The best
way to do this is from the command prompt: copy the .dll file to the desired directory, go to that
directory, and then run regsvr32 MyTelephone.dll. The OS should find regsvr32.exe
since it is usually in the WinNT/System32 folder, which is in the default path.
If it's still not working then check that the machine you are actually installing the file on is actually
the web server, and not just the machine with your browser on, and then look at the web site
http://p2p.wrox.com for support if you still have problems.
A program that uses the instance can then retrieve the values associated to these properties.
Alternatively, they can be used by a method or event to perform some action. The programmer
working with the instance of the telephone object is responsible for setting the values of many
properties; other properties will be set based on the results of methods being called.
Setting a Property
First, let's look at how to set a property. The four properties that we'll use here to describe our
instance of the telephone object are:
Color
Material
Weight
NumberOfKeys
When the instance of our object is created, these values are left blank or set to default values. It
is up to the program that creates the object to set the specific values that we want.
How It Works
The first step in obtaining a reference to an object is to allocate a variable to hold the reference.
The variable is created using the Dim statement:
Dim objTelephone
Note You'll recall from Chapter 4 that the variables in VBScript are in fact variants.
This is done using the Server.CreateObject method. This is the process of instantiation that
we referred to earlier in the chapter, when we discussed the concept of instances. The Server
object is one of the built-in ASP objects; objects are everywhere; here we are using an object to
create an object. This method has one parameter – the name of the object you want to create.
The method also has a return value – it's a reference to an instance of the object.
Since the value returned by the CreateObject method is a reference to an instance of the
Telephone object, we must use the Set statement to assign its reference to our variable. The
Set statement is a VBScript statement that lets us store object references in variables. Since the
return value is a reference to the object, we have to use the Set method to store its value for later
use. We will cover the CreateObject method in more detail in Chapter 11.
Now that we have our reference to the instance of the telephone object, we can go about setting
the properties. To do this, we simply use the object.property notation and set it to the value
that we desire:
objTelephone.Color = "Blue"
objTelephone.Material = "Thermoplastic"
objTelephone.Weight = 22
objTelephone.NumberOfKeys = 12
object.property = value
Lastly we set the object to nothing to release the reference and free up memory:
Set objTelephone = nothing
This is general housekeeping you should perform every time you have finished with an object.
Now that we have set some property values in our telephone object, we can look at how to
retrieve these values.
Retrieving a Property
The last section showed how to set the values of properties of an object. Now that information is
stored there, we can retrieve this information at a later time. In essence, we have an instance of
an object that has some data stored in its properties. All we need to refer to this instance is the
reference to the object's instance. All of the data that the object has stored inside of it comes
along with the object.
Read-Only Properties
In addition to the data that we have explicitly stored in the object, there is information that the
object uses to describe its state. In our telephone object, there is a property called Connected
that describes whether or not a telephone is connected to the telephone exchange. In order to
change the connection state of the phone, we would use a method. This method is read only for
the user, so the user cannot change it themselves. The only way to change it is internally, as a
result of using a method such as PlaceCall, which would change the property value of the
Connected property from False to True.
You may wonder why we would not just change the property by hand? This is another example of
encapsulation. There is more to disconnecting a phone than just changing a value of a property:
the object needs to perform some actions, which the user of the object does not need to be
concerned about. This functionality is encapsulated in a method, and the method is responsible
for updating the value of the Connected property. This makes the Connected property a read-
only property, which means that we cannot set its value, only retrieve it.
First, we set the Color, Material, NumberOfKeys and Weight properties of our telephone,
just as we did in the previous example. Next, we allocate some variables that will hold the values
of the properties of our telephone object, using the Dim statement. We allocate one variable for
each property that we are storing:
Dim strColor
Dim strMaterial
Dim intNumKeys
Dim intWeight
Dim blnConnected
Next, we set about retrieving the property values. To do this, we use the object.property
notation again – this time to retrieve the property, and then we store the property in the
appropriate variable. Here's the code that does this for the Color property:
strColor = objTelephone.Color
myVariable = object.property
Then we output the results. Here's the line that does this for the Color property:
Response.Write "objTelephone.Color = " & strColor & "<BR>"
If the value of the property is a reference to an object, then you will need to use the Set
statement to assign the property value to our local variable:
We have now seen how to put information into the properties of an object and retrieve that
information. You might also have noticed that the IsConnected property didn't return a value.
That's because it can only be set by another method, the Answer method. As we haven't called
the method, the value can't be assigned. Up until the moment that the method is called, it doesn't
have a value. We'll be looking at how it can be changed next. So now let’s get our object to do
some work for us by calling its methods.
To make this example a simple one, we will be calling a method that has no parameters. Also, in
this example, we are not interested in its return value. We will be using the same objTelephone
instance of our telephone object that we have been using in the previous examples in this
chapter.
1. Using your editor of choice, enter the following source code:
2. <%
3. Option Explicit
4. Dim objTelephone
5. Dim blnIsConnected
6.
7. Set objTelephone = Server.CreateObject("MyTelephone.Telephone")
8. Response.Write "Answering the phone...<BR>"
9. objTelephone.Answer()
10.
11. blnIsConnected = objTelephone.IsConnected
12. Response.Write "The IsConnected property is " & blnIsConnected &
"<P>"
13.
14. Response.Write "Hanging up the phone...<BR>"
15. objTelephone.HangUp()
16. Response.Write "The IsConnected property is " &
objTelephone.IsConnected & "<P>"
17. Set objTelephone = nothing
18. %>
19. Save this file, with the name MethodsExample.asp, to your BegASPFiles directory.
20. View the file in your web browser.
How It Works
In this example, we are using two of the methods that the Telephone object supports. We will
also be checking one of the properties after calling the methods to see if they had any effect.
<%
Option Explicit
Dim objTelephone
Dim blnIsConnected
Set objTelephone = Server.CreateObject("MyTelephone.Telephone")
The first step, as we have done in the previous examples, is to create an instance of the
Telephone object using the Server.CreateObject method. The reference that this method
returns will be stored in a local variable. Remember that since we are storing a reference to an
object, we have to use the Set statement.
Response.Write "Answering the phone...<BR>"
objTelephone.Answer()
The next step is to call the Answer method of the Telephone object. We will use the reference
to the instance that we created to call the method. The preceding Response.Write line is being
used to provide a visual indication that the method is being called.
blnIsConnected = objTelephone.IsConnected
Response.Write "The IsConnected property is " & blnIsConnected & "<P>"
Next, we will want to check the status of the IsConnected property. This property indicates if
the phone is in use or not. Since we have just answered the phone, we would assume that this
property would be set to True. We will store its value in a local variable, then use that local
variable in a Response.Write method to display its value.
Response.Write "Hanging up the phone...<BR>"
objTelephone.HangUp()
Response.Write "The IsConnected property is " &
objTelephone.IsConnected & "<P>"
Set objTelephone = nothing
%>
Finally, we will hang up the phone by calling the HangUp method of the Telephone object. Once
that has completed, we will check the value of the IsConnected property again. This time,
instead of storing the value of the property to a local variable before displaying it, we will directly
display the value of the property. Both ways work exactly the same way. Then we can release the
reference to the object.
Next, we will look at a variation of this example and see how to call a method that has a
parameter.
In this example, we will be calling a method that has parameters – we're still not interested in the
return value, just yet. Again, we will be using the objTelephone instance of our telephone
object that we have been using in all the previous examples.
1. Using your editor of choice, enter the following code:
2. <%
3. Option Explicit
4. Dim objTelephone
5. Dim strPhoneNumber
6. Dim blnIsConnected
7.
8. Set objTelephone = Server.CreateObject("MyTelephone.Telephone")
9.
10. strPhoneNumber = "615-555-8329"
11. Response.Write "Calling " & strPhoneNumber & "...<P>"
12. objTelephone.PlaceCall(strPhoneNumber)
13.
14. blnIsConnected = objTelephone.IsConnected
15. Response.Write "The IsConnected property is " & blnIsConnected &
"<P>"
16. Set objTelephone = nothing
17. %>
18. Save this file, with the name ParameterExample.asp, to your BegASPFiles
directory.
19. View the file in your web browser.
How It Works
Now we're telling the telephone to execute the PlaceCall method. As we know, the
PlaceCall method doesn't work alone: we need to tell the telephone who to call! We do this by
specifying the telephone number as a parameter to the PlaceCall method.
<%
Option Explicit
Dim objTelephone
Dim strPhoneNumber
Dim blnIsConnected
First, as we have done in the previous examples, we will create an instance of the Telephone
object. The reference to this instance is then stored in a local variable.
strPhoneNumber = "615-555-8329"
Response.Write "Calling " & strPhoneNumber & "...<P>"
objTelephone.PlaceCall(strPhoneNumber)
The telephone number that we will be calling is stored as a string. In this example, the number is
hard coded. We could have just as easily used a form to supply the value. We then will display a
message indicating the number that will be called. We can then pass this value to the
PlaceCall method. The parameter that we supply to the PlaceCall method is included within
the method's parentheses. The contents of the parentheses are known as the parameter list.
The entries in the parameter list could be variables or explicit values.
Important One thing that you need to be careful with is the order of the parameters
in the parameter list. If we were calling a method that requires multiple
parameters, then the order of the parameters in the parameter list must
exactly match the order that the method is expecting. So, for example, if
you call the SendCardNumber method, then you must specify two
parameters: the first must be the value of the NumCCN parameter, and the
second must be the value of the NumPIN parameter and these
parameters are separated by a single comma.
Finally, we check the value of the IsConnected property and display its value to the user and
release the object reference.
blnIsConnected = objTelephone.IsConnected
Response.Write "The IsConnected property is " & blnIsConnected & "<P>"
Set objTelephone = nothing
%>
We have now seen how to program with the properties and methods of objects. In our examples,
we have been using an object that represents a physical entity. The remainder of this chapter will
be devoted to looking at a set of objects that represent an application environment in Active
Server Pages. These objects comprise the Active Server Pages object model.
What is the Active Server Pages Object Model?
In this chapter, we have looked at how a physical object can be represented by a programmatic
object. This programmatic object has all of the interfaces of the physical object, and it can be
used as an interface between an application and the physical object itself. But what about objects
that don't have a physical counterpart?
In the Active Server Pages programming model, there is a wide range of functionality that is
accessible to the programmer. ASP helps us to track the state of a user, dynamically generate
HTML output, and take data from forms to be inserted into a database. All of this functionality
makes ASP a rather complex beast. Microsoft was tasked with finding the best compromise
between offering a simple programming model and providing access to all of the power that ASP
provides. To do this, the functionality was grouped into a set of objects. These objects were then
related together into what is known as an object model.
An object model is a representation of a set of objects and their relationships to one another.
These relationships can take the form of containment, where one object is embedded inside of
another. Or, they can take the form of a parent–child relationship, where one object has a set of
child objects associated with it.
We will not be examining the various methods for grouping objects together in this book. What is
important to us is what the objects that make up Active Server Pages are, and how they are
related to each other.
Each of these objects interacts with a different part of the ASP system. This chart shows how
they are related to each other, and how they are related to the client and to the server.
The following chapters in the book will go into each of these objects in greater detail. They will
also provide a series of basic examples that will quickly show you how to use these objects to
create ASP scripts. But for now, we will just take a quick look at what each object is for.
These methods and properties are provided as utility functions for you to use in your pages. They
are not directly used to affect the display of the page, but they still provide valuable support in
creating Active Server Pages. Chapter 11 will cover this object in greater detail.
Application Level Objects
As the web is moving from just serving up pages to providing access to dynamic information from
a wide range of systems, the sites that a user may access are beginning to look more like a
traditional desktop application. We touched in Chapter 2 on the idea of a web application, and
that all dynamically generated web sites are in fact web applications. The application object
represents your site and all of its visitors.
There is one instance of the Application object for each web application running on the web
server. There may be many clients accessing the same application. They each can get a
reference to the same Application object. Next, we will look at an object that is unique to each
client of an application.
The Session object is the most powerful object for continuity when using an application in Active
Server Pages. One of the problems that have existed in creating web-based applications is that
the connection between the client and the server is stateless. The web server itself has no
mechanism for tying a request for a page by a client back to a previous request for a page by the
same client. This means that each request that one client makes of a web server is treated
independently from the rest. While this allows for a very efficient and fast web server, it makes
writing applications nearly impossible.
Think of it this way. If you are writing an application using a standard web server, then every
request to the server must carry along with it everything that you have done related to the
application up to this point. Since the web server has no way of storing and retrieving that
information, it is up to you to provide it every time you make a request of the server. Sounds
pretty cumbersome? Well, with the Session object, Active Server Pages allows you to store and
retrieve information about the client accessing your application. Nevertheless, this is just to whet
your appetite. Stay tuned for Chapter 8 when the Session object will be explored in detail.
Page Scoped Objects
As we traverse our way through the object model, we now move from the Session level down to
the individual page level. When working with pages of the application, we need to look at the
basic function of a web server.
Basically, a web server operates by receiving a request from a client and then sending a
response back to it. This request could be for an HTML page, or it could be the data from a Form
submission that the user has made. To make our pages dynamic in ASP, we need to take the
information that has been submitted and craft a customized response to send back to the client.
Active Server Pages provides two objects that allow you to interact at the page scope. The
information that is sent from the client to the server is held, or encapsulated, in the Request
object. The information that the server prepares to send back to client is encapsulated in the
Response object. These two objects are good examples of objects that have physical
manifestations, like our telephone. They are modeled on the HTTP request and response.
The client asks the server to create an html page, by requesting an .asp script. When the server
sees this request, it interprets this type of page as an Active Server Page. All of the information
that the client is sending along with the request is then packaged into the Request object. This
information is then accessible to the actual ASP script that is used to construct the page.
The information is categorized into five sets of information. Since each set of information can
include multiple individual pieces of information, each set is stored as a collection. In a
collection, each piece of information is stored as a name-value pair. We talked about name-value
pairs earlier when we introduced object properties.
The collections in the Request object will be explained in detail in Chapter 7, but we will quickly
introduce them here. The collections hold information about:
The values that are provided in the URL that are sent by the client. In the URL, the client
can include name-value pairs of information after the file name. This information is stored in
the collection called QueryString.
If the client is sending a Form request, then the values of the form elements are stored in
another collection – the Form collection.
The web server itself has a great deal of information about the request, response and
general information about the server itself. These are called the HTTP Server Variables.
This information is made available as a collection as well.
If the client is sending any cookies along with the request, these are included in their own
collection.
In addition, if the client is sending any security certificates to the server, then these are
included in their own collection.
By using the information that is included with the request, along with the script code in the Active
Server Pages script file, the server can dynamically generate a page for the client to display. In
order for the client to display the information, the server needs a mechanism to relay the data
back to the client. This is the job of the Response object.
The Response object provides the ASP script with a set of interfaces that allow the script to
control what information is being sent back to the client. The details of the Response object will
be covered in Chapter 7. For now, we will just touch on some of the functions that the Response
object provides.
These interfaces give the designer of the script the ultimate flexibility to decide how the
information is presented back to the client.
When using applications made of out of components, its common to use transactions. If, for
example, an action handled by a particular component fails, then you'd want details of the failure,
and be able to take an alternative course of action. If a user tried to change details of their bank
account and then bombed out mid-track, it would be logical to want track back to what the bank
account details were previously, before trying to change the details again or continuing on
alternative course.
The second type of application that uses transactions would be one that features data
processing. If someone makes an alteration to a database via a web page, and somebody else
makes another alteration at the same time, you need to be able to accept one alteration, while
canceling, or postponing, the other. The management of these types of transactions was handled
in IIS 4.0 and PWS 4.0 by a piece of software known as Microsoft Transaction Server (MTS).
However, with IIS5 and Windows 2000, the functionality of MTS is now integrated directly into
part of the Windows 2000 operating system known as COM +.
The ObjectContext object allows access to MTS in order to start or terminate a transaction.
We don't want to go into how it does that now, that's a topic for later in this book, but this
hopefully gives you an overview of this useful object.
We can use the object model as a road map that lays out:
Where information should be stored in our applications
How information specific to a single user can be tracked
How to set up client pages to send the appropriate information to the server for it to
dynamically build a page
How to dynamically build a page, using all of the features that ASP provides, and then
send that page back to the client
In the next few chapters, we will be walking through this landscape in detail and building up the
expertise needed to put together an application using Active Server Pages.
Summary
In this chapter, we have introduced the concept of objects. For our purposes, an object is a
programmatic tool that enables us to access a physical item, or a set of associated data. An
object is described by its interfaces. These interfaces are broken into three categories:
Properties – a property's value holds data that describes an attribute of the object.
Methods are used to have the object perform some task for us.
Events let the object notify us that something has happened and can also be raised to
make something happen and can contain code that we write that can be executed.
With the concepts of objects well in hand, we introduced the Active Server Pages object model.
These seven objects encapsulate the functionality offered by the ASP server:
The Server object provides basic functionality across the web server.
The Application and Session objects provide the application functionality that is not
present in a basic web server.
The Request and Response objects are used to interpret the information sent by the
client and then construct the HTML page that will be sent back in response.
The ObjectContext object, is used to control transactions within a web page.
The Error object handles any ASP-generated errors
By understanding the relationships of these objects to one another, we can start to build true
applications by using the power of Active Server Pages. The next few chapters will begin to show
us how.
Chapter 7: The Request and Response Objects
Overview
We'll now delve into the Active Server Pages object model. This chapter will consider two almost
indivisible objects, Request and Response. These two objects handle communications between
the browser and the web server. They are central to the Active Server Pages object model, and
are vital for controlling how the user sends information to the server – via forms, for example, and
how they get details back, such as a message saying "Thank you Mr. Ullman, your details have
been submitted correctly".
Using the Request object, your ASP script can obtain information from the server about what the
user wants: this information can then be manipulated within ASP. The Request object allows
ASP to deal with all of the information that is sent to it from the client. This information can include
form data, querystring data, browser data, client certificate data, authentication data, content
length, request method, URL, and even cookies. With access to all of this information, we can
now use the scripting power of ASP to create a customized or personalized page for the user.
Once that page is created we need a way to get the page back to the user's browser; to do this,
we use the Response object. While the Request object deals with everything sent from the
client to the web server, the Response object will allow us to deal with everything sent from the
web server back to the client.
Let's do a very quick retread over what happens when a web browser or another client application
asks for a page from a web server. This is known as making a request. In order to make a
request from the server, the client must supply the specific page using an address (usually a
URL); in addition, the client can send a whole host of other information to the server. The
Request object is responsible for packaging all this information, and making it easily accessible
to the ASP application. When the client asks the server to provide a page with an .asp suffix, this
suffix tells the server to process any server-side scripting as part of an ASP Page. All of the
information that the client sends along with the request is then packaged into the Request
object. This information is then accessible to the ASP script that is used to dynamically construct
the page that the user has requested.
name=chris
luckynumber=23
date=1/1/99
If there are three fields in a form then the form collection will contain three name/value pairs. For
each, the name comes from the <INPUT> tag's NAME attribute and the Value comes from what
the user typed in the corresponding text box, or selected from the list box (or if no typing or
selection, the default VALUE attribute). This information is all included in the request.
By using the information included with the request, along with the scripting logic in the ASP page,
the server can dynamically generate a page for the client to display. In order for the client to
display the information, the server needs a mechanism to relay the data back to the client. This is
the job of the Response object. Let's see how you'd go about returning information to the client
using the Response object.
There are two main ways that you can use the Response object to send text back to the client,
which can be displayed on your web page. Both should be fairly familiar to you now – they are in
fact two faces of the same beast. One way is by using the Write method of the Response object
and the other way is a shortcut notation of Response.Write.
The Write method of the Response object allows you to display information on your web page.
This information can be the contents of a server-side variable, the return value from a function, or
a string constant. No matter what the information, the syntax of the method is consistent:
Response.Write value
The contents of value can contain any valid information that can be output in an HTML file. In
other words, it is up to the developer to make sure that whatever data is contained in value can
be properly displayed on a client browser. We'll be seeing many examples throughout this
chapter, demonstrating how to use Response.Write.
Using Shortcuts
You can use a special 'shortcut' version of Response.Write to send information to the client.
You can use shortcuts for outputting information to the client using a special form of the standard
script delimiters. When adding ASP script to a HTML page, you use the <%….%> to indicate what
is script and what is HTML. The shortcut method allows you to use a modified version of the
script delimiter, <%= … %> as a shortcut reference to Response.Write. For example, to use the
shortcut method to write the contents of the variable value as we did above, you would write:
<%= value %>
This would result in exactly the same information being written to the HTML output stream as we
would get with this statement:
<% Response.Write value %>
The shortcut method is best used when you have a block of HTML code into which you want to
insert a single piece of dynamic code. As you can see in these two examples that do the same
thing, the shortcut method really reduces the amount of code that you have to write:
<BODY>
The time is now:
<%
Response.Write Now
%>
</BODY>
Now, if you were to rewrite this using the shortcut method, you can see the savings in code that
you would gain:
<BODY>
The time is now: <%= Now %><P>
</BODY>
However, the shortcut method is not always the most efficient method in terms of performance. If
you have a big block of ASP script code and want to output information to the client, then using
the Response.Write method directly would be the way to go. The performance of a page will
drop if you use the shortcut method because of jumping into and out of script, creating the
resultant HTML bit by bit, rather than continuously in one script block. In a larger scale situation,
your users will notice a drop in speed. In many situations, using the full notation also improves the
readability of your code, which will make it easier to remember what you were doing when you
look at the page two months later.
Here are a few quick notes about what you can and can't do with Response.Write
You can't have more then one line in the shortcut. The following is not acceptable
<%= "Hello world"
="Hello world%>
You can concatenate two strings in a Response.Write
strOne = "Hello"
strTwo = "World"
Response.Write strOne & strTwo
You can calculate the result of a mathematical expression
intInteger1 = 26
intInteger2 = 456
Response.Write intInteger2/intInteger1
You can have a value which is from a Request object
Response.Write (Request.QueryString("Query1"))
The use of these two methods is fairly self-explanatory. Basically, whatever information you pass
as the parameter to the method will be sent back to the client for display. This information can be
a string constant, a variable, or the value of a built-in function. Anything goes, as long as the
output is something that the browser will understand.
You can also see in this example that while you can use both Response.Write in full and the
shortcut method to output information to the client, the context in which you output the information
should determine what method you would use. Given a choice like this, most developers will
choose the method that is the easiest to read and that also requires the fewest keystrokes. None
of us likes typing, but it goes along with the job!
Let's now take a look at each of the collections (minus Cookies) in the Request object.
A querystring's name/value pair is composed of two strings: the name and the value. These two
strings are separated by an equal sign, '='. If the request generates more than one querystring
name/value pair, then subsequent name/value pairs are separated from each other by an
ampersand, '&':
?username=DaddyKool&email=DaddyKool@kool.com
ASP retrieves the values of the variables that are given in the HTTP query string and stores them
in the QueryString collection. The QueryString collection returns exactly the same
information as the Query_String variable in the same format. So, <
%=Request.Querystring%> returns:
username=DaddyKool&email=DaddyKool@kool.com
and
username=DaddyKool&email= DaddyKool@kool.com.
The first is by clicking on a hyperlink generated with an anchor tag, <A>, which already has an in-
built querystring:
<A HREF="somepage.ext?name=value">a querystring example</A>
This hyperlink, when clicked, generates a querystring variable named "name", with the value
"value".
The second situation is when a form is sent to the server by the GET method. Take a look at the
following code:
<FORM NAME=logging ACTION="RequestQuery.asp" METHOD ="GET">
Type your first name: <INPUT TYPE="TEXT" NAME="FIRST"> <BR>
Type your last name: <INPUT TYPE="TEXT" NAME="LAST"> <BR>
<INPUT TYPE="SUBMIT" VALUE="Login">
When the browser displays an ASP page containing this code, it might look something like this:
When the user enters his username and password, and clicks on the Login button, two
querystring name/value pairs are generated. They correspond to the names of the form's input
textboxes and their respective VALUE attribute values. In other words, a name/value pair is
generated for every named INPUT element Hence, the URL requested by these actions might
look like this:
http://chrisu/BegASP/RequestQuery.asp?first=john&last=doe
Note It's important to note that the POST form method won't allow you to retrieve
querystring name/value pairs, because this stores information in a different
collection. We'll look into the POST form method shortly.
Note The main difference between POST and GET is that POST sends the information
as part of the request body, while GET sends information by appending it to the
Uniform Resource Locator, (URL).
The final method is, very simply, via a user-typed HTTP address:
When the Enter key is pressed, two name/value pairs are generated, namely first=john and
last=doe.
You can display the contents of a querystring in several different ways. In its simplest form, you
could display it with just the following statement:
<%= Request.QueryString%>
We've already discovered that the equal sign was just a shorthand notation for
Response.Write. So equally you could write the above as:
<% Response.Write(Request.QueryString) %>
We'll use the equal sign shorthand for the time being, as it distracts less from the querystring
code. Either of the above lines will return all of the querystring name/value pairs. Thus, if the
name/value pair was equal to username=DaddyKool&pass=ChangeMe then this command
would return
"username=DaddyKool&pass=ChangeMe".
In fact, it's more common to use the following:
<%= Request.QueryString("property_name")%>
This will return a specific value, corresponding to the property_name, providing it holds a single
value. Thus, you can retrieve information for the string variable name using the following code:
<%= Request.QueryString("name")%>
As this goes through the loop, it displays the values for each name/value pair in the
QueryString collection, for example:
Item – DaddyKool
Item – ChangeMe
In such a situation, you might need more than just a name/value pair to keep track of your data.
Help is at hand in the form of the Count property, which is used to track the number of
querystring name/value pairs. Let's see an example of this:
<%= Request.QueryString("name").Count %>
This line of code will return the number of querystring name/value pairs that have the name
"name". So if the name/value pair was name=DaddyKool, then this code would return the value
1. If the name/value pair was name=DaddyKool,Junior then the code would return 2; and so
on.
Note Note that there is also a global count which returns the number of query strings
held in total:
Request.QueryString.Count
How can we use this to get hold of one specific value from a set of values? The answer is to use
an index. An index enables you to retrieve one of multiple values for a given property name, and
can be any integer value in the range 1 to
Request.QueryString("property_name").Count.
To use an index for the property name would require code with the following format:
<%= Request.QueryString("name")(index_val)%>
Of course, index_val is an integer value between 1 and the value held in Count (this is
different from a conventional array which starts from 0.) If the value of index_val doesn't
correspond to a valid index value (that is, index_val doesn't fall between 1 and Count), then
ASP returns an error similar to the following:
44. Now select one or more destinations (Hold down the Ctrl button while clicking to select
more than one) and click on the Submit button. The result is:
The full URL displayed on this example is:
http://My_Server_Name/BegASP/ResponseQueryString.asp?
HolidayLocation=Madrid&
HolidayLocation=Rome&HolidayLocation=Berlin
How It Works
We'll now look at how the ASP code works, and what is contained in the querystring. In the first
page RequestQueryString.htm we set the ACTION attribute to the page we wish to transfer
control to, and we set the METHOD attribute to GET. By setting the METHOD to GET we ensured that
our information is transferred by query string:
<FORM ACTION="ResponseQueryString.asp" METHOD=GET>
<SELECT SIZE=3 NAME="HolidayLocation" MULTIPLE>
<OPTION>Madrid</OPTION>
<OPTION>Rome</OPTION>
<OPTION>Paris</OPTION>
<OPTION>Berlin</OPTION>
<OPTION>Moscow</OPTION>
<OPTION>Birmingham</OPTION>
</SELECT>
<INPUT TYPE="SUBMIT">
</FORM>
The rest of the code is straightforward. If you're unfamiliar with how forms transmit information via
list boxes, radio buttons or the like, we suggest that you look at Appendix F where there is a
detailed tutorial on the subject. One thing to note is that we name the list box HolidayLocation
in the code for the form, this is important as we reference it in the next page.
Notice that we reference our querystring using the name assigned to the <SELECT> element
created in the previous form, here. If the count is zero then we display the appropriate details:
Response.Write " won't send you any details "
Otherwise, we get the server to display the contents of the HolidayLocation query string:
Else
Response.Write "We will send you details on "
Response.Write Request.QueryString("HolidayLocation")
End If
%>
as requested.
In the example above, this will hold the values "Madrid, Rome,Berlin" – this of course will be
different if you entered different places.
This is where index values come in. We can use an index to query the name property: these
queries allow us access to each of the values within name separately. Therefore, you could
access the first element with the line:
<%= Request.QueryString("HolidayLocation")(1)%>
Here, Request.QueryString("Name")(1) holds the value "Madrid". The second value can
be accessed using:
<%= Request.QueryString("HolidayLocation")(2)%>
where Request.QueryString("Name")(2) holds the value "Rome". You could then store
each one in a separate variable:
strHolidayChoice1 = Request.QueryString("HolidayLocation")(1)
However, if you were using a long list of locations, this wouldn't be practical. So one proposed
amendment to our program would be to loop through the QueryString collection, using the
index property. As previously noted though if you access an invalid index value, you'll generate
an error. The way to avoid this is to then loop only as far as the maximum number of items in the
querystring. We can use the Count property to do this. If you were to replace the following code
in our last example like this:
Dim intLoop
If Request.QueryString("HolidayLocation").Count=0 then
Response.Write "We won't send you any details "
Else
For IntLoop = 1 To Request.QueryString("HolidayLocation").Count
Response.Write "<BR>We will send you details on "
Response.Write (Request.QueryString("HolidayLocation")(IntLoop))
Next
End If
Alternately, if you didn't want to separate out each value, you could access the whole value
contained within the querystring:
<%= Request.QueryString%>
This holds the complete QueryString collection, which is shown on the resulting output:
"Name=Chris&Name=Ullman&Pass=secret". The final line of the script does exactly the
same thing:
<%= Request.ServerVariables("QUERY_STRING")%>
Hence, this holds the exact same value as Request.QueryString, the complete querystring
collection:
"HolidayLocation=Madrid&HolidayLocation=Rome&HolidayLocation=Berlin"
Now that we've covered every facet of the QueryString collection, we'll move on to a related
collection, Form.
Of course, text boxes aren't the only method of passing information on forms. In fact there are
many different elements that you can use within a form to send information, such as:
Text Boxes
Checkboxes
Option buttons
Lists and their variations
Hidden fields
Text Area
If you're unsure of how any of these work within a form in HTML, we suggest you go to Appendix
F, which provides a comprehensive tutorial on all of these features in forms and how the Form
collection can be used to extricate information from them.
Assuming you are familiar with these form elements, then we'll only be looking at one example
here of how you can retrieve information from the Form collection, as the method of retrieval is
almost identical to that used with the QueryString collection. While there are several
differences between GET and POST forms in terms of sending data, the only difference between
them in using the data once it is in the Request object is the name of the collection itself. All
other aspects are same.
Again, this collection can be used together with the Count property, which enables the
calculation of the number of values in a given name/value pair; also, the Form collection can use
an index to access the individual values within a set of multiple values.
Items in the Form collection are also, like the QueryString collection, composed of name/value
pairs. To construct items in a Form collection we follow a similar procedure to that used when
creating items in the QueryString collection – that is, with two strings separated by an equal
sign, "=". The first string is the form element's name, and the second string is the element's value.
Again, multiple form name/value pairs in the form collection are separated by an ampersand, "&".
The main difference between a QueryString collection and a Form collection is that while the
QueryString collection gets appended to a URL (with a question mark), the Form collection is
sent as part of the HTTP request body and hence will not show up in the URL window in the
browser. Note, also, that there's only one way to generate a form collection – by submitting the
form using the POST method.
The best way to understand the Form collection is to dive in and take a look at another example.
Please select one or more destinations that you would like brochures
for:
<FORM ACTION="ResponseForm.asp" METHOD=POST>
How It Works
The program looks the same, and it runs in the same way; it even returns the same answers. So,
what's different? The answer is, very little. All we've done is changed every occurrence of
Request.QueryString to read Request.Form:
<%
If Request.Form("HolidayLocation").Count=0 then
Response.Write "We won't send you any details "
Else
Response.Write "We will send you details on "
Response.Write Request.Form("HolidayLocation")
End If
%>
The only noticeable difference in the whole program is that the information is no longer passed to
the server as part of the URL. Behind the scenes, you might also notice that you don't get a
querystring attached to the URL.
As expected (just like in the querystring example), that code displays the contents of
HolidayLocation on separate lines:
For explanations on how to extract information from different form elements and how to use it in
the Form collection, we refer you to Appendix F.
It's probably best to start with a list of all the ServerVariables available, but rather than list
them all in the book, you'll learn more by physically retrieving the contents of the
ServerVariables collection in an example.
The ServerVariables collection holds all HTTP header variables as well as lots of general
information variables. For the querystring values to show, a form must be submitted, and the
form's method must be GET.
You must know the specific name of the variable you want to see. You can get the names from
the table above or from Appendix I.
In our example, to retrieve every ServerVariable within the collection, we use a For
Each...Next loop to iterate through each of the items in the collection.
<% For Each key in Request.ServerVariables
In this case, the variable key is used to store each different Server Variable name in the
collection – so for the first iteration, the value in key is ALL_HTTP, in the second iteration it's
ALL_RAW, and so on. For each unique value of key this loop will execute the following lines:
If Request.ServerVariables(key) = "" Then
Response.Write " "
Else
Response.Write Request.ServerVariables(key)
End If
If the variable key is empty then this displays a space, otherwise the formatting will be messed
up; otherwise, it displays the value stored within the Server Variable contained by key.
In order to do that, the secure sockets layer (SSL) protocol must be used. SSL is a variation of
the HTTP protocol that has much higher levels of security. The latest version of the SSL protocol
is SSL3.0/PCT1. The acronym PCT stands for Private Communication Technology.
Using SSL/PCT allows server and client authentication, encryption, and the use of data integrity
methods. Authentication ensures that the data is being sent from an 'approved' client to the
correct server. Encryption ensures that the data can only be read by the server it is intended for
or read by the client it is intended for. Data Integrity ensures that the information sent arrives
unaltered, exactly as it was sent. When the SSL protocol is used, URLs are prefixed by
https:// (instead of http://). You will also need to connect to the server through a different
port.
Before you can use the ClientCertificate collection, you must configure the web server (In
IIS 5, from the Default Properties dialog, and select the Directory Security tab) so it can request
client certificates, otherwise the ClientCertificate collection will be empty.
Note MS Certificate Server (CS) will not run on PWS/Win95 or Win98 because
Windows95/98 cannot provide a secure environment. CS will run on Win 2000,
NT 4.0 Server or Workstation. Coverage of this subject is beyond the scope of
this book, but you can find more details in Professional ASP 3.0 – ISBN –1-
861002-61-0, from Wrox Press.
Once the web server is enabled and configured, only SSL-enabled clients will be able to
communicate with the SSL-enabled WWW folders.
Two constants need to be declared when working with the ClientCertificate collection:
Const ceCertPresent = 1
Const ceUnrecognizedIssuer = 2
As with all Request object collections, you can iterate through the ClientCertificate
collection's values:
<%
For Each key in Request.ClientCertificate
Response.Write( key & ": " & Request.ClientCertificate(key) &
"<BR>")
Next
%>
Here, key specifies the name of the certification field to retrieve. A client certificate may have
these fields:
Key Meaning
Certificate A string containing the binary stream of the entire certificate content in
ASN.1 format (a list of numbers separated by a period, e.g.
(168.77.243.12)
Flags A set of flags that provide additional client certificate information.
CeCertPresent - A client certificate is present.
CeUnrecognizedIssuer - The last certification in this chain is from an
unknown issuer.
Issuer A string that contains a list of subfield values containing information
about the issuer of the certificate. If this value is specified without a
SubField, the ClientCertificate collection returns a comma-
separated list of subfields. For example, C=US, O=Verisign, etc.
SerialNumber A string that contains the certification serial number as an ASCII
representation of hexadecimal bytes separated by hyphens (-). For
example, 04-67-F3-02.
Subject A string that contains a list of subfield values which contain information
about the subject of the certificate. If this value is specified without a
SubField, the ClientCertificate collection returns a comma-
separated list of subfields. For example, C=US, W=Wrox, and so on.
ValidFrom A date specifying when the certificate becomes valid. This date follows
VBScript format and varies with international settings. For example, in
the U.S., it could be: 6/31/98 11:59:59 PM.
ValidUntil A date specifying when the certificate expires.
SubField is an optional parameter you can use to a retrieve an individual field in either the
Subject or Issuer keys, or both. This parameter is added to the Key parameter as a suffix. For
example, IssuerC, SubjectCN, SubjectS, SubjectL, etc.
Value Meaning
The following script examples display all the fields of a client certificate:
Issuing organization: <%= Request.ClientCertificate("IssuerO")%><br>
Subject Name: <%= Request.ClientCertificate("SubjectCN")%><br>
Valid from: <%= Request.ClientCertificate("ValidFrom")%><br>
Valid until: <%= Request.ClientCertificate("ValidUntil")%><br>
Serial Number: <%= Request.ClientCertificate("SerialNumber")%><br>
Issuer: <%= Request.ClientCertificate("Issuer")%><br>
Subject: <%= Request.ClientCertificate("Subject")%><br>
<% TheCompleteCertificate = Request.ClientCertificate("Certificate") %>
The information could be from a querystring, form, or cookie etc. One thing to be careful with is to
understand the order in which ASP organizes the information in the Request collection. This
becomes important if you have a querystring variable with the same name as a form field or
cookie. ASP will search through the collections in the following order:
QueryString
Form
Cookies
ClientCertificate
ServerVariables
So, for example if you have a querystring with the same name as a form field, the value in the
Request collection will be the value from the querystring. Therefore, it is good to develop the
habit of never using the same name for a cookie and a Form Field Name. Just prefacing your
cookie names with a "c" will do the trick.
You might be tempted to say, so what, when am I going to use such a property? Well then, read
on.
Unlike the Form or QueryString collections, this method allows you to take the raw data from
the form and parcel it up in a safe array of bytes. The problem is a safe array of bytes isn't exactly
the most useful or user-friendly format and if you try to display the information directly using
Response.Write, you will generate an error. If you wish to use the whole block then you need
to know when the whole block of data has been transmitted in its entirety, and this is where the
TotalBytes property comes in. The TotalBytes property is passed to the BinaryRead
method as a parameter to indicate when to finish reading the data it has received, as follows:
<%
Dim bread, bytecnt
bytecnt = Request.TotalBytes
bread = Request.BinaryRead(bytecnt)
You can then use a special VBScript function, MidB to 'unwrap' the information from the
individual bytes of the array.
For i = 1 to bytecnt
Response.Write MidB( bread, i, 1 )
Next
%>
Then the information from the form can be used. The point is that we cannot mix and match
binary read with standard form manipulation. So bearing this in mind, let's take a look at a quick
example. In this example, we'll pass a first name, last name and password over and use the
binary method to unwrap it. This is pretty much what we've already done with the Querystring
and Form collections, and if you were using plain text, then you'd use one of these two collections
ahead of this method. However when transferring something other than pure text (zipped file for
example?), then the following method will come in useful.
How It Works
Ok, we could have returned the contents of the Request.Form collection in this example and
saved us all some code. The point however is to demonstrate that if you wished to use the form
to upload some other file format apart from raw text, such as a graphics file, you could use
BinaryRead to unload it at the other end.
However, when we got back the information we needed slightly more code to unwrap it. We
started determining the total number of bytes in the file and assigned it to the variable
intByteCnt:
intByteCnt = Request.TotalBytes
We then used the BinaryRead method, passing the Total Bytes count as a parameter:
intBread = Request.BinaryRead(intByteCnt)
Then we were able to loop through our data, one byte at a time, using the MidB function
described previously. In this way, we were actually displaying one character at a time on the
screen:
For intLoop = 1 to intByteCnt
Response.Write MidB( intBread, intLoop, 1 )
Next
An important thing to note is that once you have called BinaryRead, you cannot query the other
Request collections directly and vice versa, you cannot call binary read after calling the
Request collection – the data is effectively transformed to a binary format.
The Response Object
As we have briefly mentioned earlier in the book, the Response object is used to send the
server's output to the client. In this sense, the Response object is the counterpart to the
Request object: the Request object gathers information mainly from the client (but also from the
server), and the Response object sends, or resends, the information to the client by writing to the
outgoing page. Whereas the Request object is rich in information and scarce in properties and
methods, the Response object has only one collection but gives us many properties and
methods with which to build pages.
These features allow you to use ASP scripts to flexibly control how information is presented to the
client. We've already dealt with how you can send information back to the client using
Response.Write (or its shortcut); otherwise sending information to the server would have been
a seemingly pointless process, as you wouldn't have been able to get any information back. What
you might not know is that you can specify when you wish to send that information back.
Let's step back a bit and take a look at what the HTML output stream is. It's a dynamically created
queue of information that is waiting to be sent back to the browser. If you go back to our initial
definition of what ASP is supposed to do, then you know that its primary job is to create an HTML
page that can be displayed by a client. Although an ASP script can do much more than static
HTML, whatever we want to put on the client's screen has to be displayed through the creation of
an HTML page. ASP dynamically builds HTML pages to be displayed on the client's browser.
When an ASP script begins, it also creates an empty HTML output stream ready for the
HTMLpage to be created in. So the stream can be thought of as a holding bin, where the web
server builds a dynamic HTML page, and then the stream is sent down to the client. This
temporary storage space where the stream is held is known as a buffer. The first method that
ASP scripts use to add information to the output stream is using the Response object to set the
HTTP headers. We will cover that later in the chapter. First, let's talk about the HTML output
stream in general terms.
At the simplest level, the HTML output stream is always built in the same way: the stream is
created with the first bit of data sent, and when new information is added to it, it can only be
added to the end. It's like a simple queuing mechanism. If you got to the local bank, and join the
queue, the people who got in before you are served before you. The HTML output stream is also
a queue, one that is waiting to be sent to the client. Therefore, it has to be created in order, with
required tags being sent in order to the browser.
This means that when we send the HTTP headers to the buffer, the HTTP header information has
to be written to the buffer first. Once the headers have been sent to the buffer, we can start to
send the contents of the HTML page to the buffer. The easiest way to do this is with native HTML
in the ASP script file. Any HTML that is not created by the code within the <%…%> tags will be
added to the HTML output stream. The diagram here illustrates this:
So the buffer is the place where the ASP uses the ASP script to generate a dynamic HTML page.
As we just mentioned, the buffer starts out as empty when the script begins. As the script is run,
information that is destined for the client is placed in the buffer, along with static HTML. The order
in which information is placed in the stream is the same order that it will be sent to the browser.
Once all of the ASP scripts have been processed, then the contents of the buffer is sent to the
browser in one fell swoop.
This is fine for most pages, but what if your ASP script is going to take a long time to process?
For example, maybe you are trying to access information from a remote system. Since the HTML
output stream will not be sent to the client until the script has completed, the browser will just
appear to be spinning its wheels waiting for the server to respond. In this case, we may want to
send some information to the client, to let the user know that the script is processing. Then once
the processing has completed, we can send all of the information back to the client.
To do this, we will be manually controlling the way that ASP buffers the HTML output stream.
There are one property and three methods that will allow us to control when the output stream is
sent to the client. These are: Buffer, Flush, Clear, and End. We'll look at each in turn.
Buffer
The Buffer property of the Response object is used to tell ASP that we will be manually
controlling when the HTML output stream is sent back to the browser. Buffering is, by default,
turned on. To turn it off, we would need to set the value of the property to False:
Response.Buffer = false
Response.Buffer has to be inserted after the language declaration (if one is used), but before
any HTML is written (that is, before any output is generated):
<%@ LANGUAGE="scriptinglanguage"
Response.buffer = false %>
<HTML>
...
The Buffer property cannot be set after the server has sent output to the client. For this reason,
the call to Response.Buffer should be the first line of your asp script (not the first line of the
file though).
Flush
The Flush method sends any previously buffered output to the client immediately, but continues
processing the script. This can be useful for displaying partial results before your script finishes
processing so that your user does not get impatient while waiting for the full result of a long query.
Flush causes a run-time error if the Response.Buffer property has not been set to true.
Clear
The Response.Clear method erases any already-buffered HTML. However, it only erases the
response body and does not erase HTTP response headers. It will only erase information that
has been added to the HTML output stream since the last call to Response.Flush. If you have
not called Response.Flush, then it will erase all of the information that has been added since
the beginning of the page, except the headers.
Clear will cause a run-time error if Response.Buffer has not been set to true.
End
The End method causes the server to stop processing the script and send the buffered output.
Any further script instructions are not processed, nor is any remaining HTML sent. Calling
Response.End flushes the buffer, if Response.Buffer has been set to true.
26. View the source of the ASP file so that you can see exactly where the Response.End
was called, as no further HTML was sent either.
How It Works
The first step in taking control over how the HTML output stream is sent back to the browser is to
turn buffering on:
<% Response.Buffer = true %>
<HTML>
By setting the Buffer property of the Response object to true, we are telling ASP that we will
be controlling when information should be sent to the browser. This statement needs to be before
any statements that may cause information to be written to the HTML output stream:
Let's send some text to the HTML output stream.<P>
It is waiting to be sent - Let's send it.<P>
<% Response.Flush %>
Now we want to output some HTML text to the browser. We are doing this by inserting our HTML
code at a point in our code that is after the start of the ASP code and before the end of the ASP
code. To do this we have to make sure we close and reopen our <% ... %> script delimiters
appropriately. Even though we are not using one of the Response object's methods to send
information back to the client, this HTML is still being written to the HTML output stream.
Once we have written some text, we want to send it immediately to the browser. This is done by
calling Response.Flush. When ASP encounters this statement, it will immediately send
whatever information is in the HTML output stream to the client, before clearing that information
from the stream.
Now we want to send this to client<P>
Oops, we just changed our minds - let's clear it<P>
<% Response.Clear %>
Next, we want to output some more text to the browser. But this time, after adding the text to the
HTML output stream, we decide that we really didn't want that text after all. There is no way to
selectively remove it from the output stream, but we can erase everything that is already in the
stream. To do this, we will call the Clear method of the Response object. If you look at what the
browser is displaying, or even the source HTML for what the browser is displaying, you won't find
these two lines of text there.
<%
Response.Write "We can control the output of Response.Write method
too<P>"
Response.Flush
%>
All of the information that is sent to the HTML output stream, whether from a method of the
Response object, such as Write, or from native HTML, is under our control when we have
enabled buffering. In this step, we are adding information to the output stream by using the
Response.Write method. After outputting the line of text, we will send it to the client by again
using the Flush method.
I think we are finished - let's end it.<P>
<% Response.End %>
Wait a minute - I wanted to say this, but it is too late!
</BODY>
</HTML>
We then want to send some final information to the browser. Once we have added it to the output
stream, we call the End method of the Response object. This tells ASP that we have sent all of
the information that we want to the client. Its job is to send the remaining contents of the HTML
output stream to the browser, then stop processing any more script.
If you look at the source HTML for the page that ASP generated, you will see that the last line of
code is the line just before the call to Response.End. None of the other information that was in
the ASP script, and neither the terminating </BODY> and </HTML> tags, have been sent to the
client. So, in this case, calling End really does mean the end.
The use of these methods is a bit more complex than just sending information back to the
browser. They provide us with increased flexibility, and are worth discussing a little further here.
The browser will fill this cache up with the information that is downloaded from various web sites.
Later, if the user returns to a site they had already been to, the browser can check to see if the
page that the user is requesting is in its cache. If it is, then the page can be displayed
immediately, rather than having to wait for it to download from the server. Web site developers
can use this cache to their advantage as well. HTML pages are not the only items that are
cached. All JPG and GIF images are also cached. Knowing this, web site developers can reuse
graphics in their site, so that the only time they have to be downloaded is for the first page that
uses them.
When the browser is asked to display a page that it already has in cache, it will check that cached
page to see if it has expired. A web page developer can set the expiration date of a page so that
they can ensure that the person viewing the page is always seeing the latest content, but not
forcing them to download the page every time.
As an ASP developer, we have a bit of a dilemma. Our pages are dynamically created, so they
are, in effect, different pages every time. Even if the underlying data that generated the page
hasn't changed, a new page is always freshly created to send to the browser. There are many
cases where the page will only change on a periodic basis. We would like the clients viewing the
page to be able to take advantage of the caching that their browser offers. There are two
properties of the Response object that we can use to control the expiration of the page that is
generated. These properties are Expires and ExpiresAbsolute
Expires
The Expires property specifies the number of minutes before a page cached on a browser
expires. If the user returns to the same page before the specified number of minutes have
elapsed, the cached version is displayed.
In this example, minutes represent the number of minutes before this page will expire. Setting
minutes to 0 causes the page to be refreshed with each access. Setting the page to –1442
might be better if you're expecting users from anywhere in the world to browse the page, since all
time zones are covered this way (60 min/hr times 24 hours plus two to cover rounding at both
ends.)
ExpiresAbsolute
The ExpiresAbsolute property sets the date and/or time at which a page cached on a browser
expires. If the user returns to the page before the specified date and/or time, the cached version
is displayed. If no time is set, the page expires at midnight of that day. If a time is set, the page
expires at the specified time on the day that the script is set to expire.
The value of DateTime must be a valid date or time combination. The value must be enclosed
within the # signs. If you set this value to a date in the past, this is the same as setting the
Expires property to 0. Again, if you're expecting users from anywhere in the world, it's best to
set this value to two days into the past to cover all times zones.
Note that both of these properties set HTTP response headers to achieve their task. Therefore,
they both need setting before any text is output to the client.
Redirection
When a client requests a specific ASP page from the server, the script for that page is processed.
As that page is being processed, we may determine that there is a different ASP page that the
browser should actually be displaying. For example, we could have a page that validates a user's
login. Based on the results of that validation, the user could be shown a guest page or a
registered user's page.
We could have the code to create both pages within the same script, but that would make for a
very complicated script file. It would also make it more difficult for another developer to look at the
page at a later time, and understand what we were doing. It would be much easier if we could
check the user's login and, based on what the results were, send the user to a completely
different page.
The old way of doing this was using the Redirect method of the Response object. This method
would tell the browser that it needed to go and fetch a different page. This page could be on the
same site, or it could be on a completely different web site. There are now two newer, more
flexible methods of the Server object, Server.Transfer and Server.Execute, which can
also do the same task. We'll look at these in the next section. However, the Redirect method
isn't totally obsolete, and one important difference is that you can't send a querystring in the
Server.Transfer or Server.Execute methods, as you can with the Redirect method.
This is because the Redirect method tells the browser to send another request, something the
Server object methods can't do.
Another difference is that while Transfer and Execute hide the fact that you've been moved to
another page, the Redirect method displays the new page address in its URL. Also, you may
be have to debug or update a page that contains this method, so it helps to understand it.
Consequently, we will consider all three methods, as there maybe times when you want
redirection to be transparent.
The value of DestinationPage would need to be a string value, and hold a valid URL that the
browser would then be told to retrieve. The destination page should be formatted just as a
hyperlink would. If the URL is on the same site, then a relative reference can be used. If it is on a
different site, then you need to include a full http:// reference.
There are a few things that you need to be careful with when using the Redirect method. A
browser is told to fetch a different page through the use of an HTTP header. The problem lies in
the fact that we cannot modify the HTTP status and add a header to the response after the body
has gone. So, if the ASP script has output the HTTP headers, or has added any information to
the HTML output stream, then calling the Redirect method will cause an error.
Earlier in this chapter, we learned how to control the contents of the HTML output stream by
using the Buffer property. Setting this property to TRUE goes hand in hand with using the
Redirect method. By buffering the HTML output, we can call Redirect almost anywhere, as
long as we clear out the buffer by calling the Clear method before redirecting the browser.
Important Remember, if you are going to use the Redirect method, be sure to set
the Buffer property to True at the beginning of the page.
54. Select Page Number 1 and press the Choose Page button. This should produce:
55. Go back to the PageChoice.html page and choose the other option.
How It Works
The first page that we are creating is a straightforward static HTML page. The job of this page is
to present a standard HTML form that will allow the user to select which page they wish to
display.
<FORM ACTION="ChoosePage.asp" METHOD="POST">
<INPUT TYPE="Radio" NAME="PageChoice" VALUE="Page1" CHECKED>Page Number
1<BR>
<INPUT TYPE="Radio" NAME="PageChoice" VALUE="Page2">Page Number 2<P>
<INPUT TYPE="Submit" VALUE="Choose Page"> <INPUT
TYPE="RESET">
</FORM>
The form displays two Radio buttons, which allow the user to select which page to display. The
results of the form are going to be sent to the choosePage.asp file for processing.
<%
Option Explicit
Dim strChoice
strChoice = Request.Form("PageChoice")
The first thing we want to do is add the Option Explicit statement to help ensure that we
build up the good habit of always declaring the variables that we will be using in advance. The
next thing we want to do, when processing the form, is to grab the selection that the user made in
the previous page.
Once we have declared the variable to hold the user's selection, we will retrieve it from the Form
collection of the Request object. In our HTML form, we had two radio buttons with the same
name, PageChoice, but with different values. Depending on which button the user selected
before submitting the form, the value for that button will be added to the Form collection.
If strChoice = "Page1" Then
Response.Redirect "page1.html"
Else
Response.Redirect "page2.html"
End If
%>
We can then compare the value that was passed from the form with the possible values. Based
on that comparison, we can choose to send the browser to either page1.html or to
page2.html. You will notice that there are no Response.Write statements, or any HTML text
outside of the <% ... %> block. This ASP file is designed to produce no output to send to the
browser: it is simply designed to evaluate some data that is passed to it, and based on that
evaluation, to send the browser that is making the request to another page. In this case, both of
the possible destination pages are static HTML files. They could have just as easily been ASP
scripts, or even pages on a completely different web server. If that were the case, then we would
need to use the complete URL as the parameter to the Redirect method. For example:
Response.Redirect "http://www.wrox.co.uk"
The critical thing to remember from this example is that when you are using the Redirect
method, make sure that you are not sending any information to the HTML output stream. If you
are, then make sure that you turn buffering on, and clear the output stream before calling redirect.
None of the other programs need altering, as choosepage.asp simply works on a value passed
from an 'entity' called PageChoice, whether it's a radio button or a drop down list box. The same
principles apply for different types of list box or check box.
As with all redirection in any .asp file, if the executed .asp file attempts to modify HTTP headers
after it sends a response to the client, it will generate an error. So, let's take a look at the two
methods together. We're going to study them side by side because, apart from one small
difference, they're effectively the same.
The Transfer method transfers execution across to a second page, and executes that in its
entirety, but does not return to the original page.
Server.Transfer(Destination page)
In both cases, the value of DestinationPage could be a string value, or hold a valid URL that
the browser would then be told to retrieve. The destination page should be formatted just like a
hyperlink would. If the URL is on the same site, then a relative reference can be used. If it is on a
different site, then you need to include a full http:// reference.
One thing to note about both of these methods is that for all intents and purposes you are working
on the same page as before. So in other words, the URL in the browser won't change, any
objects you created in the previous page will still work, in fact the scope of the session
(something we'll look at in the next chapter) will also be maintained.
Let's now take a look at an example which demonstrates the use of both methods. We'll use the
methods to include the contents of the pages we created earlier, page one and page two.
Note Note that you will have to have worked through the previous example
where you created two pages, page1.html and page2.html for this
to work.
How It Works
The code is brutally straightforward. We start with Response.Write and finish with a horizontal
line to indicate we're about to execute the first redirection:
Response.Write "We're here on the original page<HR>"
We do the first redirection, using Execute, which lets the Server know that we want to come
back to this point once the next page has finished:
Server.Execute "page1.html"
What we are actually doing is executing the contents of this page (even though this page is only
HTML, it would execute any ASP script contained within):
<HTML>
<HEAD>
<TITLE>Page 1</TITLE>
</HEAD>
<BODY>
</BODY>
</HTML>
Once we have displayed this page, control is returned back to ExecuteTransfer.asp, at the
point we left. So the next line displayed in this screenshot, displays another horizontal line, and a
message indicating we are back on the original page.
Response.Write "<HR>We're back again on the original page<HR>"
This time however, control isn't returned to the original page. So while page2.html is executed
in this example, the final line is never executed:
Response.Write "<HR>We're back again on the original page"
Once page2.html has been displayed the code ends. However, take a look at the browser's
URL line – there's not a hint that we have even left our original page.
So, hopefully you can see that Server.Transfer and Server.Execute offer you a greater
degree of flexibility when redirecting to other pages, and that when you use them, you adhere to
the same rules that govern sending text to the client on the output stream with the
Response.Redirect method.
These functions are defined in Appendix C. They are really beyond the scope of this book, but
you can find more information about them in Professional Active Server Pages 3.0, ISBN 1-
861002-61-0, from Wrox Press.
Summary
The Request object is a conduit for information between the client and the server: it
encapsulates the information that the user sends, and packages it for storage and use on the
server. The Response object has a critical role in handling ASP's transmission of data from the
server back to the client. In this chapter, we started with an overview of the interaction, and
considered the Write method of the Response object. Then we looked at the attributes of the
Request object in some detail. It contains five collections that store information about the user's
request. Briefly, they were:
QueryString – this contains the values that are provided in the URL that is sent by the
client.
Form – this contains the values sent by the client in a form request.
ServerVariables – this contains information about the request and about the server,
stored in the form of server variables.
Cookies – this stores details of any cookies sent with the request, we look at this in the
next chapter
ClientCertificate – this stores details of any security certificates included with the
request
We also looked at the TotalBytes property and the BinaryRead method of the Request
object. We then turned our attention to the Response object and considered:
How to use the buffering methods to control when that information is sent back to the
client
How the Expires and ExpiresAbsolute properties can tell the browser how long or
until when to cache the contents of the page
How the Redirect method can be used to tell the browser to go fetch another page
How we can use Server.Transfer and Server.Execute as alternatives to
Response.Redirect
And some of the other functions that can affect the information being sent back to the
browser
In the next chapter we'll move on to look at two objects that help the server organize Active
Server Pages and track the interaction between the server and the clients: the Application
and Session objects.
Chapter 8: Applications, Sessions and Cookies
Overview
In our travels through the world of Active Server Pages, we have been dealing with how
information can be sent from the client to the server, and how the server can dynamically create a
page that is returned to the client. In all of these interactions, the request and the response
existed by themselves. There has been no mechanism introduced that allows you to tie two
pages, or a set of pages, together.
Until recently, existing web technologies meant that if you wanted to pass information from one
page to another, you were restricting yourself to using cookies in JavaScript, hidden form fields or
querystring parameters. Active Server Pages has extra tools to help pass information between
pages: the Application and Session objects. It also provides collections and methods that
make cookies far easier to use.
In this chapter, we will look at these objects and the power and flexibility they add to ASP. We'll
also look at how cookies have survived the onslaught of time and are still as valid as they ever
were. You will hopefully be able to see the concept of web applications in practice, and how these
objects transform our sites into true applications rather than just a bundle of loosely linked web
pages. Specifically, we will be examining:
Why you can't use HTTP to track an individual user on your web site from start to finish
How to use cookies to store information at the client's computer and what cookies should
be used for.
The Application object, which allows us to tie together all of the pages of a single web
site into a consistent web application.
The Session object, which allows us to treat a user's interaction with the web site within
a specified period of time, as a set of saved variables, rather than just a disconnected series
of page requests.
Let's start by taking a look at what you might expect from a web application.
Web Applications
As the web is evolving from simply serving up pages, to providing access to dynamic information
from a wide range of systems, the sites that a user may access begin to look more like a
traditional application, such as one written in Visual Basic or Powerbuilder. In ASP, each virtual
directory on the server, which we first saw in Chapter 1, may also be an application as well
(depending on whether you've specifically created it as an application). All of the pages in that
directory, whether static or dynamically generated, are part of that application.
Now with most applications, for example an e-commerce shop front for automobile spare parts,
you'd ideally have a profile of each user. In fact it almost goes without saying you'd want to be
able to track each individual user as you'd want to put together a bill of the items they've
purchased to date, what their address and credit card details are and so on. In fact there's a
range of questions that you'd probably want to know such as:
How many people are coming to our site?
Who are they?
Where are they coming from?
How long are they staying on our site?
Where do they go as they move through the site?
It might come as a bit of a surprise that the HTTP protocol on its own makes no provisions for
doing any of this, as it cannot distinguish between users.
Tracking Users
If you are using a traditional desktop application, like Microsoft Word for example, you start up the
application and open a file to edit. You can continue to make changes to the file until you save
and close it. Each change you make is recorded into the file, whether you are adding new text or
deleting existing text. The time period during which you are editing this file can be thought of as a
session.
There is a connection between you – the user – and the file. All of your edits go to that file (that is,
unless you switch to a different file). You do not have to tell Microsoft Word each time that you
make a change which file you are editing, because the application maintains information about
your current editing session.
Things are different on the web. In contrast to the way that traditional applications work, on the
web the relationship between the client and the server is said to be stateless. In a stateless
environment, the server does not track a client from request to request. HTTP is a client driven
protocol, the client tells the server what it wants, and has to get an answer in the same
connection. It's like writing a letter, then sending it; if you forget to put something in that letter,
then that's too bad – you'll have to mail another letter with that separate request in.
The web server that runs the HTTP service basically sits there and waits for a request to come in.
Typically a client will connect and make a request – the web server will then process the request
and send back an answer to the client. The web server cannot determine whether this client has
made any previous connections before, or has a past history. So, if you want to keep track of a
user, then HTTP on its own is not suitable for the task, since it's not capable of storing this kind of
information. Each request that comes in from a client is treated as if the server knows nothing
about the client. There is no memory of previous connections between the client and server.
Let's consider an example where a user logs on to an e-commerce site that sells books. The user
starts on the home page of the site, and then gets asked to log in. Once they've logged on,
they're directed to a customized home page that will hopefully show a range of books of particular
interest to that user, such as new books by their favorite authors etc. They'll want to be able to
browse a catalog of items, and from that catalog be able to select items to buy, and place in the
shopping basket. This would make a total of at least five separate pages that the user would be
navigating through. It would be ideal to maintain some kind of state for the entire time the user is
logged in. The diagram below illustrates this:
Firstly, you'd want only the user to be able see information about their own choices and own
shopping basket. So you'd need to track a user id and password over several of the pages. Also
when the user selects an item from the catalog, you'd need to be able to add it to the shopping
basket, along with the quantity of the item selected. Then from the shopping basket you'd want to
be able to return and continue browsing the catalog, possibly making further selections and
adding to your shopping baskets, as you jumped between the two pages. Finally, it would be nice
if the whole site was customized to your particular browser wouldn't it?
So, for both the user and for the people who wrote the application, the one request per
connection can be more than a little frustrating. The good news is that ASP introduces several
aids to the developer for tracking users. However the first one outdates ASP itself, and is known
as a cookie. Cookies are used to solve the problem of web sites using an anonymous protocol
system.
Using Cookies
A web site might have had a thousand visitors, but for all the web site coordinator could know,
every visit might have been made by the same visitor! Cookies were introduced as a method of
identifying and marking each different visitor to a web site.
Cookies are text files written by the client browser, containing information sent by a server, which
reside on the user's computer. They store information about the user, and are used by a
particular server (or server within the same sub-domain) that the user has visited previously to
personalize web pages, and determine where a user has been before within the same domain.
They can then be used to keep users up to date with relevant information. Each web server,
when a user accesses it, can send a cookie, which the user must accept if the server is to read
the cookie on the user's machine during future visits. If the user doesn't accept the cookie, it can't
be read by the server in future.
There are all sorts of cookie myths on the Internet. Mostly they revolve around the notion that a
smart programmer can get unauthorized information from a user, violating the user's " right to
privacy". Let's set the record straight. A cookie can only store information, which the user sends
voluntarily or selects on a page and that can only happen if the "accept cookies" option in the
browser is turned on by the user. No one can get your e-mail address or your home address if
you don't voluntarily send the information by filling and submitting a form.
Note Individual cookies on Netscape are limited to 4kb of data. On IE5 the
theoretical size is unlimited. The maximum number of cookies is also browser
specific and once this limit is reached, the oldest cookie will be deleted to make
room for the newest one. So make sure you use cookies judiciously.
Unlike the Form and Querystring collections, the Cookies collection does not have a Count
property but, like the Form collection, it can hold multiple values for the same cookie name. When
this happens, the cookie is said to have keys, and each key holds a separate value.
Domains and servers can only read cookies that they themselves have set. If server X writes a
cookie, then server Y cannot read it. If domain http://Myapp sets a cookie, then domain
http://MyApp2 cannot read the cookies set by MyApp, and vice versa, unless the second
domain is a sub domain of the first. When demanded by the server, the cookie that comes with
the request is read-only. You can set the value for a cookie using the Response object, which
you will learn about later in this chapter.
So display the contents of a cookie in your web page you could use:
Response.Write Request.Cookies("cookie")
Creating Cookies with the Response Object
As well as reading information supplied by a client's cookies, the server needs to be able to write
information to cookies on the client's machine. ASP uses the Response object's features to set
cookies' values.
Until ASP was released, the most common way to set cookies was using CGI or in client-side
JavaScript. The syntax for doing this with JavaScript is fairly complex – even daunting – if you're
not over-familiar with JavaScript. ASP (with VBScript) provides a one-line instruction method to
set and retrieve cookies.
If you use this method to set a cookie, the following HTTP header is generated:
Set-Cookie:YOURCOOKIENAME=somevalue
You can see that the Response.Cookies method is simply a way of sending the Set-Cookie
HTTP header without resorting to complicated code. Therefore you should use
Response.Cookies before you write any data in the response body.
Using Keys
If you add a key value, then you can access this cookie like a collection. This means that one
cookie can have multiple values stored with it.
<% Response.Cookies("cookie")("key") = value %>
If a cookie is used to store more than one value we have to specify which of these multiple values
we want to set. To do this, we refer to it via its key value. The key value is similar to a variable
name. The general syntax for writing cookies with keys is:
Response.Cookies("thesameCookieName")("somekey") = "SomeValue"
Response.Cookies("thesameCookieName")("anotherkey") = "AnotherValue"
Note If you issue another cookie with the same name but without specifying the key,
you will overwrite all cookie values for that cookie's name.
If the cookie theCookie has keys, this statement returns True, otherwise it returns False. To
iterate through the individual values for cookies with keys, use this model script:
For Each Cookie in Request.Cookies
If Request.Cookies(Cookie).HasKeys Then
For Each CookieKey in Request.Cookies(Cookie)
Response.Write(Cookie) & " ."
Response.Write(CookieKey) & " ="
Response.Write(Request.Cookies(Cookie)(CookieKey))
Next
Else
Response.Write(Cookie) & " ="
Response.Write(Request.Cookies(Cookie)) & " <BR>"
End If
Next
To make a cookie persist, i.e., for the cookie to be written to the client browser's hard disk (the
"cookie jar"), you have to set an expiration date for the cookie. The general syntax for doing this
is:
Response.Cookies("Cook").Expires = "July 4, 2001"
Deleting a Cookie
To delete a cookie, set its Expires property to any date prior to today. The easiest way to do
this is to use relative date values, as shown in this example:
Response.Cookies("Cook").Expires = Date - 1
Again, this technique could fail due to different time settings on server and client, so maybe
something like Date – 1000 would be more secure.
48. Enter an e-mail address and password. You should also check the Save Login as a Cookie
button to save your login. The press the Login button. Note: we will not be validating the e-
mail and password against anything, so feel free to enter whatever you want.
How It Works
In this example, we are using two ASP files. The first one will display the login screen for the
user. They are asked to enter their e-mail address and password. They can also click on a
checkbox that will have their login information saved as a cookie.
If you look at the code for login.asp, you will see no Active Server Pages code. So you may be
wondering why this file is an .asp and not an .htm. In the next example, we will be adding some
server-side script to this file, so we thought ahead and gave it the .asp name. Once the form is
submitted, the checkLogin.asp page will handle the results.
<%
Dim bLoginSaved
If Request.Form("SaveLogin") = "on" Then
When the form information is passed to the checkLogin.asp page, the first thing that we want
to do is see if the user has requested that their login information be saved in a cookie. We are
declaring a variable called bLoginSaved. This boolean variable will be set to true if the user
wants a cookie set. It will be false if they do not. This will allow us to display a notification later in
the page.
Response.Cookies("SavedLogin")("EMail") = Request.Form("email")
Response.Cookies("SavedLogin")("pw") = Request.Form("password")
Response.Cookies("SavedLogin").Expires = Date + 30
bLoginSaved = True
The name of the cookie we are creating is SavedLogin. It will contain two keys of information.
These keys will hold the e-mail address and password of the user. The values for these keys will
come from the Form collection of the Request object. The information that the user entered in
the fields on the login.asp page will be stored in this collection.
We will be setting this cookie to expire 30 days from today. As you saw earlier, you can use the
VBScript Date function to determine the current date, and then add the desired lifetime of the
cookie in days to that value. The last step is to set the flag that we declared earlier to true. This is
to indicate that a cookie has been set for the user. If the user hasn't set this checkbox, then they
didn't want the cookie saving so we set the Boolean variable to false to ensure we don't save the
information as a cookie.
Else
bLoginSaved = False
End If
%>
In the case where the user did not request that a cookie be set, we will set the flag to false. We
have now reached the end of our ASP script block, so we terminate it with the %> statement. Now
that we have done all of the cookie processing, we can turn to what the user sees.
<BODY>
<%
If bLoginSaved Then
Response.Write "Saving Login information to a cookie<HR>"
End If
%>
Thank you for logging into the system.<P>
First, we want to inform the user that a cookie was saved to their machine. Since we set a
boolean flag earlier in the page, we can check its value. If it is set to true, then we will display a
message for the user. If not, then we will just go on displaying the rest of the page.
E-Mail address confirmation: <%= Request.Form("email")%>
</BODY>
</HTML>
Finally, we want to display the user's e-mail address that was just entered. This is done primarily
as a validation that the correct information was entered. To display the e-mail address, we will
retrieve it from the Form collection of the Request object.
Now that we have seen how to set the cookies, let's take a look at another example that will show
how we can use the cookies in our login page.
In this example, we have made changes to both the login.asp file and the checkLogin.asp
file. The changes to login.asp will be used to detect if a cookie has been set to save the login
information.
<%
If Request.Cookies("SavedLogin").HasKeys Then
Response.Redirect "CheckLogin.asp?cookie=1"
End If
%>
This section of ASP code that has been added to the top of the login.asp page will check to
see if a cookie has been set. If you recall from the previous example, the cookie that is set
actually contains two keys: one for the e-mail and one for the password. We can determine if the
correct cookie has been set by checking to see if the cookie named SavedLogin has keys. This
is why we named the file login.asp earlier.
If the correct cookie has been set, then we will use the Redirect method of the Response
object to send the browser to the CheckLogin.asp page. We use the Redirect method rather
than the Server.Transfer method, as we want to transmit a query string as well, and as we
mentioned in the previous chapter, Server.Transfer is unable to do this. To notify this page
that the request is due to the result of a cookie being read, we will set a query string parameter.
We will then be able to check for this value when we are processing the checkLogin.asp page.
<%
Dim strEmail
If Request.QueryString("cookie") = 1 Then
strEMail = Request.Cookies("SavedLogin")("Email")
Else
strEMail = Request.Form("email")
End If
In the CheckLogin.asp page, we will first add some code that will determine if the page was
requested by the redirection from the login.asp page. We set a query string parameter called
cookie when we redirected the browser to this page. By checking to see if its value is set to 1,
we will know if this page was called due to a cookie login.
There are two possible places that the user's email address can come from. If they have selected
to save their logon information in a cookie, then their email address can be retrieved from that
cookie. If they have entered their email address directly, then we can recover it from the Form
collection of the Request object. In either case, we want to save its value into a local variable.
This will allow us to use it later in the page, without having to check which method it was supplied
by again.
<%
If Request.QueryString("cookie") = 1 Then
Response.Write "Login submitted via cookie<P>"
End If
%>
E-Mail address confirmation: <%= strEMail%>
We want to display an indication to the user that their login information was supplied via a cookie.
The query string parameter cookie being set to 1 indicates this. If this is the case, then we will
display a message. We also need to change the Response.Write shortcut that displays the
email address that the user logged on with. Earlier in the page, we stored the value in the
strEmail local variable. We will now display the contents of that variable here.
All that would be left to do is add the proper user authentication code, and you can have a
workable user login system for your web site. Later in the book, we will take a look at how to
insert and retrieve information from databases. You can then tie this method of user login with the
databases and have yourself a very robust authentication system.
However, tracking users with ASP doesn't just end with cookies, it also incorporates two objects
that provide a more advanced method of dealing with the tracking of a visitor through the site, the
Application and Session objects.
There is one instance of the Application object for each application running on the web
server, but there may be many clients accessing the same application. They can each get a
reference to the same Application object.
An IIS Server can host any number of applications, and each application has its own
Application object. This object stores variables and objects for application-scope usage.
Application-scope means that variables (and objects) can be accessed from any ASP page that is
part of the application. The Application object also holds information about the sessions active
within a particular Application.
The diagram that follows shows the relationship between applications, Application objects
and Session objects, although the containment of the Session in Application is only at
logical level and not specified in any way by the object model.
In the diagram, the applications at the top refer to the collections of web pages and objects
defined on the server as virtual directories. The Application objects are the ASP objects that
control access to the Application. The Sessions represent individual client sessions with the
application – we'll discuss sessions in detail later. For the moment, remember that the first time a
client asks for an .asp page belonging to a given application, from your server, a session is
established for that client. Any number of sessions can be hosted by any one application, limited
only by the memory resources of your server. .
Application Variables
One of the features of an application is that you can store information that is available to all
clients that are accessing the application. This information is stored in what is known as an
application-scope variable.
To initialize variables in the Application object, you store the information about them in a
special ASP file named global.asa. Each application can have only one global.asa, and it's
placed in the application's root. To make sure global.asa is read by ASP, it isn't enough just to
make sure that it is stored in a virtual directory, you have to explicitly have created the application
virtual directory it is stored in using the Properties Dialog in MMC, as we did in Chapter 1. If you've
created one, then the button will read Remove.
In this example, we will create a very basic global.asa file so that we can declare some
application-level variables. Later in this section, we will access these variables from an ASP page
in our application.
1. Create a new file, and write the following:
2. <SCRIPT LANGUAGE="VBScript" RUNAT="Server">
3.
4. Sub Application_OnStart
5. Application("myAppVariable") = " "
6. Application("anotherAppVariable") = 0
7. End Sub
8.
9. </SCRIPT>
10. Save the file as global.asa in your BegASPFiles directory.
Note If you are using FrontPage 98 or another application that doesn't
recognize .asa as a 'valid' file extension, then you can change this
yourself by going to Windows Explorer's View | Folder Options | File tab
and typing in the following settings:
Description - ASA File
Extension - ASA
Content type - <leave blank>
Opens with - Notepad (or your choice here)
What It Does
In our first global.asa file, we are declaring some application-level variables that we will use
later in our examples. The first thing that you will notice about this file is that there are no <%…%>
blocks. As you have seen before, these <% and %> tags are used to indicate ASP script within a
file. In the global.asa file, we will be using the following syntax instead (which can also be
used in ASP files):
<SCRIPT LANGUAGE="VBScript" RUNAT="Server">
The procedures that are part of global.asa are defined within a script block. The language that
we are using for scripts is VBScript. Since these scripts will be run at the server, as opposed to
the client, we have included the RUNAT parameter, and passed it a value of Server. If you
include script that is not enclosed by <SCRIPT> tags, the server returns an error, as it won't be
marked up for server-side execution.
Sub Application_OnStart
Application("myAppVariable") = " "
Application("anotherAppVariable") = 0
End Sub
We've used one event (there are a possible four, which we'll discuss shortly) in our global.asa
file and this is Application_OnStart. This event will be fired only once when the first visitor
hits the page. So, an application is started the first time one of its pages is accessed by a user.
The Application_OnStart event occurs before the first new session is created, that is,
before the Session_OnStart event.
Inside of this event handler, we are initializing two application-level variables. Application-level
variables are actually elements of the Application object. We set and retrieve their values in
the same way that we set and retrieve the values in a collection.
The general syntax for retrieving a variable's value in the Application.Contents collection is:
Application.Contents("Key")
to specify it as well.
You can iterate through the Contents collection the same way you would for any collection, with
a For...Each statement. An example of this is:
<%
For Each Key in Application.Contents
Response.Write (Key)
Next
%>
Just as with a normal collection, you can retrieve the number of elements in the collection by
using the Count property.
<%= Application.Contents.Count %>
Lock Method
The Lock method prevents clients – other than the one currently accessing it – from modifying
the variables stored in the Application object. This means you can't get one client changing
the variables, taking a long time over it, and another one changing the same variables to a
different set of values in the time between. This way data corruption can be avoided.
UnLock Method
This method removes the lock from variables stored in the Application object, freeing them up
after Application object has been locked previously using the Application.Lock method.
Remove Method
Removes an item from the Contents collection. For example, look at the following:
Application("FirstVariable") = "Cats"
Application("SecondVariable") = "Dogs"
Application.Contents.Remove("FirstVariable")
This would create two Application objects and then remove only the first.
RemoveAll Method
Removes all items from the Contents collection. For example, consider the following:
Application("FirstVariable") = "Cats"
Application("SecondVariable") = "Dogs"
Application.Contents.RemoveAll
This creates two Application objects and then removes the collection along with any other
Application objects that might have been created prior to this.
25. Open another copy of your browser and view the file again.
How It Works
In this example, we are using the application-level variables that were created in the
global.asa example that we ran earlier.
<BODY>
Let's retrieve the values of the Application Variables:<P>
myAppVariable = <%= Application("myAppVariable") %><BR>
anotherAppVariable = <%= Application("anotherAppVariable") %><HR>
First, we will retrieve the values that are currently stored in the two application-level variables that
we have declared. Since all we are doing is accessing the value stored in the variables, there is
no need to call the Lock method.
Now, let's set the variables:<HR>
<%
Application.Lock
Application("myAppVariable") = Now
Application("anotherAppVariable") =
CStr(CInt(Application("anotherAppVariable"))+1)
Application.Unlock
%>
The next step is to reset the values of the two application-level variables. In order to give this
script exclusive control over the variables, we first need to call the Lock method of the
Application object. This will give total control of all application-level variables to this script until
the corresponding call to UnLock is made. Since there can be multiple users accessing a web
site at the same time, and possibly accessing the same page, we need to take these precautions
to ensure that the data is updated properly.
Now that we have exclusive control over the variables, we can update their values. The value of
the myAppVariable variable is set to the current date and time. The value that is stored in
anotherAppVariable is a string containing the number of times this page has been accessed.
Here, we want to increment this count by one. We could rely on VBScript's automatic type
conversion, but to make our code more readable by someone trying to learn what it is doing, we
will explicitly convert the variable's type.
The first step is to convert the string value that is stored in the application-level variable to an
integer value. This is done using the CInt function. Now that we have an integer value, we can
add 1 to it to get the new count. Finally, to store the value back into the application-level variable,
we will convert the new count back to a string using the CStr function.
With all of the changes successfully made, we will release our hold on the application-level
variables by calling the Unlock method of the Application object.
Now that we have learnt some of the basics of the Application object, let's take a more in-
depth look at the global.asa file.
Global.asa
Since we can now store objects and variables in an application-level scope, we need to have a
file to store these declarations in. This file is global.asa. Each application can have only one
global.asa, and it's placed in the virtual directory's root. In global.asa, you can include
event handler scripts and declare objects that will have Session or Application scope. You can
also store application-level variables and objects used by the application. The file has no display
component, as it is not displayed to users.
Understanding the Structure of global.asa
If you are using VBScript, global.asa can contain only four event handling subroutines:
Application_OnStart
Application_OnEnd
Session_OnStart
Session_OnEnd
Sub Application_OnStart
'...your VBScript code here
End Sub
Sub Application_OnEnd
'...your VBScript code here
End Sub
Sub Session_OnStart
'...your VBScript code here
End Sub
Sub Session_OnEnd
'...your VBScript code here
End Sub
</SCRIPT>
Notice that we have to use the <SCRIPT> tag at the top of the page. Also as previously
mentioned, there are no <%…%> blocks in the file, because in the global.asa file, all of the ASP
script needs be enclosed in the <SCRIPT> block. Since these scripts will be running on the server
rather than on the client's machine, we have to make sure that the RUNAT directive is included
inside of the SCRIPT element, otherwise global.asa won't work correctly.
Application_OnStart
This event handler is run once, when the application starts. The application starts when the first
visitor to the application calls the first .asp page. In this procedure, you should put any
application initialization steps that need to be run before anyone can access the application. For
example, you could store the database login information, which you will see in Chapters 12 to 14,
in an application-level variable, so that all pages have easy access to it.
use:
Sub Application_OnStart
Application("YourVariable") = "SomeValue"
End Sub
Once this event handler is complete, Session_OnStart runs for the first session.
Application_OnEnd
An application ends immediately after the last active session within that application ends. This
event handler runs when the application is unloaded. To unload the application manually, you can
do this from the IIS MMC console by just opening the console, selecting the application you want
to unload in the left hand pane, then right click on the application and select Properties. Under the
Virtual Directory tab at the top, there is a button on the right hand side of the dialog box marked
Unload. If you click this, the application will be unloaded. It may take a little time, but the way to
tell when the application has finished unloading is to wait until the button is grayed out.
Application_OnEnd scripts are used for "cleaning up" settings after the Application stops. For
example, you might want to insert code to delete unneeded database records or write information
you want to keep to text files.
What kind of errors will you see? Well Object doesn't support this property or method: is one that
should have you scrambling to find Windows Explorer, to check out whether you have declared
your application or not, or whether you have nested one application inside of another. Think of it
this way: if you declare an object in global.asa and for some reason, ASP doesn't process the
global.asa, then any time you reference that object in your script, ASP will generate an error.
Type Mismatch is another error caused for very similar reasons. If you declared a variable to hold
an instance of an object in global.asa, and for some reason ASP is not reading the correct
global.asa, then the ASP script could still be expecting you to read in a variable not an object.
If you think about it, this is perfectly reasonable, how can ASP know which global.asa to use if
you nest applications or haven't declared your application, clairvoyance isn't one of things
Microsoft have been able to program in yet. Only you know which global.asa should be read
and therefore you have to make sure the applications are explicitly declared, and also not nested.
Extra Warnings on Declaring Objects in global.asa
You should be careful about creating components in application scope: some components are not
designed to be given application scope, such as the Dictionary object. If you are not sure if an
object can be used in application-scope, then you should err on the side of caution and find
another place to store the object.
The use of objects in application or session scope is really an advanced topic that is beyond the
scope of this book. There are advantages that can be gained by using them, but along with those
advantages comes a host of possible problems. For a more detailed look at how to use objects in
global.asa, take a look at Professional Active Server Pages 3.0, available from Wrox Press,
ISBN 1-861002-61-0.
Next, we'll have a look at ASP's link between the Application and the client – the Session
What is a Session?
In addition to providing support for applications across the web, Active Server Pages also
supports sessions within an application. We have just looked at how applications are created and
at some of the things that you can do with them. To begin our look at sessions, we first need to
define what a session is.
ASP allows the developer to track a user from page to page in an application through the use of a
session. A user's session begins when any user without a current session opens any .asp page
within an ASP Application. The user's session will continue as they navigate from page to page in
the site.
There are two ways that a session can be terminated. If the user stops interacting with the
application, then the session will end after a certain period of time has elapsed. The default value
for this time period is 20 minutes, which can be changed by setting the Session.TimeOut
property. The Session.Abandon statement in an ASP page can also explicitly end the session.
The session can be used to store pieces of information that will be available to every page in the
application and each user as private instances of those pieces of information. This can be used to
track things like the contents of a user's shopping basket, or a flag indicating that this user has
been properly authenticated with the system. In addition, just as there are event handlers in
global.asa for the beginning and end of an application, you can also write an event handler
that will be called when a session is started and when it is ended.
To interact with the session itself, you will be using the Session object.
The Session Object
There is one Application object for each application on the web server. Every client accessing
that application can get a reference to it. Each client's unique interactions with the application is
called a Session. For ASP to manage these processes, each client has a reference to a unique
Session object.
Contents Collection
The Contents collection contains all the variables established for a session without using the
<OBJECT> tag. The Contents collection is used to determine the value of a specific session
item, or to iterate through the collection and retrieve a list of all items in the session.
Session.Contents("Key")
For example:
<%= Session.Contents("VisitorID")%>
For single session variables, because contents is the default collection, this is usually shortened
to:
<%= Session("VisitorID")%>
You can iterate through the Session.Contents collection with the following code:
<%
For Each item in Session.Contents
If IsObject(Session.Contents(item)) Then
Response.write(item & " : Can't display object" & "<BR>")
Else
If IsArray(Session.Contents(item)) Then
Response.write "Array: " & Session.Content(item)
For each objArray in Session.Contents(item)
Response.write "<LI>" & _
Session.Contents(item)(objArray)& "<BR>"
Next
Response.write "</LI>"
Else
Response.write(item & " : " & Session.Contents(item) &
"<BR>")
End If
End If
Next
%>
This code will produce a list of all the Session variables' names and values. The For Each
statement will iterate through each of the elements in the Contents collection. As you can store
objects or arrays in the collection, we need to check for these specific occurrences and handle
them. You can't display an object, so we need to just mention that a certain item is an object. You
also need to iterate through an array and display each element. Finally you need to display the
'normal' items. Each of these elements will be a key into the collection. You can then display this
key, as well as the element in the collection associated with that key. The Next statement will
take you to the next key in the collection, or if you are at the end, it will take you to the next line in
the script.
StaticObjects Collection
The Session StaticObjects collection contains all the objects created with the <OBJECT> tag
within session scope. The StaticObjects collection is used to retrieve the value for an object's
specific property, or to iterate through the collection and retrieve all properties for all objects.
SessionID Property
The SessionID property is a read-only property that returns the session identification number for
each user. Each session has a unique identifier, generated by the application when the session is
created. The session identification number is actually a cookie that is stored on the user's
machine. It will expire only when the session times out. One rather large problem with the
Session object is that it simply won't work if the user has cookies turned off. The session won't
get started when the user browses the site and they won't get access to the Session object.
Timeout Property
The Timeout property sets the timeout period assigned to the Session object for any
application, in minutes. If the user does not request or refresh a page before the timeout period
expires, the session ends.
You should exercise caution when setting Timeout values. If you set Timeout to too short a
duration, the server will terminate user sessions too quickly. If you rely on session variables to
process user data and the user has not been able to complete the processing of an identification
form, for example, the loss of session variables will cause all sorts of problems.
Setting Session Timeout to too long a duration also has its problems. All Session variables
are stored in the server's memory. Since the server has no way to determine if the user is still
viewing your sites' pages, you are probably going to have quite a few user sessions eating up
server memory resources, if sessions last for too long a time. This is particularly critical if you
store database query results in Session arrays. These results can hold a lot of data sometimes,
and having multiple unused sessions still open with large amounts of data stored in them could
slow down your server.
The best strategy is to analyze each application, by conducting average user browsing time tests,
and then set the session timeout to a more appropriate value where needed. Generally though,
the 20 minute timeout is a good compromise for most applications.
CodePage Property
The CodePage property determines the code page that will be used to display content. A code
page is used to map between the characters that are displayed on the screen and an internal
table. Unless you are developing sites that use non-Roman alphabets, such as Russian or
Japanese, you will not have to worry about setting this property.
In order to be able to set the code page property you must have first enabled code page support
with:
<%@ CODEPAGE = CodePage %>
This also happens to be the default value for the LCID property for servers installed in the US,
meaning you don't need to explicitly set it. The various LCID values are usually defined in the
documentation as hexadecimal values. You must convert these values to a decimal value to use
when setting the LCID property.
Session Object Methods
The Session object has just one method, although the Contents collection also now boasts
Remove and RemoveAll methods.
If you know that when a user finishes browsing a certain page they don't need any more session
variables, you can call the Abandon method to release server memory resources associated with
that session. Bear in mind that if you set certain useful session variable values, like UserName,
all the values will be lost. On the other hand, if you are programming web games, which use
server memory resources heavily, you might be better off explicitly ending the session and having
the user visit the start page for another game.
A quirk of the abandon method is that it will not clear the session variables until the current page
has been fully processed. If you have a page that displays the session variables, after the
abandon method is called you will be still able to see the session variables.
One use of the Abandon method is during development when testing Session variables. If you
create a page named abandon.asp, as described below, you can call a page when needed.
<%
Session.Abandon
Response.Redirect "default.asp"
%>
In this page, we first call the Abandon method of the Session object. This releases all of the
session-level variables. Next, the Redirect method of the Response object is used to send the
browser to the starting page of the application, creating a new session. In this example, that page
is named default.asp.
Remove Method
Removes an item from the Contents collection. Consider, for example, the following:
Session("FirstVariable") = "Fish"
Session("SecondVariable") = "Carrots"
Session.Contents.Remove("FirstVariable")
This would create two Session objects and then remove only the first.
RemoveAll Method
Removes all items from the Contents collection. So, for example, look at the following code:
Session("FirstVariable") = "Fish"
Session("SecondVariable") = "Carrots
Session.Contents.RemoveAll
This creates two Session objects and then removes from both the collection, along with any
other Session objects that might have been created prior to this.
Now we'll look at example of making variables persist across different ASP pages.
35. To create a new session click on Refresh and view the page again.
Note In Windows 2000 with ASP 3.0, all browsers (including Navigator etc) on
the same machine now have the same session, the only way to create a
new session is by refreshing the page. This wasn't the case previously
with ASP 2.0, where if you opened new instances of the browser(not just
a new window, using File/New/Window) by starting Internet Explorer
from either a desktop shortcut or from the start menu, you would create
a new session.
How It Works
In this example, we are using a session-level variable to store the date and time that this page
was last accessed. If there is a value present, then it will be displayed for the user. If there is no
value present, then the user will be shown a message telling them this.
<%
Dim tAccessTime
tAccessTime = Session("LastAccessTime")
This first thing that we will need to do when processing this page is to retrieve the value stored in
the session-level variable. We will be storing it in a local variable, which we can use throughout
this page. This is very efficient way of working with session-level variables. It's important to
remember that since the act of retrieving the session-level variable tends to consume a good deal
of processor time, it is better to store the data locally, and then work with the local variable.
If tAccessTime = "" Then
Response.Write ("This is the first time this page has been accessed!
<P>")
Else
Response.Write ("This page was last accessed at " & tAccessTime &
"<P>")
End If
Next, we check to see if there is a value in the variable that we just retrieved. If no value has been
set, then the variable will be empty. This will be the case if the session has not been created
before, if the session timed out, or if this is the first time a user has been to this page. This may
seem a bit strange to some people who are familiar with other languages in which a variable must
have a value before it can be accessed. But in ASP, you can check to see if a session-level
variable exists simply by trying to access it. If the variable does not exist, then nothing will be
returned. If there is a value there, then we will display it.
<HR>Writing current access time to the Session object<P>
<% Session("LastAccessTime") = Now %>
We will then update the value of the session-level variable called LastAccessTime with the
current date and time. Notice the difference between interacting with a session-level variable, and
an application-level variable. Since the session is only for one user, there are no Lock and
UnLock methods for the Session object as there are for the Application object. To set a
value in a session-level variable, nothing special has to be done beforehand.
<A HREF="sessVarTest.asp">Click here</A> to refresh the page.
<A HREF="abandon.asp?dest=sessVarTest.asp">Click here</A> to abandon the
session.<P>
We then provide two hyperlinks to allow the user to navigate from this page. The first hyperlink
will reload the current page. This will cause the value that was just stored in the session-level
variable to be displayed, and a new time stored there. The second hyperlink will call the
abandon.asp file, which will abandon the current session. The abandon.asp file needs to
know where to send the browser once the session has been abandoned, and this page name is
passed as the dest query string variable.
<%
Session.Abandon
Response.Redirect Request("dest")
%>
The abandon.asp file will perform two functions. First, it will explicitly end the current session by
calling the Abandon method. This will free any session-level variables and reset their values.
Next, the script will redirect the browser to the page that the user supplied. Notice that we have
used the shortcut to retrieving the value of a query string variable, whereby we don't tell ASP
which collection the information resides in. ASP will search each of the collections looking for the
supplied key. When and if it finds it, it will return the value to the script. This is fine as long as you
don't access a collection before the generic Request collection that has a variable with the same
name, resulting in the wrong variable being read.
Sessions and global.asa
Earlier in this chapter, we saw how the global.asa file can be used to process the startup and
the shutdown of web applications. The file can also be used to handle events fired by the startup
and shutdown of user sessions within a specific web application. Just as there are two event
handlers for the Application object, there are two for the Session object as well.
Session_OnStart
Session_OnStart is called every time a new user begins a session with the web site. If you
want to initialize a session-scoped variable in Session_OnStart, you'd use:
Sub Session_OnStart
Session("YourVariable") = "SomeValue"
End Sub
For example, let's say you want to track the number of visitors currently accessing your site. (This
is different from a traditional counter which tracks total numbers of visitors.) To do this, you would
have an application-level variable that tracked the current number of users that were currently
accessing the site. This is the same number of sessions that are currently active. Every time that
a new session was started, we would want to add one to that number, and that can be done in
the Session_OnStart routine.
Session_OnEnd
Session_OnEnd is called whenever a session terminates. This can happen if the session times
out, or if it is explicitly abandoned. This isn't the same as just closing a browser down, in which
case the session has to time out, before it ends. In Session_OnEnd, you might want to transfer
temporary session variables to databases, or set application-level variables to another value: for
example, if you are tracking the number of users currently visiting a site in an application-level
variable, then you would subtract one from this number every time Session_OnEnd was run.
Sub Session_OnEnd
Application.lock
Application("Active") = Application("Active") - 1
Application.unlock
End Sub
As with application objects, session-level objects declared in global.asa are not created until
the server processes a script that calls the object. Again, this saves resources by only creating
objects that are actually used.
In this example, we will take what we've learned and construct a global.asa file that will allow
us to track the number of current visitors to the application, as well as the total that have visited
since the application was started.
Unfortunately, since some of you might not have the facility to use multiple machines to track
sessions, we're going to have to 'cheat' a little bit, to demonstrate the code working. We'll explain
how the 'cheat' affects what we see, as we go along.
1. Create a new folder called Session, within the c:\Inetpub\wwwroot folder.
2. Start up MMC and right click on the Session folder and select the Properties option to
bring up the Properties dialog.
3. Click on the Create button to create a new application, and click on OK to close the dialog.
(The folder must be turned into an application for this example to work.)
4. Create a new file, and key in the following:
5. <SCRIPT LANGUAGE=VBScript RUNAT=Server>
6. Sub Application_OnStart
7. Application("visits") = 0
8. Application("Active") = 0
9. End Sub
10.
11. Sub Application_OnEnd
12.
13. End Sub
14.
15. Sub Session_OnStart
16. Session.Timeout = 1
17. Session("Start") = Now
18. Application.lock
19. Application("visits") = Application("visits") + 1
20. intTotal_visitors = Application("visits")
21. Application.unlock
22. Session("VisitorID") = intTotal_visitors
23.
24. Application.lock
25. Application("Active") = Application("Active") + 1
26. Application.unlock
27. End Sub
28.
29. Sub Session_OnEnd
30. Application.lock
31. Application("Active") = Application("Active") - 1
32. Application.unlock
33. End Sub
34. </SCRIPT>
35. Save this file as global.asa. For this example, you need to store your global.asa file
in a different directory. Store it in your Session folder, which should be found under C:\
InetPub\wwwroot\Session.
Note If you are downloading the code for this chapter from the Wrox webisite,
you will find that this file is called global_session.asa. You will need
to place it in the correct directory and rename it to global.asa in order
for this example to work.
36. Create a new file, and key the following:
37. <HTML>
38. <HEAD>
39. <TITLE>Retrieving Variables Set in Global.asa</TITLE>
40. </HEAD>
41. <BODY>
42. <P>
43. There have been <B><%= Session("VisitorID")%></B> total visits to
this site.
44. <BR>You are one of <B> <%= Application("Active")%></B> active
visitors.
45. <BR>Your session started at <%= Session("Start") %>
46.
47. </BODY>
48. </HTML>
49. Save this file as VisitorCount.asp. You will need to save it in the Session folder, as it
needs to be in the same folder as the global.asa.
50. View the file in your browser.
51. View the file in another browser or in another instance of your browser (if you simply
open a new window, you will find that this won't work).
52. Close down the second browser immediately and press the Refresh on the first (time is of
the essence as we'll explain shortly).
Note Note that the number of active visitors is two, even though we've just
closed a session, we'll explain shortly. Also note that the number of total
visits is still only one on this browser, even though two browsers have
visited the site. This is because the number of total visits is actually set
when the session is started, and not updated throughout. So when this
browser viewed the site, it was the only browser to have viewed this site.
Even though another browser has viewed this site, the total remains at
one. So while strictly speaking our total number of visits is accurate for
the beginning of the session, it doesn't adjust dynamically throughout
the session.
53. Wait one whole minute and press the Refresh on the browser again
Note As explained earlier, we can't really mimic the action of several users
browsing our site (unless you have a few spare PCs handy, or a lot of
friends), so what we've done is set the value of the Session.Timeout to
one minute in global.asa. When you close a browser your session
doesn't automatically end, but the session will timeout after a minute,
and be ended for you. Only then is the number of active users is
reduced. So our count is accurate, but it takes a little while to update.
How It Works
In this application, we are using application-level variables to track the number of users that are
currently looking at a site, as well as the total number that have accessed it.
<SCRIPT LANGUAGE=VBScript RUNAT=Server>
Sub Application_OnStart
Application("visits") = 0
Application("Active") = 0
End Sub
In the first event handler of the global.asa file, we will be working with the
Application_OnStart method that is called whenever the application is started.
There are two variables that we will need to maintain throughout the life of the application. The
visits variable will hold the total number of visits since the application was started. One thing to
note is the Visits variable is initialized as zero. That means every time the application is
unloaded, and restarted, this variable will be set to zero. If you don't want this to happen, each
time you restart the application after unloading it, we suggest you remove the zero. The Active
variable will hold the number of current user sessions in the application.
Sub Application_OnEnd
End Sub
We will not be doing any processing when the application is ended, so we are just adding this
event handler as a placeholder.
Sub Session_OnStart
Session.Timeout = 1
Session("Start") = Now
When a session is started, we will perform some processing in the Session_OnStart event
handler. To make our example work faster, we set the Timeout value of the Session object to 1
minute. In a real application, you would very rarely have your Timeout value set so low, but then
again you would have people visiting your application using different machines, rather than
having to rely on the one machine we are using.
We will also store the starting date and time for this session into a session-level variable.
Application.Lock
Application("visits") = Application("visits") + 1
intTotal_visitors = Application("visits")
Application.Unlock
Session("VisitorID") = intTotal_visitors
Next, we will need to find out the total number of visitors to the site since the application was
started. This information is stored in an application-level variable. In order to change an
application-level variable, we need to first Lock the Application object. We can then
increment the value stored in the visits variable by one. This new value will represent the total
number of visitors, including the one currently starting. The incremented value is stored back into
the same application-level variable as it came from. Once we have made all the changes that we
need to at this point, we will call Unlock to free up the Application object.
Application.Lock
Application("Active") = Application("Active") + 1
Application.Unlock
Finally, we will update the current number of active users by one. This information is stored in
another application-level variable.
Sub Session_OnEnd
Application.Lock
Application("Active") = Application("Active") - 1
Application.Unlock
End Sub
When the user leaves the site and their session times out, this event handler will be called. The
important thing that needs to happen in this file is for the number of active users to be
decremented by one. Since this information is stored at the application-level, you will need to
Lock the Application object when making the change, and then Unlock the object as quickly
as possible.
There have been <B><%= Session("VisitorID")%></B> total visits to this
site.
<BR>You are one of <b> <%= Application("Active")%></B> active visitors.
<BR>Your session started at <%= Session("Start") %>
In our test page, we will be displaying the total number of visits to this site since the application
was started, at the time when the user's session is started. This is because we fix the total
number of visits to the site, when we start the session in global.asa and we can't update it
dynamically. We will also be showing the number of active visitors, as well as the time at which
the current session began. Even though we are interacting with an application-level variable, we
do not need to Lock and Unlock the Application object if we only want to read the data.
As you can see from the screen shots, once a session times out, the Session_OnEnd really is
called. This can be seen by the change in number of active users as we look at each of the
browser snapshots.
We've completed our discussions on how to preserve data from page to page using the Session
object. Also, we have covered how to keep information from session to session in an application
using the Application object.
Finally, we'll turn our attention back towards the mechanism that is now supposedly obsolete.
Cookies also still play a role in storing information between sessions. While a session has a
timeout value, of usually 20 minutes, after which all of its information is deleted, a cookie can
persist for a much longer period of time. The reason sessions can't have very long time-outs is
that every time a user comes to your site, the server would have to wait for the period specified
by the timeout to determine whether the user had logged out or not. Remember that closing a
browser doesn't end the session, rather it just begins the period for which a session is dormant,
until it times out. The server would use up precious resources monitoring long dead sessions, and
the more users who visit the site, the more resources would be stretched. For this reason,
cookies also play a vital role in storing information during the times when the user is off visiting
other sites, or is not even using their browser at all, and you won't see the back of them for a
while yet.
Summary
The world of web-based applications is made possible by Active Server Pages and the
Application and Session objects. These powerful objects allow you transform a web site
from a series of linked pages to an actual application by creating a means to hold user
information from page to page. In this chapter, we have looked at:
How cookies can help us store information over a long period of time, and even after the
user has left our site
When and how to use cookies to store information at the client's computer
How the Application object can be used to store information that can be accessed by
all users accessing a web site
What the global.asa file is used for, and how it helps us interact with the beginning
and the end of sessions and applications
The powerful Session object, which allows us to treat a user's interaction with the web
site as a continuous action, rather than just a disconnected series of page requests
Now that we are on our way towards building web-based applications, we will need to look at
some good coding techniques and how we can go about debugging our code. That's the subject
of our next chapter.
Chapter 9: Error Handling
Overview
No matter how hard we try, it really is very difficult to write error-free software. We are inherently
fallible. That's not to say it's impossible to write perfect software; it's just very hard, and takes
effort, practice, diligence and lots of experience. Let's face facts; we all make mistakes
occasionally!
So, given that we make these mistakes, how do we go about tracking them down? Most of the
popular programming languages are supported by a rich set of development tools, and these
tools usually include a debugger. However, it's a sad fact that web developers have all too often
had to rely on the WYSINWYI or WYSIAAMEM approach to debugging – that's "What You See Is
Not What You Intended" or "What You See Is An Arcane Microsoft Error Message".
In this chapter we are going to look at some good coding techniques, some debugging
techniques and how we apply them to our ASP environment. You'll see how some additions to
ASP 3.0 make life a lot easier than it used to be. In particular we will be looking at:
How to start debugging your ASP code
The difference between debugging client-side script and server-side script
Different types of errors
Good coding practices
How to debug your script
The Microsoft Script debugger
How the Server object can help us debug
The VBScript Err object and the ASPError object
Of course, none of these will make you a perfect programmer, but that's really a false goal
anyway. The best that you can do is to understand your code, recognize where it might break,
and be sure to handle possible error conditions. Making a mistake is acceptable, as long as you
find out what the problem is quickly, and then learn from it.
Arrgh! I Goofed Again!
We've already said that it is acceptable to make mistakes. If you're a manager reading this, don't
rip this chapter out before giving it to your programmers. The fact is that 'trial and error' is often a
good way to learn a new technology – once you've crashed the server for the third time in 20
minutes, you will probably have worked out what it is you are doing wrong. OK, if you're writing
monitoring software for a hospital intensive care unit, your quality goals might be a little stricter –
but on your own, while learning, you will make mistakes. Guaranteed. You will learn from them;
you won't make them again, and you'll go on to write books that tell people how to debug
applications.
So what this chapter is really about is how to write code that, while not exactly bulletproof, is a lot
more robust than it might otherwise be. When the worst does happen, we talk about how you
track down those errors quickly and efficiently so that you can get to the root of the problem and
fix it, or at least find sources of information when the error still mystifies you.
This shows a typical situation. In step 1, the user selects a URL for an ASP page and the request
is forwarded to the web server. Step 2 shows ASP processing the ASP script code on the server.
The processed HTML is then sent back to the client in step 3. And finally, in step 4 the web
browser displays the HTML and executes any client-side script. Pretty obvious, but also very
important as you must be aware of where certain actions are processed.
The thing to remember is the difference between server-side script and client-side script. Server-
side script is enclosed in the following tags:
<%
' server-side script here
%>
Alternatively, server-side script can be denoted by using the standard script tag and the RUNAT
property:
<SCRIPT LANGUAGE=VBScript RUNAT=Server>
' server-side script here
</SCRIPT>
You might already be used to this by now, but it's surprising how often you forget where the script
code is being executed, especially if your ASP page has both server-side and client-side script.
What's all this leading to? Well, there are distinct sets of objects. Some are server-side objects,
such as the Response object, and some are client-side, such as the Document object.
</HTML>
This is because the Response object is only available as part of ASP, that is, in server-side
scripting. Thus this is a client-side error, not an ASP error.
<%
Dim objField
Set objField = Document.All("FieldName")
%>
</BODY>
</HTML>
will give the following error if you scroll down the page:
This is because the Document object is only available at the client, that is, in the web browser.
Also notice the difference between how the two error messages are displayed. The first example
generated an error on the client, so it popped up an error dialog. The second example generated
an error in ASP, so the error was returned as HTML code, which subsequently made up the web
page. ASP, on its own, can't display error dialogs because it runs on the server – any dialog
would appear on the screen of the web server, which isn't much good if you're five thousand
miles away. As we're talking about ASP, we're not going to be paying much attention to client-
side errors, but we will be looking at the difference between client and server debugging a little
later.
Types of Error
So we've noted that errors can arise in two different places, but there are also several different
types of error that may occur. This is important to realize, as some errors might not even
generate an error message. Debugging is a lot easier if you know where to look and what you are
looking for. There are lots of types and subtypes of errors, but in ASP you can very broadly
classify them as one of the following three types: syntax error, logical error or ASP error.
Syntax Errors
Syntax errors are easily spotted, as they're the ones that cause your program to go belly up.
Computers are very precise machines, and the applications that run on them are just as picky.
You and I can muddle up words in our sentences and still expect somebody else to make general
sense of them. For example, I could say "good morning" to you at one o'clock in the afternoon,
and you'd still know what I meant. However your ASP programs can be thrown out by the mildest
of typos.
<% Reponse.Write "Hello" %>
This would provoke an Object Required error, ASP failing completely to recognize that you merely
missed an s out. Also if you fail to create a loop structure correctly, such as by missing a Next off
the end of the structure:
<% For intLoop = 1 to 5
Response.Write "Hello" %>
This would cause an error. Or if you fail to close a conditional structure with an End If, or
requisite closing statement:
<%
If strChoice = "Yes" Then
Response.Write = "Yes"
Else
Response.Write = "No"
%>
This too will generate an error. In fact, ASP will also tell you very quickly about this. As you can
imagine there are literally hundreds of different possible types of errors, so we can't go into them
all here. Syntax errors are like abuses of the grammatical rules of language. If you wrote, "You
was here on time", then your old English tutor would have put a big red line under it at school.
Writing a program is almost like writing an English essay, yet you have to get everything correct –
one spelling mistake and everything will come tumbling down around your ears. Ok, let's take a
look at a few very common causes of syntax errors
For intLoop1 = 1 to 10
For intLoop2 = 2 to 20
For intLoop3 = 3 to 30
…Code here…
Next
Next
It is not always obvious if the loops are closed 10 pages of code later. However, indenting
the code will certainly help.
Using the wrong method or property of an object: I find that the Cookies collection is
a classic source of this type of error. The following code will not work (I've tried it!):
Request.Cookies("Cookie").Expires = Date+1
Using a property as a method or a method as a property: It seems obvious, but most
experienced programmers have done this at least once:
Response.Write = "Hello" or
Response.Buffer True
Writing to a Read Only property: there are certain properties that just return
information. If you tried to assign a value to them, it would generate an error. Back in our
Telephone object example, in Chapter 6, the IsConnected property was read only. The
same goes for the TotalBytes property of the Request object. The following would not be
allowed:
Request.TotalBytes = 200
Not creating an object properly: If you don't use the keyword Set then the object won't
get created. For example in Chapter 6
ObjTelephone = Server.CreateObject("MyTelephone.Telephone")
The one upside is that syntax errors are usually easy to spot, and if you do spot them then they
are almost all very easy to correct, unlike the next type of error we will look at.
Logical Errors
I've seen these kinds of errors called everything under the sun from "algorithmic errors" to "why
the heck isn't my program working, that can't possibly be the right answer ". Most often, you've
checked your code, cleared up any typos and obvious coding errors, and yet the program's not
returning data or is returning data that can't possibly be correct. That's usually because you've
made a mistake in the programming logic. Let's consider some common types of logical error:
Division By Zero Errors
Type Mismatch Errors
No Error message, but the output is wrong
Division by zero is where, for whatever reason, you end up dividing a number by zero. It's usually
not an intentional mistake: you divide a variable by a positive number, but for some reason the
variable is empty and so you inadvertently end up dividing your number by zero. Of course this is
a mathematical impossibility and the computer will generate an error whenever this problem
arises. However the problem might not always be apparent. Consider a form where you ask the
user to enter a number of items for purchase, and you wish to give him/her a discount and
calculate requisite taxation. The user accidentally forgets to enter this number in the box and
submits it to your server. Your program multiplies the number of items by the price and ends up
with zero. This result could then be used as basis for division, later in the calculation, and we end
up with a division by zero error. However if the user had supplied a number other than zero then
the program would have worked correctly.
Type mismatch errors occur, for example, when variants of one subtype (such as integers) are
used to store the results of a calculation and then are added to information of another
incompatible subtype (such as text). As variants are "variables for all seasons", they're normally
very durable, so you can assign it a number one moment and text the next:
<%
varint = 1
Response.Write varInt
varint = "Hello"
Response.Write varInt
%>
The implicit type conversion would make sure, that the first value is handled as a number and the
second is handled as text. Thus, you'd get the response 1 followed by Hello. However, if you tried
to add text to a variant storing a numerical value, then you'd generate an error:
<%
varInt = 1
Response.Write varInt
varInt = varInt + "Hello"
Response.Write varInt
%>
Another common occurrence of this type of error is where you attempt but fail to create an object
and then try and use it as an object, while ASP still thinks you're using the variable designed to
hold an instance of an object, as a variable. Later in this chapter we will look at a method that can
help to reduce the frequency at which this type of error occurs.
I'd hazard that one of the most common types of logical error is where the programmer makes a
mistake in his/her assumptions, and codes that error into the program. Consider the following
pearl – whether it's unverifiable truth or merely urban myth is open to speculation, but it could
have happened. In the 1950s/60s, on the U.S. nuclear defense system, a programmer calculated
a number incorrectly – he or she managed to shift the decimal point one place to the left.
Unfortunately, the number in question corresponded to a flight trajectory angle in the algorithm
that detected whether a nuclear missile attack had been launched. Once in operation the program
detected an object corresponding to its calculations, and displayed that it was 99.9% certain that
it was an incoming missile. It turned out that the program had been triggered by the rising moon!
In the business world, such mistakes are probably less dramatic, but consider the following
program to calculate the royalties that a best-selling novel writer might receive from his
publishers:
<%
intSales = 10000 'Sales for One Quarter
intRoyaltyRate = 0.1 '1% Royalty Rate
intPrice = 40 'Book Price
intRoyaltyReceived = intSales * intRoyaltyRate * intPrice
%>
All looks very plausible until you realize the mistake. The author is only meant to be getting 1%
royalties, not 10%. The correct line should read:
intRoyaltyRate = 0.01 '1% Royalty Rate
If you calculate the difference between the two totals, you'll find that the first is 40,000 dollars,
while the corrected one is 4,000 dollars. This simple accounting mistake could be costing the
publisher 36,000 dollars, if the author forgets to mention it.
Of course there are plenty of other types of logical errors, but hopefully these examples have
given you an idea of the kind of thing you should be looking out for when you're checking and
testing your code.
ASP Errors
Sometimes the operating system itself goes down. If you've used Windows 98/NT you'll be
familiar with the "blue screen of death", where Windows itself generates an error and usually has
to shut down. Sometimes these errors are caused by the user or programmer, and sometimes by
bugs in the operating system. We explained in an earlier chapter that ASP was a DLL (Dynamic
Link Library), and DLLs aren't immune to failure. If the ASP DLL fails it returns with an error code.
Here's a list of them.
These errors are displayed on the user's own browser, as they are sent back in lieu of the
expected page content. They can be recognized as ASP errors since 'ASP' always prefixes the
error code, for example ASP 0100. Typically, ASP errors arise when there is a problem either
with your web server or an external component. A failure to install a component correctly, or a
problem with the component code, is a very common cause behind ASP errors. You should
definitely suspect a problem of this nature if you experience either ASP 0115 or ASP 0177 errors.
As for preventing them – well if it's somebody else's code that is broken then, unfortunately,
there's not much you can do about them.
Good Coding Practice
So, you've spotted your error. Now what are you going to do about it? The first advice we're going
to offer is almost blindingly obvious. Don't write code with errors in it in the first place! This may
seem easier said than done, but if you stick to some well-worn practices, that programmers from
Roman times onwards have used, then you can vastly reduce the chances of introducing errors
into your page.
These techniques are, without exception, simple to understand, easy to execute and yet a lot of
programmers don't use them! "Why not?" you might be wondering. Well, because your average
programmer is a busy person, who doesn't like anything that distracts from the actual task of
programming. The main objections are usually that a) these techniques are time-consuming, b)
they require some degree of forward planning and organization and c) that they don't guarantee
that your code will be error free. Programmers might not couch their objections in these terms,
but that's what it boils down to. And remember, nothing can guarantee that your code will be error
free and, as doctors maintain, prevention is better than cure…
In this case it makes it easier to see to which bit of code each End If refers. If an End If had
been omitted, it would be much easier to spot with the code indented in this manner. When
debugging, you would only need to glance at the code to spot, for example, code at the end of the
structure that was still indented by several characters. You would know immediately that
something had been left out.
Of course the flip side to this is that the excessive indentation can make your code more difficult
to read. One line might start outside of your viewing area to the left and another line might finish
outside it to the right. In other words, you can't read the ASP script all at once and find yourself
constantly scrolling back and forth. To counteract this you always reduce the amount you indent
code, to two or three characters per indent.
Comments are also useful after loops, to indicate what is being repeated, or after Else and End
If to indicate what decision is being taken by that branch of the code:
If varQuestion = "Fax" Then 'If user selects fax then initiate fax
confirmation
...code...
ElseIf varQuestion = "Email" 'If user selects email then initiate email
confirmation
...more code...
Else 'Don't send user confirmation
...yet more code...
End If 'End of confirmation decision tree
It's simple to use. For server-side script, just put the following line at the beginning of your ASP
file, following the language and buffer tags:
<% Option Explicit %>
For client-side script you can add it at the beginning of the script:
<SCRIPT LANGUAGE=VBScript>
Option Explicit
. . .
</SCRIPT>
That's all there is to it, and it saves a lot of time hunting for those little typing mistakes. One thing
to remember is that once you've completed your debugging and removed all of the typos, then
you can remove Option Explicit; otherwise it may slow down your server.
Use Subprocedures
We introduced procedures in Chapter 5 and have already applied them to several of our
examples. What you may not have realized is how useful they can be in optimizing your code. As
you start writing more and more ASP code you'll find that you're using lots of similar routines in
many of your pages, and possibly even several times in the same page. Instead of repeating this
code you can put it into a sub procedure and then just call this procedure:
<%
' get the Form details
%>
Now the processing of the form details is only done in one place, and if anything is wrong you
only need to look for errors in one place. You can combine this with the trace statements too:
Sub ProcessFormDetails (strN, strE)
Response.Write "Debug: ProcessFormDetails Started<BR>"
This allows you to see when a sub procedure started and ended. You could even take this one
step further and create some debugging and tracing routines:
Sub Trace (strString)
End Sub
Your procedure to process the form details would now look like this:
Sub ProcessFormDetails (strN, strE)
Trace "ProcessFormDetails Started"
' do some processing here
The one thing you have to watch out for, when using include files, is the possibility of changes
affecting more than one ASP script. If you have taken out a set of routines and put them into an
include file, and then made them available to other ASP developers in your organization, you
must be careful not to suddenly change the functionality of those procedures. This could wreak
havoc amongst other programs, so be careful when using this method in a shared development
environment. To help safeguard against this, make sure you place comments in your include files
that describe its purpose and intended output.
Convert Your Variants into the Requisite Subtypes
Earlier we talked about logical errors, one form of which is the type mismatch error. One way of
avoiding type mismatches is to convert the value you're expecting from the user into the subtype
needed for any calculations or manipulation before you perform any operations on the data. If you
can't do this, then it's a fair guess that you're going to hit errors in your program. The VBScript
conversion functions provide the ideal tools for performing conversions:
So, for example, to convert your user value to an integer you could do the following:
IntMyinteger = CInt(varUservalue)
The other functions are all used in the same way. Of course if you wish to convert a value, it's an
idea to test the type of data you are converting; if the conversion is not possible then you will still
end up generating an error. VBScript also presents some type check functions to help:
So, if you're expecting a variant containing data of a certain type, it might be a good idea to check
for the type before converting. In our previous line of code, it would be wise to check and see
whether the data contained is actually numeric, before trying to convert our data to integer format:
If IsNumeric(varUserValue) Then
IntMyinteger = CInt(varUservalue)
Else
Response.Write "A number is required"
End If
Use a Variable Naming Convention (Consistently!)
In Chapter 4 we discussed the many benefits of using a good naming convention to help us keep
track of which variables contain which subtype of data, especially when we have lots of variables
in a program. We recommended a specific naming convention (go back and check it in Chapter 4,
if you can't remember it). In this manner, you can tell at a glance whether you're using an integer,
a string or date and can manipulate it in a consistent way. Our suggested convention was to use
the first three letters of a variable's name to distinguish the subtype. The fourth letter of the
variable was then typed in upper case, to indicate that this is where the actual variable name
starts. For example:
blnBoolean 'where bln denotes Boolean in our convention, and
Boolean is the
'variable name
intInteger 'int denotes an integer in our convention, and Integer
is the
'variable name
One problem is that naming conventions are used inconsistently. Programmers often start out
defining and naming all of their variables correctly – it is when a piece of a code has to be
changed or updated that things often go awry. For example, a temporary variable (called
something like test) is introduced and then, the next thing you know, that variable is being used
to pass data as well. You might find variables that 'break' our suggested naming convention in
this book – it's not deliberate, but this tip requires a lot of attention from the programmer and it is
easy to miss something.
A good strategy for doing this is to break down all the possible values into three types:
Expected values – these are values in the range that you ask for. If you asked for
numbers between 1 and 10, these would be 2 through to 9.
Boundary Condition values – these are the values that lie at the boundaries of our
range. If you asked for numbers between 1 and 10, then did you actually mean to include
these values or not? Does your program deal with them correctly?
Out-of-Bounds values – these are the values that fall anywhere outside the range. In
our example, -5, 0 and 999 would all be out of bounds values. But, it doesn't stop there; this
could equally apply to values of an unexpected type, such as a letter or date. For instance
were you expecting one of these values: 0, -1, 0.9, -1E5, 10.1, 5.5, "dog", #5/9/58#, "true",
"false", (empty), 4+4, 5/0, 9.9, 9.99, 9.999, 003, "three"? Will your program cope with all of
these types?
Testing your code with the kind of values you'd expect your user to supply is an absolute must. If
you ask a user to supply a number between 1 and 10, you must test your program using each
and every number between one and ten. And while, realistically, you couldn't test every possible
out of bounds value that could be input, you do need to hypothesize a large possible range of
values and different types. If your program doesn't break when the user inputs 11, that's all very
well and good, but does it still work when the user enters "three", which would be a perfectly
"correct" answer to the question in some user's minds?
Creating Error Traps
In my Computer Science class it was a ritual routine that once we'd finished a large program,
we'd pass it around other members of the class to see if they could break it. If someone did
manage to break it they'd display the bug with great merriment to the rest of the class (OK, we
were spotty teenagers then). On a more serious note, this is pretty much how everybody tests
code. Microsoft, for instance has a team of people coding applications by day and then another
team testing that same code at night.
In our case though, unlike Microsoft, a few simple lines of easily modifiable code usually made
our little applications relatively weatherproof. Let's look at a trivial program that takes a number
between 1 and 5 from the user and multiplies it by ten. The purpose of this exercise isn't the
calculation, but actually ensuring we get what we want from the user.
47. Try various other erroneous values. You will find that the only way you can get to this
screen is by entering the numbers 1,2 3,4 or 5. For example we've entered 3:
How It Works
As yet we don't know what type the subtype of variable is so we prefix it with var (short for
variant). First, we test to see if a letter has been input:
If IsNumeric(varTest) = False Then
We use the VBScript function IsNumeric, which returns False if the variant is anything other
than a number. Then we return a suitable message and transfer back to the first page and await
another response:
Response.Write "No Letters<BR>"
Server.Transfer "enter.asp"
Then, in turn, we check to see if the variant is too large, too small or a decimal. If it fails any of
these tests, we output a suitable message and return to the enter.asp page. Otherwise we
multiply the variant by 10 and output the result:
varTest = varTest * 10
Response.Write "<BR>Your lucky number is " & varTest
The idea is that this code is reusable, as you only have to change the first variable assignation to
whichever part of your form needs testing, and alter the values in the tests:
varTest = Request.Form("TextBox1")
Debugging ASP Script
Now you've done your best to waterproof your code, but errors always manage to seep through.
So how do you go about removing the little blighters? One difficulty you might encounter when
debugging is finding out exactly where the error is occurring. Remember the first ASP error we
showed you? We tried to access the Document object from within ASP:
Microsoft VBScript runtime error '800a01a8'
Object required: 'Document'
/BegAsp/Debug.asp, line 5
That's pretty easy to follow, but we've already mentioned that not all errors generate messages.
Even if your error does generate a message, it is not always the line that is at fault that generates
the error message (as in a type mismatch error). Let's look at some tips you can use to make
your errors easier to find.
Use Response.Write
This is one of the oldest methods of debugging, and involves putting in lots of trace statements
that indicate where you are in a particular script. If you remember, Response.Write writes a
line of text into the output stream, so this will be seen as text when the page is viewed. For
example, consider the following ASP script that expects some details from a Form on the
previous ASP page.
<%
Dim strName
Dim strEmail
strName = Request.Form("Name")
strEmail = Request.Form("Email")
Let's suppose an error occurs in the 'complex processing', and you have little idea what it is
doing. Changing the script can help track down the problem:
<%
Dim strName
Dim strEmail
strName = Request.Form("Name")
strEmail = Request.Form("Email")
Now when you run this script you'll see the name and email address displayed before the
processing starts, and this can tell you whether or not they are correct. If the last message does
not appear, then you know that the error occurred before it got to this line.
Although a very simple idea it's extremely valuable, and is a technique I still use to debug
complex ASP pages.
Note You can use another method of the Response object, namely End to stop
execution before hitting a trouble spot. You can then walk that Response.End
down through the code.
Conditional Tracing
Conditional tracing can be quite useful during the development of an ASP application, especially
if it is quite large. The process of tracing is the process of running a program and returning
information at each separate step of the program. This simply involves having a variable that tells
you whether or not tracing is in action. So our tracing routine could look like this:
Sub Trace (strString)
If blnTracing Then
Response.Write "Debug: " & strString & "<BR>"
End If
End Sub
You can now scatter Trace statements amongst your code, and you can simply turn tracing on
or off by setting blnTracing to True or False.
You might think that these techniques are a bit cumbersome, but let's have a look at combining
some of them into a set of tracing routines that can be included into any ASP script.
Open up your ASP editor and add the following code. This will be our HTML form.
<HTML>
<HEAD>
<TITLE>Tracing Form</TITLE>
</HEAD>
Enter your name and email address here:
</HTML>
1. Save this file as Tracing.html, close it and create a new one. This will be the ASP file
into which we have put the tracing statements.
2. Add the following code to this new file.
3. <% Option Explicit %>
4. <HTML>
5. <!-- #INCLUDE FILE="Trace.asp" -->
6. <HEAD>
7. <TITLE>Tracing Example</TITLE>
8. </HEAD>
9. <BODY>
10.
11. Tracing is on for this bit<BR>
12. <%
13. Dim strName
14. Dim strEmail
15.
16. TraceStart
17.
18. Trace "Getting form details"
19.
20. strName = Request.Form("Name")
21. strEmail = Request.Form("Email")
22.
23. ProcessFormDetails strName, strEmail
24.
25. TraceStop
26.
27. ProcessFormDetails strName, strEmail
28.
29. Sub ProcessFormDetails (strName, strEmail)
30.
31. TraceProcedureStart "ProcessFormDetails"
32.
33. ' some form of processing goes here
34. Response.Write "We are doing some processing here<BR>"
35.
36. Trace "Name=" & strName
37. Trace "Email=" & strEmail
38.
39. TraceProcedureEnd "ProcessFormDetails"
40.
41. End Sub
42. %>
43.
44. </BODY>
45. </HTML>
46. Save this file as DoTracing.asp, and create a new one. This will be our include file.
Note An include file is a way to include the contents of one file within another.
Thus we can define a routine or connection string etc. in one place and
use it from other files. They will be discussed in more detail in Chapter
10.
Dim blnTracing
Sub TraceStart()
blnTracing = True
Trace "Tracing started"
End Sub
Sub TraceStop()
End Sub
If blnTracing Then
Response.Write "Debug: " & strString & "<BR>"
End If
End Sub
End Sub
End Sub
%>
1. Save this file as Trace.asp, and close down your ASP editor.
2. Open the first file, Tracing.html, in your browser:
3. Enter some details and press the Process button.
You can see that all of our trace lines start with Debug. You can also see that the processing
procedure that outputs We are doing some processing here is run twice, but that the second time it is
run tracing has been turned off.
How it Works
We don't need to look at the HTML file – it's just a standard file with a form on it, whose purpose
is to call the ASP page. Before we look at this ASP page, let's first look at the actual tracing code
in Trace.asp
The first thing to notice is that we have a variable to determine whether tracing is enabled or not.
Dim blnTracing
We then have two procedures to set this flag. We also have a trace message telling us that
tracing has been turned on and off – this stops you worrying whether you have set it or not.
Sub TraceStart()
blnTracing = True
Trace "Tracing started"
End Sub
Sub TraceStop()
End Sub
We then have the actual trace procedure. This accepts a single string as an argument, and it
writes this to the output stream if the trace flag is True. This way you can still call the Trace
statement even if tracing is disabled:
Sub Trace (strString)
If blnTracing Then
Response.Write "Debug: " & strString & "<BR>"
End If
End Sub
Lastly we have two procedures that can be called at the start and end of procedures, just so you
can see when procedures are called.
Sub TraceProcedureStart (strProcedure)
End Sub
Sub TraceProcedureEnd (strProcedure)
End Sub
Let's now see how they were used in the ASP script in DoTracing.asp. After declaring the
script variables, the first thing to do is turn on tracing:
<%
Dim strName
Dim strEmail
TraceStart
Now tracing is enabled, we write a message indicating that we are about to get the form details:
Trace "Getting form details"
strName = Request.Form("Name")
strEmail = Request.Form("Email")
Once we have the form details in variables we pass them to our processing routine:
ProcessFormDetails strName, strEmail
We then turn off tracing, and call our processing routine again:
TraceStop
Now let's consider the actual processing routine. It doesn't actually do any processing in this
case, but simply displays a string. The first thing we do is to trace the start of the procedure:
Sub ProcessFormDetails (strName, strEmail)
TraceProcedureStart "ProcessFormDetails"
We then display our processing string, and display the name and email address using the Trace
statement:
' some form of processing goes here
Response.Write "We are doing some processing here<BR>"
End Sub
%>
So all we have done is used a few simple routines to see exactly where in the ASP code we are.
You can see that we can include tracing in routines, and then turn off tracing without worrying
about it, because the tracing routine detects whether tracing is in action or not. In other words,
you can turn on tracing to see what's happening and then turn it off with a single statement,
without having to remove the tracing code.
The Debugging Flags section is the one we are interested in. The first option is the most important
as checking this allows the Script Debugger to be used for ASP script code. Make sure this is
checked before you continue with the next example. We're not examining client-side debugging,
so it doesn't matter whether this is checked or cleared.
Important Some important things to note are that when you have enabled script
debugging for an application, error messages are not returned to the
client as part of the page, but raised on the server where the debugger
can intercept them. You should therefore only use the script debugger
when you can work on the server itself, and remember you can only
debug the server-side code. Also, having debugging enabled slows down
the server so once you have finished debugging you should disable this
option.
The Running Documents window displays a list of applications, along with their documents, that are
hosting scripting (non-hosting scripting documents are not included).
The Command Window allows you to inspect and modify variables during the execution of the
script code.
It's important to remember that to debug server-side ASP script the debugger needs to be open
on the web server. So if you access an ASP page with an error from a client machine, then the
debugger dialog will appear on the web server. Your client page will be blank, and will suspend
operation until the error dialog is cleared or the page reaches its timeout value.
Now you know how to set up debugging and how to start the debugger, it's time to see how it
works.
Now it doesn't work, but then you knew that already. So where are the errors, and how do you
find them using the Script Debugger?
1. Start the script debugger (on the server, if this is separate from the machine you're
creating pages on), and from the View menu select Running Documents:
2. Expand both the Microsoft Internet Explorer and the Microsoft Active Server Pages
levels, by clicking on the small plus signs. You might have to keep expanding these items
if there are more under the ASP branch:
This shows that the script debugger is now attached to both IE and IIS, and it shows the current
documents. Two copies of Debug.asp are shown, highlighting the two places where scripting
can take place – at the client, shown under Microsoft Internet Explorer, and at the Server, shown
under Microsoft Active Server Pages. It also shows a global.asa file, which was used in a
previous example and previous chapter and has no bearing in this example. If you can see any
other .asps, then this is because the sessions involved on these pages haven't timed out yet.
1. We are interested in ASP script so double click on the Debug.asp under Microsoft Active
Server Pages. This will open the ASP page:
Notice that the title states that this is read only. This is because the Script Debugger only allows
us to follow the script through and not actively change it. We can change variable contents
though, as you'll see soon.
1. Place the cursor on the first Response.Write line and press F9 to toggle a break point.
Notice that the line is highlighted in red, with a large dot to show a breakpoint.
2. Switch back to your browser and press the Refresh button. You are either automatically
switched into the Script Debugger, with the current highlighted line the one on which there
is a breakpoint, or the corresponding application on your task bar will flash, and you will be
switched to debugger when you click on it.
3. From your first run of this script you noticed that nothing was printed for strVar, but
why? Well, you've probably spotted that the second time we use the variable we've spelt it
incorrectly, putting strVsr instead of strVar. Because the script debugger only gives us
read only access to the script we can't change the variable name, but we can check that
strVar is correct.
4. From the View menu select Command Window.
5. Type ?strVar, pressing the enter key after you have typed this in:
The question mark is how we tell the debugger that we want to print out (in the command
window) the value of a variable. Now look at what the value is. Not what was really expected, but
that's because we've also made another spelling mistake in the name of the server variable we
wanted to look at – there's a T missing from the end.
Note You don't need to use the question mark to print out values when you are
debugging Jscript – you can just type the variable name.
1. Once again we can't modify the script, but we can modify the contents of the variable, so
type this in – note there's no question mark here. Don't forget to hit the Enter key after
you've typed it in:
2. strVar = Request.ServerVariables("HTTP_USER_AGENT")
This assigns strVar to the value of the server variable, using exactly the same syntax as if you
were typing the script in. In fact, you type using the syntax of the current script language – in our
case this is VBScript. Now examine strVar again:
That's better – much more like we expected. You can also update strVsr, just so that the
variable is returned to the browser, by typing:
strVsr = strVar
1. Now you've updated this variable, click back into the main script window and press F8 to
step onto the next line.
2. Move the cursor to the second Response.Write line and press F9 to set another
breakpoint. Now press F5 – this continues executing script until it finds another breakpoint,
or it finishes the script.
3. As the script has stopped at the second breakpoint we are now in a function, so it's a
good time to look at the call stack. From the View menu select Call Stack.
This shows where we are in the code, with the most recent procedure at the top. You can see we
started in a set of global code, with no procedure, and then moved into the ShowVariables
procedure. You can double-click on an item to get the main script window to show you exactly
where you are in the code. For example, if ShowVariables was called several times, but you'd
lost track of which time it was, you could double-click on the VBScript global code line to show
which call it was:
Notice the arrow in the margin, pointing to the calling routine. This doesn't change the currently
executing line, so pressing F8 will switch you back into the ShowVariables routine and continue
executing, one line at a time. As you're in a loop that iterates through the whole of the Server
Variables collection, you'll have to hit Next quite a few times to get to the end of it!
1. You'll also notice there is a spelling mistake on this Response.Write line, but because
this is in a loop, it's not really practical to change this every time. At this stage you are best
served by either continuing, even with the error, or stopping the script by selecting Stop
Debugging from the Debug menu. These sort of spelling errors are much more easily
caught by using Option Explicit to force all variable names to be declared, and any
variable not declared generates a run-time error.
That's really all there is to using the Script Debugger. You can see that it's quite a simple tool, but
very effective, allowing you almost everything you could expect from a more high-powered tool. If
you make syntax errors in your script code, then the Script Debugger will be launched
automatically, showing you the line with the problem.
Client-side versus Server-side Debugging
There's really no difference between client and server-side debugging. If you remember, in the
Running Documents window, you have a chance to decide where you want your debugging to
occur.
Remember that we double-clicked on the bottom instance of Debug.asp to open the main
window, and then set break points in this window. But what happens if you do the same with the
first instance of Debug.asp? Why not give it a try – you'll be rewarded with a window that might
not make sense. Before you try you'll need to clear any breakpoints in the ASP script (Ctrl-Shift-
F9) and then refresh your browser window.
This is the HTML page as returned by ASP. Remember when we are debugging the client-side,
all of our ASP script has been executed, so all we see here is the HTML tags and any client-side
script. Now this file doesn't have any client-side script, so it's not shown, but if it did have script
you follow exactly the same methods as debugging server-side script
The Server Object's Role in Debugging
One final aid is the ability to write your own custom-made ASP scripts to handle the occurrence of
errors. The Server object provides some of the most used and, indeed, most useful properties
and methods contained in any ASP page. However, unlike the rest of the objects in the ASP
object model, it doesn't seem to have been designed for one specific purpose, but rather as a
catch-all home for a miscellaneous and often unrelated set of properties and methods that run on
the server. This shouldn't detract from the importance of these attributes of the Server object.
Indeed, one method (CreateObject) is so important that we're taking a separate chapter out to
talk about what you can do with it.
We're not going to discuss all of the properties and methods of the Server object here, but there
is one method that has special relevance to debugging procedures and in the creation of custom
made error pages.
And that's all there is to it. It isn't particularly detailed, but the advantage of it was that it's easy to
use. You can suppress errors using the line On Error Resume Next, which tells the server to
carry on reading the script, even if it comes across any more errors. This requires that you do
your own error handling, and provide more detailed information, if you think it necessary.
This of course means a lot of extra work for ASP developers who have grown complacent from a
lifetime of Visual Basic or Visual C++, and are used to all sorts of weird and wonderful tools for
deconstructing and recompiling their code to get to the source of an error. So, you'll be glad to
find out that ASP 3.0 finally adds the error-handling capability that developers have been crying
out for, for a long time. It comes in the form of ASP's very own error object.
Note The VBScript Err object will still work, but a more comprehensive error object
has been introduced with ASP 3.0 that works in a different way to the Err
object.
In fact the ASPError object also does a lot of the dirty work for you. If you've generated an error
in your scripts then you've probably already used it by now. You can use this tool more effectively
if you understand what ASP is actually doing when an error is generated, so let's take a quick
peek into this murky world and find out what actually goes on when you generate an error. Here's
a very simple program with a very simple bug:
<%
for intLoop = 1 to 5
Response.Write intLoop
Nxt 'Error is on this line
%>
It's fairly obvious that we've missed out an 'e' in Next, but what does ASP do once it comes
across the error? Of course, it displays a familiar The Page Cannot be Displayed message.
However, if you scroll down to the foot of the screen, you'll find valuable extra information:
If you've used ASP 2.0 at all, then you'll know that you're now getting a lot more information than
you used to. All this extra information is actually generated by the ASPError object. What
happens when an error is generated? Well, ASP is told how to handle each type of error by the
Custom Error dialog in IIS 5.0. For each type of error (denoted by the number of the error) that
ASP encounters, you can use this dialog to instruct ASP to do one of three things. It can:
Throw up the standard, unhelpful "There has been an error" message
Redirect the user to a HTML file, which contains more details about that type of error
Redirect the user to a URL, and then execute the ASP file (or whatever else is found
there)
In the previous example, ASP actually performed the third of these options. You can check this
for yourself, as follows. Start up the Internet Service Manager/MMC console and highlight the
Default Web Site node in the left-hand box:
Go to the Action menu and select the Properties option then, when the dialog appears containing
many tabs, select the Custom Error tab. From this dialog you can control what page ASP will
display for each of the many possible different errors you may encounter. If you scroll down the
dialog you'll see references to all of the major errors, such as HTTP 404 (page not found) or HTTP
403 (access to the page is forbidden). If you check our previous example, you'll see that this was
the type of error generated was HTTP 500.100. In general, any errors that are generated within
the script on your page are going to fall under the header 500, which you will find is an Internal
Server error (if you look it up in Appendix G)
An Internal Server error indicates that a program running on the server (not necessarily an ASP
script, it could just as easily be CGI or another similar type of application) has encountered an
error. The subdivision 100 is used to indicate, more specifically, that the ASP itself has generated
an error. In which case the standard behavior set up in the Custom IIS error dialog is to transfer
you to the URL indicated:
Of course, an Internal Server error isn't a particularly informative response for the average ASP
developer. Usually, something more detailed is required. The page 500-100.asp is a standard
page stored in C:\Windows\help\iishelp\common and looks after ASP-generated errors. It
deals with pretty much every eventuality that a script can throw up, using the properties available.
However, there's nothing to stop you replacing these pages with your own web pages, which can
then utilize the Server.GetLastError properties to provide more effective error notification.
Using Server.GetLastError
One upside/downside (depending on how you look at it!) is that you can't turn off error handling
and use the On Error Resume Next statement to handle errors found in the ASPError object,
as you could with VBScript Err object. Consider the following code:
<%
On Error Resume Next
Dim arrHello(3)
arrHello(5) = 6 'this line has the error:
index/array size
Set objASPError = Server.GetLastError()
Response.Write "<BR>VBScript Error: " & Err.Description
Response.Write "<BR>ASPError Object Error Number: " & objAspError.Number
%>
We've generated a quick error by trying to assign a value to an array outside its previously
specified dimensions. The On Error Resume Next statement, as we mentioned earlier, would
automatically tell ASP that, even if it did find any errors, it should just go on to the next line as
though nothing untoward had happened. Under this scheme, you wouldn't generate an error by
executing his code.
The idea was that you could then use the VBScript Err object to pick up any errors that had
occurred. You could then return and deal with them separately, as indeed you still can. However,
when you create an instance of the ASPError object, you have to go to the default page (500-
100.asp or your own web page) to populate the ASPError object. If you use
Server.GetLastError(), after On Error Resume Next, it won't pick up any errors and the
ASPError object will be empty – just as if no error had occurred. So if we run our above
program, this would be the result:
As you can see from the screenshot, we return the contents of the VBScript Err object's
description method, which contains the message Subscript out of range, but the contents of the
ASPError object's method is equal to zero, which is the code used to show that no error has
occurred.
What is the long and short of this then? Well, if you want to create your own customized error
handling web page you need to go to the Custom Error dialog and reroute it, with the methods
described earlier, to your new URL (if its an ASP) or file (if its an HTML page). You can't just stick
Server.GetLastError in your own page and expect it to pick up error information – you have
to have been redirected to a page by the Custom Error dialog. Thus, if you do change error
handling and redirect ASP to your own custom page, just remember that you are changing the
error handling for all errors generated in ASP. So please take care!
Summary
You can clearly see that debugging and error handling in the world of ASP is not fraught with the
problems that you might have thought. With a little careful planning and a simple set of rules to
follow, it can be made even easier. Here are some of the simple rules that you should follow:
Use Option Explicit. This saves huge amounts of time looking for typing mistakes.
Remember to take it out of your scripts after the page has been successfully debugged
otherwise it could degrade performance.
Reuse code where possible, by use of sub routines and include files.
Don't get confused between client-side and server-side script.
Use sensible variable names. This helps prevent confusion as to what the variable is for.
Comment your code. A lot of experienced programmers joke that if it was difficult to write,
then it should be difficult to read. That's just an excuse for laziness. Try to provide code for
others to read that you would like to read yourself. If you're worried about the slower
processing of a commented page, then why not keep two versions: one version with
comments for development and documentation and a second version without comments, for
use in the production environment.
The script debugger provides a very useful tool, which can make locating and testing errors that
much easier. Also, the addition of an ASPError object, in ASP 3.0, means that errors are
reported in a more detailed and comprehensive way in ASP 3.0.
If you've done any programming before, you've probably realized that ASP programming is not
that different from any other type of programming and that you need the same range of skills. You
need to plan ahead and be prepared to think laterally when errors occur, as the problem isn't
always where you think it is.
Chapter 10: The Scripting Objects
Overview
In the last few chapters, we have been looking at some of the different objects that can be used
with Active Server Pages. These objects, such as the Application and Session objects, are
built-in to Active Server Pages. We have mentioned in previous chapters that there are also
objects on the client-side that are built into the scripting language. As ASP utilizes scripting
languages to access its own built-in objects, it stands to reason that you should, in ASP, be able
to use the built in objects provided by VBScript, Jscript, or the different objects that come free
with libraries distributed as part of IIS 5.0, and indeed you can.
These objects do not directly deal with the communication between the client and the web server,
as the built-in objects do. These objects provide additional functionality to the scripting language
itself. This is why they are known as the scripting objects. The scripting objects add some
important features to ASP, such as the Dictionary object and the RegExp object. The
Dictionary object allows you to store and index data about your application, in a manner
similar to a dictionary, and the RegExp object allows you to search for multiple occurrences of a
sequence of characters within a large volume of text.
We're going to look at six scripting objects that a developer can use from within their scripts (there
are over twice that amount). The first that we'll consider is the Dictionary object, which allows
you to store information in a single data structure for easy retrieval. It is similar to an array, and it
is also similar to a collection. The FileSystemObject object provides access from an Active
Server Pages script to the hard disk file system of the server. This object will allow you to work
with files, as well as directories and sub-directories.
The TextStream object allows you to deal with the contents of a file that you have got
information about using the FileSystemObject object. You can read information from the file
and write data to it. These are all Scripting Runtime objects.
The odd one out we'll look at is the RegExp object, which comes as part of the VBScript itself.
Regular expressions are something that JScript has enjoyed support of for years, and they have
been added to version 5.0 of VBScript. They can be used to search for and manipulate a
sequence of characters in a large amount of data. We'll first look at how you'd create an instance
of a scripting object in your pages.
Creating an Instance of a Scripting Object
When you want to use a Scripting object, the first step is to create an instance of an object using
the VBScript method CreateObject.
Note You'll recall our discussion of the relationship between objects and instances
from Chapter 6. Essentially, the class is the template of the object; and an
instance is a particular copy of the object which is created in the shape of that
template. This action is similar to cookies (the instance) coming out of a cookie
cutter (the class).
Once we've created an instance, it must be assigned to a variable name (the above line wouldn't
work on its own) – this allows you to refer to the instance later on in your code.
Although you can use any name, many programmers prefix the name of their object (and indeed
our suggested naming convention does), by appending the letters obj before it. For example, if
you were using an instance of the Dictionary object, then you might want to give it a variable
name like objDictionary or objDict. A typical instantiation might look something like this:
Set objDictionary = CreateObject ("Scripting.Dictionary")
Note Instantiation is the name we give to the process of creating an instance of an
object. For example, in this piece of code we have instantiated
objDictionary, which is an instance of the Dictionary object.
As you can see, we have used the Set statement, followed by the name that we have chosen for
our instance. Once you've used the Set statement, and the CreateObject method in this way,
the instance of the object will be ready to use. Subsequently, you use the instance by referring to
it using the name you give within the Set statement.
The difference is that CreateObject goes through COM for creation while New means the
object goes to the VBScript runtime engine. At the moment you don't need to worry about why
this happens, but you need to be aware that it is preferable for your object to be created by COM
if you intend it to use it in either a secure application, or one that involves transactions.
Since dealing with data is very important to a lot of web applications, let's take a look at how the
Dictionary object allows you to manipulate sets of data very easily.
The Dictionary Object
When you pick up a copy of Webster's dictionary, you have the ability to find out information
about a particular word. In this case, the information is the definition of the word. All of the words
are organized in a particular order. In a dictionary, this order is alphabetical. Associated with
every word in the dictionary is its definition. This could also very easily be a synonym for the
word, or a picture of what the word represents. The key concept is that for each word, there is a
piece of information.
The Dictionary object can be thought of as a Webster's for your application. You can store
information in it and attach a keyword to each piece of information. Later, when you want to
retrieve the information, all you do is provide the dictionary with the keyword, and it will return the
information you have stored there.
Try It Out – Simple Dictionary Example
To start becoming familiar with the Dictionary object, let's take a look at a very simple example
that shows you how to store and retrieve information from a Dictionary object.
1. Use your editor of choice to create the SimpleDictionary.asp file with the following
source code.
2. <%
3. Dim objDictionary, strKey, strValue
4. Set objDictionary = CreateObject("Scripting.Dictionary")
5. objDictionary.Add "Apple", "Red"
6. objDictionary.Add "Lemon", "Yellow"
7.
8. Response.Write "<P>All Data Stored in the Dictionary"
9. Response.Write "<P>Let's retrieve"
10. Response.Write "<HR>"
11. Response.Write "<P>Retrieving Data..."
12. strValue = objDictionary.Item("Apple")
13. Response.Write "<P>Value stored with key of 'Apple' is " &
strValue
14.
15. strKey = "Lemon"
16. strValue = objDictionary.Item(strKey)
17. Response.Write "<P>Value stored with key of '" & strKey & "' is
" & strValue
18. %>
19. Save the file in your BegASPFiles folder.
20. View the page in your web browser.
How It Works
Using the Dictionary object is actually quite easy. The first step is to create an instance of the
Dictionary object. The ProgID is Scripting.Dictionary, so all we need to do is:
Set objNewDictionaryObj = CreateObject("Scripting.Dictionary")
Once we have created an instance of the Dictionary object, we are ready to add data to it. The
data that is stored in the dictionary is stored as a name/value pair. The name part of the pair is
known as the key. When you add an item to the dictionary, you will use the Add method. This
method takes the key and the value associated with the key and adds it to the Dictionary.
objDictionary.Add key, value
You can either pass the explicit values of the key and value parameters, or you can pass
variables that hold the data that you want for either parameter.
After we have stored items in the Dictionary object, the next logical step is to retrieve the
information. To access information in a Dictionary, you need to supply the key. Given that key,
the Dictionary object will return the value associated with it. This returned value can either be
stored in a variable, or used immediately in another method. As with the Add method, you can
either pass an explicit reference to the key's name, or you can pass a variable that contains the
name of the key.
Note Since in VBScript all variables are treated as variants, you don't need to worry
about what the data type of the data that is stored in the Dictionary object is:
except in one case, that is. If you have stored a reference to an object as a
value in your dictionary, you need to use the Set statement to assign the
reference to another variable.
To access the information for a specific key, you will use the Item method of the Dictionary
object.
value = objDictionary.Item(key)
Now that we have loaded information into the Dictionary object and successfully retrieved it,
the next step is to look at how to change the value of an entry in the Dictionary.
How It Works
In the first example, we manually stored information in the Dictionary object. In this example
we do the same thing again. You might be tempted to think why not store this in a Session
variable, and then this information would persist between pages? Unfortunately due to a fault in
ASP this isn't possible, and if you try it, you will slow your server to a crawl. We'll point you to a
workaround at the end of this section, but for now you'll have to accept that we need to populate
the Dictionary object by hand, each time we use it.
We confirm once again that we've successfully stored our items and keys in the Dictionary
object, by displaying them on the screen:
strValue = objDictionary.Item("Apple")
Response.Write "<P>Value stored with key of 'Apple' is " & strValue
strKey = "Lemon"
strValue = objDictionary.Item(strKey)
Response.Write "<P>Value stored with key of '" & strKey & "' is " &
strValue
Response.Write "<HR>"
So far, so good, this is still the same as in the previous example. Next, we change the value
contained within our Apple key from red to green. We display details showing that the
information has been changed:
objDictionary.Item("Apple") = "Green"
Response.Write "<P>Changed the value of Apple to Green"
Response.Write "<HR>"
Response.Write "<P>Value stored with key of 'Apple' is "
Response.Write objDictionary.Item("Apple")
Response.Write "<HR>"
Our next step is to do the same for the second object we have stored in our dictionary, except this
time we're changing the key, rather than the value. We change it in exactly the same way, except
here we use the key method rather than the item method:
objDictionary.Key("Lemon") = "Banana"
Response.Write "<P>Changed the key of Lemon to Banana"
Response.Write "<HR>"
Response.Write "<P>Value stored with key of 'Banana' is "
Response.Write objDictionary.Item("Banana")
In the previous example, you saw that the Item method could be used to access the value. Now
using this method, as you have seen above, it can also be used as the left side of an assignment
statement to set the value for a particular key:
objDictionary.Item(keyValue) = newValue
We also changed the key itself. While this technically makes sense, from a real-world perspective
it may not be a wise thing to do. If you are familiar with relational databases, then you know that
each record in a table must have a unique key, and you cannot change the identity of that key.
Changing the key itself in a Dictionary object is a very similar operation. A good word of
advice is to be very sure you know why you are doing this when you decide to do it. The method
we used to change the key itself was as follows:
objDictionary.Key (key) = newKey
These examples have shown you how to retrieve values from the Dictionary object by
knowing the keys themselves. But what if you want to access information without knowing the
keys? This next example will show you how to do this.
Try it Out – Retrieving Values When You Don't Know the Key
1. Using your favorite ASP editor, enter the source code for
GetAllValuesDictionary.asp.
2. <%
3. Dim objDictionary, strKey, strValue
4. Set objDictionary = CreateObject("Scripting.Dictionary")
5. objDictionary.Add "Apple", "Red"
6. objDictionary.Add "Lemon", "Yellow"
7.
8. strValue = objDictionary.Item("Apple")
9. Response.Write "<P>Value stored with key of 'Apple' is " &
strValue
10.
11. strKey = "Lemon"
12. strValue = objDictionary.Item(strKey)
13. Response.Write "<P>Value stored with key of '" & strKey & "' is
" & strValue
14. Response.Write "<HR>"
15.
16. Response.Write "<P>Retrieve list of keys"
17.
18. For Each key in objDictionary
19. Response.Write "<P>Key = " & key & " -- Value = " &
objDictionary.item(key)
20. Next
21.
22. Response.Write "<HR>Retrieve list of values"
23. For Each item in objDictionary
24. Response.Write "<P>Value = " & objDictionary(item)
25. Next
26. %>
27. Save the file in your BegASPFiles folder.
28. View the GetAllValuesDictionary.asp file in your web browser.
How It Works
In this example, we are looking for a way to retrieve all of the keys or items in the Dictionary
object at once. There are many practical applications of this. For example, you could have
multiple pages all dumping information into a Dictionary object. Another page could quickly
access all of the information without having to know all of the keys that have been added.
There are two sets of information that can be retrieved from the Dictionary object:
A list of all keys in the Dictionary
A list of all values for the keys in the Dictionary
There are different uses for both of these sets of information. If you retrieve all of the keys from a
Dictionary object, then you can use the Item method to retrieve each corresponding value.
Unfortunately, if you retrieve all of the items in the Dictionary object, there is no way to tie
each item back to its key.
Once again we started by manually populating the Dictionary object. The first way we
retrieved information in bulk from the Dictionary object was with the Keys method. This
method returns an array, where each element contains one key. Once we have this array, we can
loop through the array and display each of the keys on the client.
For Each key in objDictionary. keys
Response.Write "<P>Key = " & key & " -- Value = " &
objDictionary.item(key)
Next
The other type of information that we retrieved in bulk from the Dictionary object was all of the
values in the Dictionary. This was done using the Items method.
For Each item in objDictionary. items
Response.Write "<P>Value = " & item
Next
This method will return an array that contains all of the item values from the Dictionary object.
With this array, we can iterate through it and display each of the values to the client. As we stated
before, we have no way of retrieving the corresponding key for each item. So all we can do is
display the value on the client.
Before we close this section on the Dictionary object, here's one word of warning.
This is a bit beyond the scope of this book, so we won't be covering this problem in too much
detail. The basic problem is this: if you store a Dictionary object in a Session object, your
web server will slow down in direct proportion to the amount of people accessing it. Worse still, if
you store a Dictionary object in an Application object, you could crash the web server
completely. This is due to a bug in the Dictionary object (when the Dictionary object was
originally written, it was only intended to be used on the client side). There is a component
available from Microsoft, which allows you to pass this kind of information between pages. It is
the Lookuptable component and it is available from
http://msdn.microsoft.com/workshop/server/downloads/lkuptbl_eula.asp. You
can find more details about this error and how to avoid it at
http://msdn.microsoft.com/msdn-online/MSDNchronicles2.asp.
We have now covered the various ways that the Dictionary object can be used. Next, we will
take a look at a pair of objects that will allow you access to the file system and files of your web
server. These objects will allow you to manipulate the files and directories on your server, as well
as manipulate the contents of the individual files themselves
The FileSystemObject Object
When our ASP server is handling requests from its clients, there are two types of pages it can
return. As we have seen in this book, we can use the scripting power of ASP to dynamically
create pages on-the-fly and send them back to the client. We can also serve static pages that are
stored on the web server itself. But up until this point, we, as web application developers, had to
know which files were stored in what place on what drive.
With the FileSystemObject object we now can use our code to access the file system of the
web server itself. This will allow us to:
Get and manipulate information about all of the drives in the server. These can be
physical drives or mapped drives that the web server is connected to
Get and manipulate information about all of the folders and sub-folders on a drive
Get and manipulate information about all of the files inside of a folder
With this information, there is a very broad range of things that we can do with the file system.
Aside from setting security information, basically anything that you can do with the file system
using Windows Explorer or File Manager can be done using the FileSystemObject object
Let's take a look at each of the objects in the object model and briefly describe what they are
used for. Then, our examples will show different ways to use the FileSystemObject object in
real-life web applications.
Most of the information contained in the Drive object is read-only. You can't change the amount
of free space that a drive has available using the Drive object. However, you can change the
Volume name of a drive, if it can physically be changed.
Now, let's take a look at a few examples that show you the different ways that you can use the
FileSystemObject object.
With IIS 5.0 we have the ability to enable directory browsing. This can be done from the Properties
option of the Action menu of the MMC for the directory we wish to enable browsing for. In the
following screenshot we would be enabling browsing for the BegASPFiles directory/application
only:
By checking the Directory Browsing checkbox, then the client is able to view the contents of the
selected directory. However, this method has some drawbacks. If there is a default document,
such as index.htm in this example, then there is no way to display the directory. Also, if the
directory is displayed, then there is no way to control the fact that each file in the directory is also
an active link. This has many implications, especially in the area of security. This setting will allow
unauthorized individuals to see hidden files and URLs. This is why most commercial sites disable
directory browsing.
This first example will show how to use the FileSystemObject object to display a listing of the
files in the current directory.
1. Using your editor of choice, create the DisplayDirectory.asp file with the following
source code.
2. <HTML>
3. <HEAD>
4. <TITLE>Chapter 10 Example - Display Directory</TITLE>
5. </HEAD>
6. <BODY>
7.
8. <%
9. Dim strPathInfo, strPhysicalPath
10. strPathInfo = Request.ServerVariables("PATH_INFO")
11. strPhysicalPath = Server.MapPath(strPathInfo)
12.
13. Dim objFSO, objFile, objFileItem, objFolder, objFolderContents
14. Set objFSO = CreateObject("Scripting.FileSystemObject")
15.
16. Set objFile = objFSO.GetFile(strPhysicalPath)
17. Set objFolder = objFile.ParentFolder
18. Set objFolderContents = objFolder.Files
19. %>
20.
21. <TABLE cellpadding=5>
22. <TR align=center>
23. <TH align=left>File Name</TH>
24. <TH>File Size</TH>
25. <TH>Last Modified</TH>
26. </TR>
27.
28. <%
29. For Each objFileItem In objFolderContents
30. Response.Write "<TR><TD align=left>"
31. Response.Write objFileItem.Name
32. Response.Write "</TD><TD align=right>"
33. Response.Write objFileItem.Size
34. Response.Write "</TD><TD align=right>"
35. Response.Write objFileItem.DateLastModified
36. Response.Write "</TD></TR>"
37. Next
38. %>
39.
40. </TABLE>
41. </BODY>
42. </HTML>
43. Save the file in your BegASPFiles folder.
44. View the page in your web browser.
Note When you're trying out this example, you might come across the error
Object doesn't support this property or method: 'GetFile'. If so, it's because
the Scripting Runtime Library (scrrun.dll) hasn't been installed
properly. We recommend that you go back and reinstall the Scripting
Runtime Library (available from
http://msdn.microsoft.com/scripting).
How It Works
After putting all of the requisite header stuff at the top of the page, the first thing that we need to
do is determine the physical path to the current file. Our ASP page will display a list of the files in
the directory that the file resides in. The URL that is used to display this file is a virtual path. This
means that the path is defined by the web server's configuration. The virtual path does not
necessarily correspond to the file's location in the drive's local directory tree. It's also very
important to remember that, for the FileScriptingObject objects to work, they need the
physical path and not the virtual path.
To get the physical path to the current file, we need to start with the virtual path to the file. This
information can be found in the HTTP variable PATH_INFO. In our example program, this variable
contains the following string:
/BegASPFiles/displayDirectory.asp
This should look familiar – it's the part of the URL that follows the name of the server. To retrieve
this value, we will use the Request object's ServerVariables collection.
Now that we have this value, we need to translate it to the physical path. There is a Server
object method that will allow you to do this. The MapPath method will take a virtual path and
convert it to a physical path. After we convert our virtual path to a physical path, we have:
C:\InetPub\wwwroot\BegASPFiles\DisplayDirectory.asp
This is the data that we need to use with the FileScriptingObject objects. In this example,
the virtual path looks very similar to the physical path. This is a just a coincidence in the way that
this particular web server is configured. In other cases, only the file name could be similar.
To start working with the FileSystemObject object, we will need to create an instance of it
using:
Set objFSO = CreateObject("Scripting.FileSystemObject")
We create the object and save its reference in the objFSO variable. Before we dive into how the
code itself works, let's look at the strategy we will use to generate the directory.
We are starting with the physical path to the file. This file is in the directory that we want the
contents for. So, we need to find a way to relate this file to the directory that it's in. Fortunately,
one of the FileSystemObject objects provides this type of functionality.
To get started, we need to get a File object that corresponds to the file that we have the path
for. If you remember, the File object allows you to get information about a particular file. The
FileSystemObject object itself provides a method called GetFile that will take a physical
path to a file and return a reference to a File object that represents that file.
Now that we are armed with a File object representing a file in the directory we are interested in,
we can use a property of the File object to get a reference to the folder that it's in.
Note We have been using the words directory and folder interchangeably. For those
of us who came from the DOS world, our files have always been arranged in
directories. With the advent of Windows 95 and the addition of Windows
Explorer, the folder term has become more prevalent. But they both still refer to
the same thing.
One of the properties of the File object is the ParentFolder property. Given a valid file object,
this will return a reference to a Folder object that represents the folder that the file resides in. If
you have a File object in the root folder, the ParentFolder property will be null (not known or
missing). We have now reached our objective of having an object that represents the directory we
are interested in.
Now it is time to look at the files that are in this directory. To do this, we will use the Files
collection that is stored as a property of the Folder object. This collection is a set of File
objects; one object for each file in the directory.
To display the files, we will be using a <TABLE> for formatting. There will be three columns in the
table, we will display the file name, the size of the file in bytes, and the date and time the file was
last modified. The easiest way to go through all of the items in a collection is to use the For
Each loop statement.
For Each objFileItem in objFolderContents
This statement will set up a loop structure that will be called one time for each object in the
objFolderContents collection. Each time through the loop, the reference to the current object
will be available using the objFileItem variable. The information that we are interested in
displaying is available in three properties of the File object:
objFileItem.Name – returns the name of the file
objFileItem.Size – returns the size of the file in bytes
objFileItem.DateLastModified – returns the date and time of the last modification
of the file
Each of these pieces of information will be stored in their own table cell. Before moving to the
next item in the collection, we will need to end the current row in the table. Once we have
reached the end of the collection, we can end the table, then finish the page and send it back to
the client to be displayed.
The next example will enhance the directory viewer page that we have just completed. While
displaying a list of files is nice, we may want to be able to interact with the files in the list. This will
make the directory an interactive directory, similar to the directory provided by the web server, but
with you – the developer – in control of the way the information is presented. If we enable the web
server to allow directory browsing, then the contents of the directory we are working in would look
like this:
Since this page is generated by the server itself, there is no way that we can change how that
page looks. So let's take a look at how to use the FileSystemObject objects to obtain the
same information, and using the power of ASP, create a different presentation of the data.
How It Works
We have made three major changes to the previous example to arrive at this new interactive
directory display.
First, we have added some text formatting of the file information. There are two text styles that we
will be using. One will be for the display of the file name itself in the directory listing. The other will
be for the display of the other pieces of information about each file. To make the file names stand
out, we have put the file names in a different font at a larger font size.
Note Note that while we set the color for the file data information, we did not set a
color for the file name. This is because the file name will be an active link to the
file. This means that the color of this text will be controlled by the settings in the
user's browser. If we were to change the colors, then we run the risk of
confusing the user as to which files they may have already visited.
Second, we added an <A HREF> tag to the name of the file itself. This is to replicate the
functionality that is in the standard directory browsing display. All files in that display have a link
associated with them. This will allow the user to click on the name of the file to navigate to it. To
support this functionality in our page, we need to wrap the name of the file with a <A HREF> tag.
The value of the HREF will need to be the same as the name of the file, so that when the user
clicks on the link they will be taken to that file.
Note Even though the user may be able to navigate to any file in the directory, there
may be files present that, when clicked on, will not return any valid information.
Other files may actually include information that you don't want the user to be
able to see. So while the ability to list files in a directory may be powerful, you
need to exercise some care when using it.
Finally, we have added another column to our directory display. This column will display the file
type for each file. This is the same file type that would be displayed for the file when viewed using
Windows Explorer. To display this information, we retrieve it from the Type property of the File
object.
Displaying directories is a nice feature, but let's look at some of the other things we can do with
the FileSystemObject objects. One thing that you see on a lot of web pages is some small
text at the bottom of the page that identifies certain properties about the page. Some of these
properties include:
File Name
Size
Creation Date
Last Modification Date
Last Accessed Date
For a person viewing the page, this information can be helpful in determining the accuracy of the
information on the page, or it can be useful for debugging purposes. Whatever it is used for, there
should be an easy way to add it to each page. We'll now look at another method that can be used
in ASP to further enhance our directory display example; the practice of including information
already processed by the server.
Server-Side Includes
Server-side includes (SSIs) are a very useful way of making your site easier to manage, and for
providing extra information. The 'including' is done at the same time as the Active Server Pages
interpreter gets to see the page, so generally include files should be placed at the head of your
ASP file. This means that information placed in the file, when included in the web page at the top,
can be used throughout the script code. If you wanted to, it is possible to use code to decide
which SSI #include directives we want to put into action, if you place them within an If...Then
construct. There are five basic types of SSI we can use. We can:
Include text files in our pages, as they are loaded
Retrieve the size and last modification date of a file
Define how variables and error messages are displayed
Insert the values of HTTP variables in the page sent back to the browser
Execute other programs or scripts, such as CGI and ISAPI applications
Only the first of these is directly applicable to Active Server Pages. SSIs are normally used in a
separate file, which can be referenced and loaded from an ASP file.
One thing to watch out for is if you want to include script from another file, this file must contain
complete script sections. In other words, it has to have opening and closing <SCRIPT> or <%...%>
tags – we can't place part of the code section in an included file, and the rest in the main page.
Theoretically, we could include half of, say, an If...Then construct in the file, and the rest in the
main page, as long as each part was enclosed in <%...%> tags. This isn't likely to produce code
that is easy to read or debug later, though!
Of course, the text we include doesn't have to be VBScript or JScript code. We can quite easily
use it to include HTML or just plain text. If your site uses pages with standard footers for your
copyright notice, or a standard <STYLE> tag to set up the text and page styles, these can equally
well be stored as a separate file, and referenced with a #include statement.
We can also, as you've already seen, use relative paths. If the file is in the same folder, we just
use the file name. If it's in the Projects subdirectory, we can use:
<!-- #include file="Projects\MyFile.txt" --> 'physical path
In this example, we will actually be building an SSI that can be added to any Active Server Page.
This server-side include file has two primary functions. First, it needs to obtain a File object for
the file that it is included in. Then, using that File object, we can display the information about
the file itself.
If we were to just load this file directly from the server, then it would be displayed as text rather
than being passed through the ASP engine. This would not be what we are looking for. The nice
part about making it a server-side include file is that even though it is a separate file, when the
server includes it into the other file, it acts as if it were part of that file. This means that when we
get the File object that corresponds to the current file, we get the file that we are really
interested in.
In the previous examples, we have shown how to get a File object that corresponds to the
current file. In those examples, we went a step further and used that File object's
ParentFolder property to get a reference to the current folder. In this server-side include file,
we will stop once we get the reference to the File object.
One change that we have made from the earlier examples is that we are going to use different
variable names. In the earlier examples, the variable names were both easy to read and
corresponded closely to the data the variable contained. In a server-side include file, you need to
be careful about the naming of any variables. Since this file is actually treated as part of the file
that it is included in, you need to ensure that the variable names do not conflict. In our example,
since we are leveraging code from the InteractiveDirectory.asp example, our variables
would have been named
strPathInfo
strPhysicalPath
objFSO
objFile
If this server-side include file is then included into the InteractiveDirectory.asp file itself,
then there would be two statements that each declared a variable with each of these names. This
would cause a script error when the page was processed since these variables already exist
within InteractiveDirectory.asp. To get around this, we have changed the names of the
variables in the hope that they will be unique. Since this server-side include file provides
additional file data, we have added a FD to each variable name. This results in variables named
strFDPathInfo
strFDPhysicalPath
objFDFSO
objFDFile
With our File object referencing the file we are interested in, we can now turn to the display of
the information. The information will be visually separated from the rest of the page with a
horizontal line generated by the <HR> tag. To set the formatting for all of the information, we have
created a <DIV> section and set the text format properties for that container element. All of the
information that is displayed is retrieved from a property of the File object except one. Since the
FileSystemObject objects deal with files in their physical space, the File object has no
information about the virtual path to the file as seen by the web server. Since we want to display
that information on the client, we will need to retrieve that information from the HTTP Server
Variables that are passed with each request to the server. The strFDPathInfo variable will
contain the virtual path that points to the file we are currently viewing.
Up until now, we have been using the FileSystemObject objects to access information about
the properties of folders and files in the physical file system. They also provide one other function
with respect to files. With a valid File object, you can open the file itself as a text file and deal
with the data contained inside it. To do this, you will interact with the file's data using the
TextStream object.
There are three ways that you can get a TextStream object. With a valid File object, you can
use the OpenAsTextStream method. This will return a TextStream object that you can then
use to manipulate the contents of the file. If you know the physical file name of the file, and don't
want to worry about creating a File object for it, then the FileSystemObject object's
OpenTextFile method will open the file in the same way. Lastly, if you want to create a brand
new file and add text to it, you can use the CreateTextFile method of the
FileSystemObject object and pass it the name of the file you want to create. This method will
return a TextStream object, which you can use to add text to the file.
Our first example will look at just accessing the information contained in a file. One of the best
ways to learn how to program on the web is to look at other people's source code. You can learn
many HTML tricks by looking at the source code of pages you like. But how can you use this
same approach to learn the insides of ASP? With ASP, the source is interpreted on the server,
and all the client sees is the completed HTML.
In this example, we will create an Active Server Pages script that will display the source of any of
the ASP files on your server. The file name will be passed in as a URL parameter. We will also
show how to link it to an existing ASP page.
1. Using your editor of choice, create the DisplaySource.asp file with the following
source code:
2. <%
3. Const ForReading = 1, ForWriting = 2, ForAppending = 8
4. Const TristateUseDefault = -2, TristateTrue = -1, TristateFalse
= 0
5. Dim strPathInfo, strPhysicalPath
6. strPathInfo = Request.QueryString("FileName")
7. strPhysicalPath = Server.MapPath(strPathInfo)
8.
9. Dim objFSO, objFile
10. Set objFSO = CreateObject("Scripting.FileSystemObject")
11. Set objFile = objFSO.GetFile(strPhysicalPath)
12. %>
13. <HTML>
14. <HEAD>
15. <TITLE><%= objFile.Name %> Source Code</TITLE>
16. </HEAD>
17. <BODY>
18. Source code for <%= objFile.Name %><HR><P>
19. <FONT FACE=Courier SIZE=2>
20.
21. <%
22. Dim objFileTextStream
23. Set objFileTextStream = objFile.OpenAsTextStream(ForReading,
TristateUseDefault)
24.
25. Dim strLine
26. Do While objFileTextStream.AtEndOfStream <> True
27. strLine = Server.HTMLEncode(objFileTextStream.ReadLine)
28. strLine = Replace (strLine, Chr(9),
" ")
29. Response.Write strLine
30. Response.Write "<BR>" + vbCrLf
31. Loop
32. objFileTextStream.Close
33. %>
34.
35. </FONT>
36. <BR>
37. <BR>
38. <HR>
39. <A HREF="<%= strPathInfo %>">Return to Displayed File</A>
40. </BODY>
41. </HTML>
42. Save this file in your BegASPFiles folder.
43. To link to this file, add this source code to the InteractiveDirectory.asp file from a
previous example:
44. </TABLE>
45. <!-- #include file="FileDetails.asp" -->
46. <HR>
47. <A HREF="DisplaySource.asp?FileName=
48. <%= Server.URLEncode(Request.ServerVariables("PATH_INFO")) %> ">
49. Click here to see ASP source</A>
50. </BODY>
</HTML>
51. View the page InteractiveDirectory.asp in your web browser.
52. When you click on the hyperlink at the bottom of the page, you will then see this in your
browser.
How It Works
For this application to work, we need to build on what we did in the previous examples. In order to
create a TextStream object, we need to start with a valid File object for the file we are
interested in. To get this, we will use the tried and tested method that we have used in the
previous examples.
Dim objFSO, objFile
Set objFSO = CreateObject("Scripting.FileSystemObject")
The difference in this case is that the virtual path to the file we are interested in is passed in as a
query string variable. So, to begin our steps to getting a File object, we need to use that passed
in value to compute the physical file path, instead of using a server variable as we did in the other
examples.
Set objFile = objFSO.GetFile(strPhysicalPath)
Once we have the valid File object, the next step is to open that file as a text file. To do this, we
will use the OpenAsTextStream method of the File object. This method will return a reference
to a TextStream object that contains the contents of the file.
Dim objFileTextStream
Set objFileTextStream = objFile.OpenAsTextStream(ForReading,
TristateUseDefault)
The first parameter is used to determine the state the file is opened in. There are three possible
states for opening the file. To make the code more readable, we have defined constants at the
top of the source file for these three states.
The second parameter indicates in what file format the file should be opened. Possible values
are:
TristateTrue – This will open the file in UNICODE format.
TristateFalse – This will open the file in ASCII format.
TristateUseDefault – This will use the default setting for the web server.
To be safe, you should usually open the file in the default mode, unless you are absolutely sure
that the contents are not of the default type.
Once the file is open, we can begin reading the information from the file. There are a number of
ways information can be read from the file. It can be read all at once, using the ReadAll method.
This will return all of the text in the file in one big chunk. While this is the easiest to implement, it
isn't very effective for large files. Rather than reading the file all at once, it can be read in pieces.
There are two different size chunks that can be read from a file. The Read method can be used to
read a certain number of characters from the file into a string. This number of characters is
determined by a parameter passed to the Read method. Alternatively, the file can be read one
line at a time, as we have done in this example - using the ReadLine method:
Dim strLine
Do While objFileTextStream.AtEndOfStream <> True
strLine = Server.HTMLEncode(objFileTextStream.ReadLine)
strLine = Replace (strLine, Chr(9), " ")
Response.Write strLine
Response.Write "<BR>" + vbCrLf
Loop
objFileTextStream.Close
This method will return all of the text starting at the current location in the file up to the first line
break. When you have a file open for reading, the TextStream object maintains a pointer in the
file indicating where you last read text. When you first open the file, this pointer is pointing at the
beginning of the file. When you read text from the file using either the Read or ReadLine
methods, the object moves this pointer to a point just after where you finished reading. In the
case of the ReadLine method, the pointer will be pointing at the first character after the line
break.
As you are reading the file piece by piece, you will need some indication as to when you have run
out of file to read. The TextStream object provides a property that is very simple to access,
which will tell you when you are at the end of the file. The AtEndOfStream property will return
True when you have reached the end of the file.
As we want to read the file one line at a time, we use the ReadLine method. Since we want to
read the entire file, we will need to call the ReadLine method over and over again until we have
read the entire file. This can be easily done using a loop. The condition that we are checking
every time we go through the loop is based on the AtEndOfStream property. As long as this
property's value is not true, we still have more data to read, so we can grab the next chunk using
the ReadLine method.
Once we have the line stored in a string, there is one more procedure that we need to perform on
it. Since we will be displaying primarily the source code for Active Server Pages files, we need to
take care of how special characters in the file are handled.
If we were to just spit the contents of the file back to the client using a Response.Write
method, we will probably not get the desired results. Since a large part of an ASP source file is
HTML, when this is sent back to the client, it will just display it as if it were regular HTML. This will
not give us the display that we are interested in.
In order to display the HTML as source, we need to change the way the HTML tags are
presented. When the client is displaying an HTML page, it is looking for HTML tags that are
bounded by < >. When it finds one of these, it will use it as formatting instructions, and not as text
to be displayed. In order for the browser to display the HTML source as text, we need to change
the < > to a character that will look the same, but will be interpreted differently.
To do this, we need to look through the string and wherever we find a < or > we need to replace
it with a < or > respectively. When the client sees this set of characters, it will display a <,
but it will not treat what is inside as an HTML tag. This is exactly what we need for our source
code viewer. Lucky for us, there is a very convenient Server object method called HTMLEncode.
It does exactly what we need. Given a string, it will search for characters that would be
interpreted as HTML by a browser, when we really want it to display as text.
The last bit of formatting that we need to do concerns the area of indented text. Many ASP source
files use indented sections to make the code more readable. These indents are usually made
using tab characters. Unfortunately, when a web client is told to display a tab character, it will just
ignore it. We want our source code display to retain the formatting that the developer added.
Again, we will be using the Replace method. This time we will be looking for a tab character.
The tab character is one of those non-printable characters. This means that there is no visible
character that represents it. But we need a tab character as input to the Replace method.
VBScript also provides us with a Chr function. This function will convert a number into its
equivalent ASCII character. After consulting our ASCII character chart, we know that the ASCII
code for the Tab character is 9. So we will be using Chr(9) as our search sub-string in the
Replace method. We will be replacing each of the tab characters with four non-breaking space
characters. These are represented as and when the client encounters one of these, it
adds an explicit space character to the output.
Now that the whole line is properly formatted, we can output it to the client using the
Response.Write method. Since the client will ignore any carriage returns in the text file, we
need to add our own line break to the displayed source code. To do this, we add the <BR> tag to
the end of the line. Having finished all of the processing on this line, we start our loop all over
again with the next line. When we run out of lines to process, we close the TextStream and
send the completed page back to the client.
In order to launch this page, we need to add a few lines of code to an existing ASP file. We will be
using one of the examples from earlier in this chapter. At the bottom of the page, we will add a
hyperlink that will request the DisplaySource.asp file. The name of the file that we are
displaying the source for is passed as a URL parameter. Before we pass the file name as a query
string variable, we need to make sure that all of the characters in the file name are valid URL
characters. The easiest way to do this is to use the Server.URLEncode method. This method
will replace any invalid characters in a URL string with their corresponding URL representations.
This will ensure that the file name is properly passed to the DisplaySource.asp file,
regardless of the characters in the file name. So now, when the user clicks on this link, the source
code of the ASP file that generated the page will be displayed for the user.
This example has shown us how to read information from a text file on the server and display it
on the client. Next, we will take a look at how to write information to a text file. There are many
instances in web applications where you want more detail than is available in the standard server
logs, yet you don't want to have a database to write the information to. This example will show
how to create an application log file routine that can be packaged into a server-side include and
dropped into any ASP file.
In this example, we will be creating a server-side include file that can be added to any of your
Active Server Pages script files. This file will provide your script with a method that can be called
to write information to a log file. All that your script will need to do is add this include file and then
call the method to write the information to the file.
1. Using NotePad, create the WriteLog.asp file with the following source code:
2. <%
3. Const ForAppending = 8
4.
5. Dim strLogFileName
6. strLogFileName = "c:\AppLogFile.log" 'enter your
preferred path here
7.
8. Dim objLogFileFSO
9. set objLogFileFSO = CreateObject("Scripting.FileSystemObject")
10.
11. Dim objLogFileTS
12. If objLogFileFSO.FileExists(strLogFileName) Then
13. Set objLogFileTS = objLogFileFSO.OpenTextFile(strLogFileName,
ForAppending)
14. Else
15. Set objLogFileTS =
objLogFileFSO.CreateTextFile(strLogFileName)
16. End If
17.
18. Sub WriteToLog (strNewEntry)
19. Dim strLogEntry
20.
21. strLogEntry = FormatDateTime(Now) & " - "
22. strLogEntry = strLogEntry & strNewEntry
23. objLogFileTS.WriteLine strLogEntry
24. End Sub
25.
26. Sub CloseLog()
27. objLogFileTS.Close
28. End Sub
29. %>
30. Save this file in your BegASPFiles folder.
31. Next, add the include statement to your ASP file. For this example, we will be using the
DisplayDirectory.asp file that we looked at earlier.
32. set objFolderContents = objFolder.Files
33. %>
34. <!-- #include file="WriteLog.asp" -->
35.
<TABLE cellpadding=5>
36. We'll add another line to DisplayDirectory.asp which will call the WriteToLog
method to write information to the log file.
37. Response.Write objFileItem.DateLastModified
38. Response.Write "</TD></TR>"
39. WriteToLog "Directory Entry for " + objFileItem.Name
40. Next
%>
41. At the end of DisplayDirectory.asp, add a call to the CloseLog method to close
the log file.
42. Response.Write objFileItem.DateLastModified
43. Response.Write "</TD></TR>"
44. WriteToLog "Directory Entry for " + objFileItem.Name
45. Next
46. CloseLog
%>
47. View the DisplayDirectory.asp file in your web browser. This will cause the server
to generate the page, which will write the information to the log file. The contents of the log
file, which will be called AppLogFile.log, can be found under the C:\ folder on your
web server's hard drive and can be viewed using any editor. In this example, we're using
Notepad. The contents will look something like this:
How It Works
This server-side include file will need to perform two functions. First, it will need to get the
TextStream object that represents our log file properly prepared to accept information written to
it. Secondly, it will need to provide an easy mechanism for the file that includes it to write
information to the log file.
In our example, we have defined the name of the log file. This could have just as easily been
stored in a variable and retrieved by the include file. Once we have this file name, which is
already a physical file name, we can then prepare the TextStream object to accept information
written to it.
Dim objLogFileTS
If objLogFileFSO.FileExists(strLogFileName) Then
Set objLogFileTS = objLogFileFSO.OpenTextFile(strLogFileName,
ForAppending)
Else
Set objLogFileTS = objLogFileFSO.CreateTextFile(strLogFileName)
End If
There are two states that we need to work with when preparing the TextStream object. The first
state is when there is no log file present. In this case, we need to create the log file and then open
it with write permissions enabled. To check to see if the file is present, we will be using the
FileExists method of the FileSystemObject object. This method takes the name of a
physical file and returns true if the file exists and false if it does not.
If this method returns True, then we know that our log file exists (although it could possibly be
empty) and all we need to do is open it. To open it, we will use the OpenTextFile method of the
FileSystemObject object. This method will return a TextStream object that represents the
file. There are two parameters that we will pass to this method. The first is the name of the
physical log file. This is stored in the strLogFileName variable. The second parameter is used
to tell the FileSystemObject object what we want to do with this file once we have opened it.
There are two possible values for this parameter. If we just want to read information from the file,
then we can set this parameter's value to ForReading. This value will cause the file to be
opened as read-only, meaning we can only read information from the file. Since our task here is
to create a file that we can write information to, we need to supply the other value for the
parameter. The value of ForAppending means that we will be able to both write information to
the end of the file. To define the actual value for this parameter, we have included a Const
statement to set the value of ForAppending to 8.
If the FileExists method returns False, then we know that we have to create the log file so that
we can write to it. This is done using the CreateTextFile method of the FileSystemObject.
For this method, we will be supplying one parameter. We will pass in the physical name of the file
that we want to create. This method will return a TextStream object that represents our new
physical file. This TextStream object will be set up so that information can be written to the file.
The steps involved in opening or creating the file are the first steps in our server side include file.
These steps need to be executed as soon as the server reads them from the source file. The
other step in the log file is writing information to the log file itself. This needs to be able to be
called from anywhere in the source file. In order to do this, we have created a subroutine.
Sub WriteToLog (strNewEntry)
Dim strLogEntry
This subroutine is a feature of the server-side include file. It will accept one parameter and then
perform some processing. The parameter will be the text information that is written to the log file.
When the method receives this information, it will add a date and time stamp to the beginning of
it, then writes the information to the log file. To write the information to the log file, we will be
using the WriteLine method of the TextStream object. This method will add the text contained
in its one parameter to the file associated with the instance of the TextStream object. It will also
add a line break at the end of the text that it adds to the file.
Now that we have created our server-side include file, we need to add the logging functionality to
another ASP file. For this example, we will be adding it to the DisplayDirectory.asp file that
we looked at earlier in this chapter.
set objFolderContents = objFolder.Files
%>
<!-- #include file="WriteLog.asp" -->
There are three steps to adding the logging capabilities to this file. First, we will need to include
the WriteLog.asp file that we just created. This will be done using the #include directive in
the DisplayDirectory.asp file. This statement needs to be included prior to any calls to the
WriteLog method, so that the method can be properly defined before it is called.
Response.Write objFileItem.DateLastModified
Response.Write "</TD></TR>"
WriteToLog "Directory Entry for " + objFileItem.Name
Next
%>
The next step will be to call the WriteLog method whenever we want to add information to the
log file. For this example, we will be writing the name of each file that is read in the current
directory to the log file. When you view the log file in Notepad, you can see that the date and time
that the entry was made has been added to the beginning of each entry in the log.
Response.Write objFileItem.DateLastModified
Response.Write "</TD></TR>"
WriteToLog "Directory Entry for " + objFileItem.Name
Next
CloseLog
%>
The final step is to call the CloseLog subroutine, which will close the log file on the server.
While logging this type of information may not be very useful on a production basis, because the
volume of information being written is just too large, and the problems involved with several
sessions trying to write to the same log file at once. There are a number of occasions where a
method like this can come in useful. This method can be used to output information to a log file as
you are developing a page. If a problem occurs, it is very easy to go back to the log file to see
what processing actually took place, and thereby determine what the error was.
The RegExp Object
The Regular Expression object, to give it its full name, is new in VBScript 5.0 and adds to the
language something JavaScript developers have enjoyed for ages. But what is a regular
expression, and why have VBScript developers been missing out on this important feature?
Rather than describe what it is straightaway in rather abstract terms, I'm going to recount a short
story of my juvenile delinquent days, which will help you visualize why regular expressions might
be useful. Back to my Computer Science classes, when we were starry-eyed by the stories of
hackers breaking into the Pentagon files with little more than a Commodore Vic20, we were
convinced that we could break into anything too – well at least our colleagues' accounts, to check
to see whether their coursework answers tallied with ours. Our coursework was stored on a UNIX
system, and everybody kept theirs under a user account and password.
Now, one week we learned about the UNIX command GREP (it stands for Get Regular
ExPression) and how it is used to look for a sequence of characters. Rumor quickly passed that if
you got onto the system and forced it to make a dump of everything it had in its memory currently
(known as a core dump), then you would come across the various user accounts logged on at the
time together with the unencrypted passwords. As these core dumps would often amount to
thousands of pages it just wasn't feasible to check every page of garbled information by hand, for
these few nuggets, but the GREP command could be used to sift through the information. As
everybody's account in our class started with the prefix dh1 followed by another two numbers, it
was possible to set the GREP command to search for this pattern of letters along with another 2
characters after to locate the errant passwords. Not that we ever put them to use, of course.
So a regular expression is a sequence of characters, both known and unknown. You replace the
unknown characters with wildcards. The regular expression forms what is known as a search
string. The search string is what the object will attempt to locate within the data. The RegExp
object provides a set of properties and methods for manipulating this search string.
RegExp Properties
The RegExp object has the following three properties:
Global: used to indicate whether every occurrence of the search string should be
matched, or just the first one VBScript comes across
IgnoreCase: used to indicate whether or not the case should be taken into account
when trying to match a search string
Pattern: used to set (or return) the sequence being searched for
The first two properties mentioned both taken Boolean values and so they can only be set to true
or false. Consider the following:
objRegExp.Pattern = "Hello Hello"
objRegExp.IgnoreCase = False
objRegExp.Global = True
In this example we are searching for the pattern "Hello Hello". Because we have set Global
to True, we are saying that we want every occurrence of this sequence that is matched to be
returned. By setting IgnoreCase to false then it will take account of the case of the text to be
matched. So for example "hello hello" or "Hello hello" would both be ignored by the
search.
The regular expression is compared to the text we want to search character by character. If the
RegExp object finds a match for the first character, it then looks to see if the following character
matches the next character in the regular expression, and if so, it checks the next, and so on. The
clever part is that we can tell the RegExp object to match one of a specific set of letters, or any
letter, or a sequence of repeated letters. We do this using wildcards and special characters, and
we'll take a look at those most commonly used now:
Character Purpose
"bed" or "bud".
[^xyz] Matches any of the character NOT contained in the set, so "[^sb]et" would
match "get", but wouldn't match "set" or "bet".
[a-z] Matches any character within the range specified. "[a-z]{3}", for example, will
match any three letter word.
^ Matches the start of a line, for example "^a" would match any line beginning
with the letter "a".
$ Matches the end of a line, for example "e$" would match any line ending with
the letter "e".
\ This is used to allow us to match characters which otherwise have a special
meaning in regular expressions, or which we can't type. for example, "\*" is
used to match an asterisk character, and "\t" matches a tab. "\\" is used to
match a backslash character.
Let's take a string and look at whether some regular expressions will return a match.
The quick brown fox jumps over the lazy dog
First, remember that unless we specify the character "^" or "$", we'll get a match for our
expression if it occurs anywhere within the string. So, the following regular expressions will all
match:
quick
jumps
ver th
row
^The will match, since this word occurs at the start of the string, but ^lazy won't match.
The expression [a-z]+ can be used to match any word consisting of more than one letter (it
matches one or more occurrences of a character between a and z). We could use that as follows:
The quick [a-z]+ fox jumps over the [a-z]+ dog
This will match our string, but it will also match different strings with different colored foxes and
differently tempered dogs. Why? The RegExp object comes to the b in brown, and sees it is
allowed, because it's in the range a to z. Likewise the r, the o, the w, and the n. When it comes
across the next character, a space, it sees it is no longer allowed by the [a-z] expression, but
that the next character is a space, so it carries on matching the literal characters we specified.
Here are some useful regular expression shorthands you might use:
Sequence Purpose
RegExp Methods
The RegExp object has three methods:
Replace: replaces text found within the search
Test: executes a search and returns a Boolean True or False value indicating whether
the string was matched anywhere
Execute: the method used to actually carry out the search, once the Pattern property
has been specified. Execute is better than Test because it can return a number of matches
rather than just the simple yes/no answer that Test returns indicating whether a match was
found.
Replace is used where you supply the string you wish to search, followed by the new text you
wish to insert. You must have previously defined the text to be replaced in the Pattern property.
So if there had been computers available in medieval England, you might have found yourself
charged with the following task:
objRegExp.Pattern = "Richard III"
objRegExp.Replace(strRoyalDocument, "Henry VII")
This acts just like a find and replace does in Word or the Replace function in VBScript does, the
first string is the string to find, and the second is the one to replace. Simple!
The next method is only slightly more complex. Test takes one parameter – that of the original
text or string being searched. If the Pattern property has already been set then it returns a True
or False value. This value can then be used to clarify whether the search item was found or not,
and then take appropriate action depending on the outcome:
strSearch = "abcdefghijklmnopqrstestuvwyz"
objRegExp.Pattern = "test"
blnFind = objRegExp.Test(strSearch)
If blnFind Then
Response.Write "Pattern matched"
Else
Response.Write "Pattern not matched"
End If
In this example, we'd just have one match of the pattern 'test'. Consider what might happen if we
were searching for the letter 't' as our pattern instead. In which case there would be two matches.
How would this be dealt with? The Execute method proves more complex than you might
imagine. We've already hinted that you can return more than one match in a search, so what
happens when you make more than one match in a search; how are details of them stored?
The Match Object
For each match made in the search string, a separate read-only Match object is created. Each
Match object is stored in a Matches collection. The Match object itself has three properties.
These are:
FirstIndex: Returns a numerical value indicating where in the search string a match
was found, as an offset from the first character. So a value of one would indicate that a
match beginning at the second character.
Length: The length of the match found within the search string
Value: Returns the matching text or value found within the search string
So every time a match is made, you can use these properties to return the position and length of
the match. If you go back to our previous example and wished to iterate through the Matches
collection you could do the following:
set objRegExp = New RegExp
strSearch = "abcdefghijklmnopqrstestuvwyz"
objRegExp.Pattern = "t"
objRegExp.Global = True 'So search won't just return the first match
only
Set Matches = objRegExp.Execute(strSearch)
This is a relatively trivial example, so now we've introduced all of the requisite properties and
methods, let's look at a more detailed example.
We're not really doing anything more than we've done in the previous explanations. We start by
creating an instance of the RegExp object
Set objRegExp = New RegExp
We set the Pattern property to equal "force" as that's the word we're going to replace with "pants".
We also set the Global property to equal true, as we don't just want to replace one occurrence,
but every occurrence.
objRegExp.Pattern = "Force"
objRegExp.Global = True
At the beginning of the ASP we've added the include file textfile.txt which contains our
target text. However, cunningly, we've surrounded the text with the ASP declaration strSearch
= so that our whole text is now kept in string format. So in the next line we can start our search in
the Matches collection, using strSearch as our target search string:
Set Matches = objRegExp.Execute(strSearch)
We then create a loop to iterate through each occurrence of the Match object and display the text
it contains and the position it was found at:
For Each Match in Matches
Response.Write "Text " & Match.Value & " was found at position "
Response.Write Match.FirstIndex &"<BR>"
Next
Finally we go back to our text and replace whatever is held in the Pattern property with the word
"Pants" and display it using Response.Write.
strReplace = objRegExp.Replace(strSearch,"Pants")
Response.Write "With the text replaced, the script now reads:<BR>"
Response.Write (strReplace)
By using these objects, you can extend the reach of your web applications beyond what exists
inside of the six built-in ASP objects. They allow you to categorize and store information more
efficiently, as well as manipulate the physical file system of the server itself.
In the next chapter, we will look at how we can extend ASP's functionality further by using a set of
built-in components provided with ASP, and also how we'd go about installing ones available from
third party sources.
Chapter 11: Active Server Pages Components
Overview
Most web site designers have similar basic goals for their site, including:
A technique to navigate through their web site
A way to manage advertisements on the site
Molding pages to accommodate the capabilities of the visitor's browser
It would be inefficient for all web masters to spend time writing the code to achieve these
common goals. Far better, then, for a single team of experts to develop, troubleshoot and
optimize the code, then make it available for everyone. Microsoft has done just this with a set of
tools that automate these common tasks, called Installable Components for ASP. In this
chapter we will cover the general concept, then look at four components:
The Server Object and how to use Server Components – the basics of the techniques.
The Advertisement Rotator Component – A tool used to display advertisements on your
site.
The Content Linker – A tool to allow the website manager to link together a series of
pages, for example to create a tour of the site or to step through the pages of a tutorial.
The Browser Capabilities Component – a tool to determine what features, like tables,
scripting and frames, your visitor's browser supports. With that knowledge you can build
pages that take advantage of every feature available, but do not rely on unsupported
functions.
The Server Object
The tools that Microsoft provides with ASP are organized into objects, as we covered in Chapter
6. To review; the software designer (in this case, Microsoft) creates an object which can hold
and/or provide information (properties) and can carry out certain tasks (methods). ASP has
seven objects: the Server, Application, Request, Response, Session, Error, and
ObjectContext objects.
The Server object allows you to create components, which are pre-packaged software objects
that Microsoft and others have created to provide commonly needed functions. As you move
further into ASP, you will probably create your own components as well – this is introduced in
chapter 16. Microsoft has provided about a dozen components that contain the ability to add
powerful features to your site. We will look at three of these components in this chapter.
Note The rest of these Microsoft components are covered in the ASP 3
Programmer's Reference (Wrox Press – ISBN 1861003234).
Many students ask why some capabilities are encapsulated in components, whereas others (like
reading information from a querystring) are not. MS put a reflection of the information defined in
the HTTP standards into the intrinsic ASP objects. These are the most basic and frequently used
functions, such as reading from a request and writing to the nascent page. Then, the MS
designers built additional goal-oriented features into components. With this split, the core
functions are always loaded and quickly available, whereas less-universal tasks are loaded in
memory only as needed. Furthermore, Microsoft tries to focus on developing the core of each
software product and then allows other companies to build accessories to enhance that core. In
ASP, this means that Microsoft includes a few rudimentary components, but encourages the
development of additional components from third party vendors.
To summarize, the more basic and closely matched a function is to the HTTP standards, the
more likely it is contained in one of the seven intrinsic ASP objects. The more specialized a
function, the more likely it is to be available in a component; either from Microsoft, a third party, or
custom developed from your own programming team.
You'll recall our discussion of the relationship between objects and instances from Chapter 5.
Essentially, the component (sometimes a class or type) is the template; and an instance is a
particular copy that is created in the shape of that template. This action is similar to cookies
coming out of a cookie cutter. The act of creating an object from a component is called
instantiation.
In ASP, we first Dim a variable to hold a reference to the object. We then create an instance of
the component with the Server.CreateObject method. This returns a reference to that object,
which is assigned to our variable using the Set statement. The instance must be assigned to a
variable name – this allows you to refer to the instance later on in your code. If we go by the
naming convention outlines that we discussed previously, these are prefixed with obj before an
abbreviation of the name of the component. For example, if you were using an instance of the
Next Linker, then you might want to give it a variable name like objNextLink or objNL. Since
the instance of your object is held in a variable, it should also be named in a similar manner. You
may recall that we discussed naming conventions and rules in Chapter 4.
We Dim the variable that will hold the instance of our object in the normal manner – avoiding
special characters other then the underscore, starting with a letter and preferably starting with the
obj abbreviation to designate an object. Then we use the Set statement, followed by the name
that we have chosen for our instance. On the right of the equal sign is the code that we use to
create an instance of the Server object's NextLink component. To give you the proper
vocabulary, we are instantiating from a ("Library.Class"), or ("Vendor.Component").
(Class can sometimes be called ObjectType.) All of the Microsoft components that ship with
ASP are in the Library "MSWC", standing for the Microsoft Web Class. Components from third
parties or from other Microsoft software will have other class names.
Many students ask how to find out more about a given component. You can view all of the
properties and methods of a class by using the Object Browser that comes with Microsoft Visual
Basic and also with the VBA editors in Word, Excel and Access. (Of course, it must be a machine
with IIS installed.) The techniques for VB and VBA Editors are a bit different; so pick the
appropriate paragraph below.
To use the Object Browser in VB, you open a new project and click through
Menu:Project/References, scroll down to MSWC (after "Microsoft") and you will see the ASP
components listed, for example MSWC Advertisement Rotator Object Library and MSWC Content
Linking Object Library. Click the corresponding checkboxes (to the left) to include those of interest,
then close the References dialog box. Now click through Menu:View/ObjectBrowser or strike F2.
To use the Object Browser in one of the VBA Editors you start the application (Word, Excel or
Access), then strike Alt+F11 to open the VBA editor. Click through Menu:Tools/References, scroll
down to MSWC (after "Microsoft") and you will see the ASP components listed, for example
MSWC Advertisement Rotator Object Library and MSWC Content Linking Object Library. Click the
corresponding checkboxes (to the left) to include those of interest, then close the References
dialog box. Now you can open the object browser by Menu:View/ObjectBrowser or striking F2.
Once the Object Browser is open, drop down the Project/Library list in the top left corner. Click on
the component of interest and you can see its methods and properties in the pane to the right.
Properties have an icon with a hand pointing to a list, methods have a flying green box and
Events have a lightening bolt. By single clicking on a feature on the right you can get a short
description and syntax guide in the gray pane below.
Now, we'll look at the specific techniques of using three components: the Ad Rotator, Content
Linking, and Browser Capability
The Ad Rotator Component
To date, one of the most common sources of revenue on the World Wide Web has been the sale
of advertising space. Although the Ad Rotator is not limited to Banner ads, in this book we will
concentrate on this standard format of a .GIF file: 440 pixels wide by 60 pixels high. Since
viewers will pay little attention to the same ad again and again, site designers want to rotate a
collection of ads in the hope of giving each visit a fresh and inviting look. Furthermore, many sites
charge advertisers by the number of hits or impressions (both mean the number of times an ad is
shown to viewers) or the number of click-throughs that you track for invoicing. ASP includes a
component, the Ad Rotator, which performs and manages the task of presenting ads in rotation
as pages are requested.
The Ad Rotator component utilizes the following four files. The first two you make specifically to
implement the Ad Rotator. The third is an existing page in your site, upon which you want to
display ads. The fourth, the Targets, are provided by and hosted at the site of the advertiser.
File Purpose Type File Name in the Try-It-Out
When a visitor requests the display page, the ad rotator will calculate which ad to show next,
gather the information for that ad from the scheduler.txt and pass it to the display page. The
ad will appear on the display page, along with the rest of the text and graphics of the page. If the
visitor clicks on the ad then the redirector page is opened, with the URL for the ad's target page
passed along in the querystring. Usually the redirector page contains no viewable HTML; rather it
has code that updates click-through statistics and then immediately redirects the user to the
advertiser's site.
The click-through statistics can be sent to a datastore or held in application or session level
variables. Our exercise will use application variables created in global.asa and we will create a
small page called AdRotatorViewHits.asp to check on the number of times visitors have
clicked on each ad. So, we will also work with the following two files.
Let's take a closer look at the structure of the scheduler file before we try out the ad rotator. The
schedule keeps track of which ads should be displayed, how frequently to display them, the
advertiser's URL where the user will be sent, and an alternate text to display. Create your
scheduler file as a simple ASCII text file with two sections. The first section sets the general
parameters for displaying all ads and ends with an asterisk. The second section sets specific
parameters for each ad in the rotation.
In the first section, there are four general parameters that can be set to apply to all ads in the file.
If you leave any of these parameters out, then ASP will provide the default shown in parenthesis.
The name of the file that is used to redirect the user when he/she clicks on the ads, as
listed in row one of the table below (No default)
The width of the ad as it will appear on the page (460)
The height of the ad as it will appear on the page (65)
The width of the borders of the ad as it will appear on the page (1)
Remember that this section must end with a single line containing an asterisk, and nothing else
on that line. In the following example schedule file, we will depart from the normal ad size to
illustrate changes from the defaults.
Redirect AdRotatorRedirector.asp
Width 460
Height 65
Border 1
*
wroxconferences.gif
http://www.WroxConferences.com
WroxConferences - Held in America, Europe and Asia
20
activepath.gif
-
Activepath provides textbooks and teaching materials for software
training
10
wroxpress.gif
http://www.wrox.com
Wrox Press, Programmer to Programmer
50
In the second section of the above file, we write four lines for each ad in the rotation. Since there
is no marker dividing the entries, the parser counts off every four lines as a group. Therefore, use
no more and no less then four lines per ad and do not revise the order of information. Note that
we do not type double quotes around any of the parameters, in either of the two sections of the
scheduler file. The four lines used for each ad represent the following:
The name of the file that holds the ad graphic (for example, WroxConferences.gif).
The URL of the hyperlink to be sent to the Redirector file if the user clicks on the ad. If no
hyperlink is required – that is, the user's click causes no action – then we use a single
hyphen (with no spaces) in place of an URL.
Text description of the ad graphic for text-only browsers.
The frequency with which the ad should be displayed.
Calculate the frequency of display for an ad by dividing its frequency parameter by the total of all
the frequency parameters. So, in the sample below, the total of the frequencies is 20+10+50 =
80. The WroxConferences ad will run 20/80 or 25% of the time. (Remember that a number is not
a percentage unless the total comes to 100.)
We'll create a page that gives a tip for programming students. Each time the page is viewed, it will
also display one of three ads.
1. Begin by downloading three sample ad files. In the real world, your advertisers would
send these to you, but for this exercise, we will download WroxConferences.gif,
ActivePath.gif and WroxPress.gif from the Wrox Press web site at
http://www.wrox.com/, and save them into your BegASPFiles directory.
Note At the same time, you can also download all of the files that we will be
creating in this chapter from the website.
2. Next, we set up the scheduler file. Either download AdRotatorSchedule.txt from the
Wrox site or open up your editor of choice and type the following lines. Save this as
AdRotatorSchedule.txt in your BegASPFiles directory.
3. Redirect AdRotatorRedirector.asp
4. Width 460
5. Height 65
6. Border 1
7. *
8. wroxconferences.gif
9. http://www.WroxConferences.com
10. WroxConferences - Held in America, Europe and Asia
11. 20
12. activepath.gif
13. -
14. Activepath provides textbooks and teaching materials for software
training
15. 10
16. wroxpress.gif
17. http://www.wrox.com
18. Wrox Press, Programmer to Programmer
19. 50
20. Now, we need a place to hold the number of clicks on each ad. In this exercise, we will
do that in an application variable. Add the following shaded lines to your global.asa.
More sophisticated editors will create a global.asa for you and you will just have to add
the shaded lines below. If you do not already have an .asa, then type all of the lines
below into a simple text file and name it global.asa in your BegASPFiles directory.
21. Sub Application_OnStart
22. Application("iWroxPress") = 0
23. Application("iWroxConf") = 0
24. End Sub
</SCRIPT>
25. Next, we create the file to handle a user's click on an ad. Open your editor, type the
following code, and save as AdRotatorRedirector.asp in your BegASPFiles
directory.
26. <%
27. ' This page handles logging of clicks, then redirects
28.
29. strURL = Request.Querystring("url")
30.
31. Select Case lcase(strURL)
32. Case "http://www.wrox.com"
33. Application.Lock
34. Application("iWroxPress") = Application("iWroxPress") + 1
35. Application.Unlock
36. Case "http://www.wroxconferences.com"
37. Application.Lock
38. Application("iWroxConf") = Application("iWroxConf") + 1
39. Application.Unlock
40. End Select
41.
42. Response.Redirect strURL
43. %>
44. Lastly, we can create a page that will display ads using the Ad Rotator Component and
the above files. Clear your editor, type the following code, and save as
AdRotatorHomePage.asp, again in the BegASPFiles directory.
45. <HTML>
46. <HEAD>
47. <TITLE>Ad_Rotator_Home_Page</TITLE>
48. </HEAD>
49. <BODY>
50.
51. <H3>Ad rotator home page</H3>
52. Programmer's tip:<BR>
53. When writing a loop always double check which commands <BR>
54. should be inside the loop and which should be outside the
loop.<BR><BR>
55. <%
56. Dim objAR
57. Set objAR = Server.Createobject("MSWC.AdRotator")
58. Response.Write (objAR.GetAdvertisement("AdRotatorSchedule.txt"))
59. %>
60. </BODY>
61. </HTML>
62. That takes care of the pages for the visitor using the ad rotator. However, as
webmasters, we would like to know how many clicks our ads have accumulated. We will
now make a final page that displays the statistics. Clear your editor and type the following
code and save this code as AdRotatorViewHits.asp, again in the BegASPFiles
directory.
63. <HTML>
64. <HEAD>
65. <TITLE>Ad_Rotator_Hit_Viewer</TITLE>
66. </HEAD>
67. <BODY>
68. This page gives you feedback on number of click-throughs<BR>
69.
70. <TABLE Border = 1>
71. <TR>
72. <TD>WroxPress</TD>
73. <TD><%= Application("iWroxPress")%></TD>
74. </TR>
75. <TR>
76. <TD>WroxConferences</TD>
77. <TD><%= Application("iWroxConf")%></TD>
78. </TR>
79. </TABLE>
80.
81. </BODY>
82. </HTML>
83. We have now finished entering our code. However, before testing, close your browser
and then stop your application to reset the application variables back to zero. Open up the
MMC and select the Default Web Site. Click on the Action Menu, and then select Stop to
stop your web server. Wait a few seconds and click Start, then close the MMC.
84. Test your Ad Rotator by using your browser to look at http://my_server_name/
AdRotatorHomePage.asp. Try viewing the page a number of times by pressing the
Refresh/Reload button. You should get the text information (Programmer's tip) every time,
but a different ad appearing. Clicking on the ad should take you to the appropriate
advertiser.
After several clicks on ads, use the browser to open AdRotatorViewHits.asp to see the click-
through statistics.
How It Works
Next, we code the details of the three ads, ensuring that this section contains exactly four rows
for each ad. These rows contain the name of the ad image file, the hyperlink URL, a comment
that the browser associates with the graphic, and the relative frequency with which the ad should
be shown. Note that in this example, Active Path has asked us to show their ad but do not offer
click-throughs, therefore their URL is replaced with a hyphen. The frequencies have been set
based on selling eighths of the total volume of ad appearances. While Wrox Conferences bought
two eighths (20 out of a total of 80), Active Path bought one eighth only:
wroxconferences.gif
http://www.wroxconferences.com
WroxConferences - Held in America, Europe and Asia
20
activepath.gif
-
Activepath provides textbooks and teaching materials for software
training
10
wroxpress.gif
http://www.wrox.com
Wrox Press, Programmer to Programmer
50
The file to handle a user's click is, like Gaul, divided into three parts. Note that this page will
perform some processing and then send the user on his way to one of the advertiser's sites.
Therefore we do not need <HTML>, <HEADER> or any other tags for setting up a "real" page that
the user would view. Firstly, we grab the data named "url" from the querystring and store it in a
variable:
<%
' this page handles logging of clicks, then redirects
strURL = Request.Querystring("url")
Then we figure out which URL we have got using a Select Case. Depending on the match, we
update one or the other of the application variables. Just prior to changing the application variable
we lock the application and just after the change we unlock it. Since there are no click-throughs
from ActivePath (remember the hyphen in the Schedule.txt?) we do not need to include
ActivePath in the Select Case:
Select Case lcase(strURL)
Case "http://www.wrox.com"
Application.lock
Application("iWroxPress") = Application("iWroxPress") + 1
Application.unlock
Case "http://www.wroxconferences.com"
Application.lock
Application("iWroxConf") = Application("iWroxConf") + 1
Application.unlock
End Select
The AdRotatorHomePage.asp starts with the usual headings, and then displays the
Programmer's tip:
<HTML>
<HEAD>
<TITLE>Ad_Rotator_Home_Page</TITLE>
</HEAD>
<BODY>
<H3>Ad rotator home page</H3>
Programmer's tip:<BR>
When writing a loop always double check which commands <BR>
should be inside the loop and which should be outside the loop.<BR><BR>
The code that actually shows the ad is very concise. We Dim and Set an object of the
AdRotator class. Then we run its GetAdvertisement method and put that result onto the
page with a Response.Write:
<%
Dim objAR
Set objAR=Server.CreateObject("MSWC.AdRotator")
Response.Write (objAR.Getadvertisement("AdRotatorSchedule.txt"))
%>
</BODY>
</HTML>
Viewing the statistics with the AdRotatorViewHits.asp again relies on the techniques of
Application variables as covered in Chapter 8. We build a simple table and write the current value
of the application variables:
<HTML>
<HEAD>
<TITLE>Ad_Rotator_Hit_Viewer</TITLE>
</HEAD>
<BODY>
This page gives you feedback on number of click-throughs<BR>
<TABLE Border = 1>
<TR>
<TD>WroxPress</TD>
<TD><%= Application("iWroxPress")%></TD>
</TR>
<TR>
<TD>WroxConferences</TD>
<TD><%= Application("iWroxConf")%></TD>
</TR>
</TABLE>
</BODY>
Common Errors
If you had trouble with this example, here's a checklist of things that you might have missed:
Testing before all elements are in place; scheduler, ads and redirector
Redirector file does not do a Server.Execute or Server.Transfer to actually get
the user to the advertiser's site
Files are scattered across different folders without paths
Mismatches between variable names in global.asa, AdRotatorRedirector.asp
and AdRotatorViewHits.asp
Mismatch in file names between Schedule.txt and Redirector.asp
After reading the chapter on data access you may want to try making another
AdRotatorRedirector.asp, this time keeping the click counts in a datastore rather then an
application variable. At the simplest level you would set up a table named Ads with each record
being an advertisement, and having fields called ClickCounter and AdURL. Then instead of the
Select Case section you would use:
///// Store to database an increment
SQLtext = "UPDATE Ads"
SQLtext=SQLtext& " SET Ads.ClickCounter = Ads.ClickCounter+1
SQLtext=SQLtext& " WHERE Ads.AdURL='" & strURL & "');"
We'll be talking more about using datastores with ASP in Chapter 12.
The Content Linking Component
A well-designed web site will provide the visitor with tools and guidance to navigate the site. ASP
allows you to create one type of guidance tool by using the Content Linker, also called the
NextLink component. You can create a list of pages in a specific order as a path through the
site. Then on any given page in this path, the user can click on the Next or Previous buttons, and
display the appropriate page in the path. The Content Linker even allows you to display a
description of the next/previous pages. Additional features of the Content Linker allow you to
program sophisticated jumps or loops. For example, the Content Linker would enable the
following objectives:
Tutorial
Five-minute tour
Review of new products (we'll use this as the example in this chapter)
Multi-page forms
In this section of the chapter, we'll start with a very simple example, which you can have a go at
yourself as we walk through the first Try-It-Out. Then we'll improve on the example in a step-by-
step manner (with Try-It-Outs for you) until you have a very sophisticated set of navigation tools.
Although most people refer to this component as the Content Linking or Content Linker
component, in IIS5 it is referred to as NextLink. For our purposes, the two terms will be used
interchangeably.
Using Content Linking
Over the next few pages, we will build several examples of increasing complexity, but let's start
here by looking at an example that doesn't even use ASP. Consider the HTML page shown
below, which displays information about the first item on our tour of new products – hat model
#501. We'll refer to this as CL-less-Hat501.htm, the CL for content linker. You can try making
this page, but it is not required for the other exercises of this chapter.
<HTML>
<HEAD>
<TITLE>Hat with no content linking</TITLE>
</HEAD>
<BODY>
<P>Hat of the Week:</P>
<P>Featuring an expanding elastic rim</P>
<A HREF="CL-Sweater.asp">Click here for next item</A>
</BODY>
</HTML>
At the top of the page, the user will see text that describes this hat; at the bottom of the page,
there is a hyperlink which, when clicked, takes the user to the next item on the tour of products (in
this case, the page describing sweater #304). In this context, what we're really interested in is the
site navigation technique. Here's the line that should jump to our attention:
<A HREF="CL-Sweater.asp">Click here for next item</A>
In this first case, the HREF to the next page is hard-coded: that is, it's typed in raw HTML. Note
that the target of the HREF attribute must be in double quotes. Provided you've already created
the target page CL_Sweater.asp, the user will have no problem with this.
However, when it is time for the web master to make changes to the order of the pages, he will
have to dig through every page, find each of these Next hyperlinks, and write in the new HREF.
It's a laborious task (believe me, I've done it); and it's also very confusing, since there is no stage
at which the web master ever sees the whole list of pages on the tour. This problem can be neatly
overcome by using the content linker which, in turn, uses the index file containing the full list of
the tour.
A note on the files used in the Try It Outs for Content Linking
We will be creating a site with four pages of clothing products which we want the visitor to tour.
Our focus in this section will be on the code to navigate from page to page. We will put all of the
code for navigation into a file called "CL-Navigation.asp" which will be included as a footer in
each product page. In this way we only have to make changes in one place and they will apply to
all pages. At the end we will add a table of contents page. So the files in the next table will be
created and modified. Note that each file name begins with CL- so that they stay together in an
alphabetical listing of files in your BegASPFiles directory. There is no other purpose to the
naming. Note that we will be making several improvements to the navigation footer file. I suggest
you keep the old ones so that you can review the simpler versions of the code. You can name
them CL-Navigation01, CL-Navigation02, etc., but your currently utilized file will always be
the one named simply CL-Navigation.asp.
File Purpose Type File Name in the Try-It-Out
How it Works
In the lines of the index file, we have created three columns separated by tabs:
URL column
Description column
Comments column
The URLs are the files that are contained in our tour of pages: we must list them in the order in
which we want them to appear in the tour. The contents of the Description column will be used
later to give information to the user. The contents of the Comments column will only be seen by
the site designers when they look at the index file. The Comments column is optional. You can add
various comment lines such as a title for the file, comments or a revision date.
ASP Pages that Use Content Linker to Hyperlink to the Next Page
Our first example will use the Content Linker simply to show us the next URL based on what ASP
finds in the index file. This example uses the NewProductsTour.txt file from the last Try-It-
Out.
How It Works
Let's look at the file CL_Hat.asp first. In fact, it looks very similar to the HTML file CL-Hat-
NoContentLinking.asp that we saw earlier in this chapter, when considering the benefits of
using Content Linker. However, instead of the hard-coded <A HREF> we have inserted the CL-
Navigation.asp file, which contains the code of interest:
<HTML>
<HEAD>
<TITLE>Hat</TITLE>
</HEAD>
<BODY>
<P>Hat of the Week:</P>
<P>Featuring an expanding elastic rim</P>
<!--#include file="CL-Navigation.asp"-->
</BODY>
</HTML>
Within CL-Navigation.asp we create a horizontal line so you can see where the navigation
area begins, then Dim a variable and an object. We use the Set statement to create an instance
of the NextLink component, named objNL:
<%
Response.Write "<HR>"
Dim MyPageNext
Dim objNL
Set objNL = Server.Createobject("MSWC.NextLink")
Then, we ask objNL to use a method called GetNextURL and leave VBScript:
MyPageNext = objNL.GetNextURL("CL-NewProductsTour.txt")
%>
In our original file, CL-less-Hat501.htm the HREF names the hyperlink target directly as CL-
Sweaters.asp, using the following line:
<A HREF="CL-Sweater.asp">Click here for next item</A>
This line is a little confusing, so let's step through it piece by piece. In our previous example, we
explicitly named the target file in our link. In this example, the URL of the target file (the next page
on our tour) is held in the variable MyPageNext. We are also still within our script block, so we
start with our Response.Write statement, and then the first part of our HTML code. However,
when using the <A HREF='target'> syntax, we need to enclose our target within quotes. This
is where things get a little tricky. When using Response.Write, any text or HTML is enclosed in
double quotes, so we have to use single quotes to enclose everything within the HREF. We use
the & character to join items within the Response.Write statement. We have now recreated our
HTML code using a slightly longer Response.Write statement. The result will be the same as if
we had typed the URL directly, but there is a huge difference: if we want to change the URL we
can do so in the Index file rather then on this page
If you view the source of the page, you will see the end result of our hard work - we have
produced a standard HTML link:
Beginners to ASP are frequently confused about the variables and object here. First, we must
supply two (later three) variable names. The first variable name identifies the instance of the
NextLink object – in this case, we used objNL. The second name is for the variable which will
hold the URL found by the objNL.GetNextURL method – here, I used MyPageNext. The third
variable will appear later to hold the URL of the previous page. You should also note that
although we get two pieces of data (next and previous URLs) we don't need to create two
Content Linker objects here. We have only created a single Content Linker object, namely
objNL, and then use two of its methods, namely GetNextURL and GetPreviousURL.
The other four files are the same as CL-Hat.asp in that they include the navigation page. The
Content Linker will take care of inserting the appropriate Next URL for whatever page is using
the Content Linker. Some folks get confused about which page we mean by 'current', 'next' and
'previous'. Recall that the ASP code is written within a page. That ASP code will be executed on
the server in the process of the server building a page to send to the browser. The server
considers the current page to be the one that it is now building. If your user clicks on next page
then that sends an <A HREF> for the next page to the server, and as that page is being built, its
URL becomes the current page for the ASP Content Linker methods finding the next and
previous pages.
Adding Descriptions and Previous to Our Link
When we created the index file, we saw that it was organized into three columns of data with a
row for each URL in the progression. The first column contains the name of the URL, the second
column contains a description, and the third column held comments about the page.
We can adapt the previous example, so that the user can click on the description rather than
Previous or Next. In fact, we will even embellish that description with some hard-coded text to give
us a dynamic hyperlink label. Hence, when the user clicks on the hyperlink text, they know
exactly what they are going to learn about in advance.
Furthermore, we can improve this tour to accommodate a viewer that might want to go back to a
product page. With a bit of copying and syntax "changing" we can add a Previous Page hyperlink.
Since all of the navigation code is contained in one file we do not have to edit the product pages.
We just modify CL-Navigation.asp.
I suggest that prior to these steps you save your old navigation file as CL-Navigation01.asp.
That way, if you ever need to review it you have it in its simplest form. Remember that the product
pages always include CL-Navigation.asp, so whichever version has that name will be the one
in effect. If you are downloading files from the Wrox website, the file below will be CL-
Navigator02.asp; rename it to CL-Navigation.asp.
1. With your editor of choice, open up CL-Navigation.asp, make the following changes,
and save the file with the same name.
2. <%
3. Response.Write "<HR>"
4. Dim MyPageNext
5. Dim MyPagePrevious
6. Dim MyDescriptNext
7. Dim MyDescriptPrevious
8. Dim objNL
9. Set objNL = Server.Createobject("MSWC.NextLink")
10. MyPageNext = objNL.GetNextURL("CL-NewProductsTour.txt")
11. MyPagePrevious = objNL.GetPreviousURL("CL-NewProductsTour.txt")
12. MyDescriptNext = objNL.GetNextDescription("CL-
NewProductsTour.txt")
13. MyDescriptPrevious = objNL.GetPreviousDescription("CL-
NewProductsTour.txt")
14.
15. Response.Write "Next: <A HREF='" & MyPageNext & "'>" &_
16. MyDescriptNext & "</A>   "
17. Response.Write "Back: <A HREF='" & MyPagePrevious & "'>" &_
18. MyDescriptPrevious & "</A>"
%>
19. Open up your browser of choice. At the address line, type in the URL
http://my_server_name/BegASP/CL-Hat.asp. Click on the hyperlinks to move
back and forward through the tour.
How It Works
In this example, we have added to the navigation file a few lines that utilize new methods – ones
that can get the description of a page and can get the information for the previous page. Note that
even though we have an additional method, we can still use the same instance of the NextLink
object. In other words, there's no need for a second CreateObject statement. Here's the
statement, for a given page, that calls the description of the next page in the tour:
MyDescriptNext = objNL.GetNextDescription("CL-NewProductsTour.txt")
The description is contained in the variable MyDescriptNext. Then we use this to make the
hyperlink even more complex, as follows:
Response.Write "Next: <A HREF='" & MyPageNext & "'>" &_
MyDescriptNext & "</A>   "
This looks similar to our previous link, but you should now notice that we use two variables within
our link. We start by building up the statement as we did before, using single quotes to enclose
the HREF and double quotes to enclose any HTML and text. We insert the target variable (which,
if you remember, holds the URL of the next item in our index) between the two HTML blocks that
end and begin with the single quote, enabling the variable to be used like a standard link. Instead
of inserting the text saying "Click here for next item", this time we call up the description of the
next item in the index with the MyDescriptNext variable. We then close off the tag and insert a
couple of spaces. Again, try viewing the source to our page in your browser, to see the HTML.
Note Keep in mind that the URL of the HREF needs double quotes, but the
hyperlinked text does not
We then do exactly the same style of procedure to produce a link to the previous item in our
index:
MyDescriptPrev = objNL.GetPreviousDescription("NewProductsTour.txt")
This line calls up the description of the previous page on the tour. We then use this variable to
insert the description of the file into the link:
Response.Write "Back: <A HREF='" & MyPagePrevious & "'>" &_
MyDescriptPrevious & "</A>"
The second improvement, adding the previous links is the exact same as the next link except that
we use the methods GetPreviousURL and GetPreviousDescription. Be careful not to
accidentally type GetPrevURL, a very common mistake.
Using the Content Linker and Control Structures to Display Only
Appropriate Links
By now your hyperlinks for touring the on-line clothier are efficient for several reasons:
The user is guided through the pages of the tour in the order of your design.
The viewer gets a description of the next and previous pages, which assist his or her
orientation.
However, there remain some rough edges. For example, the first page should not contain a
"Previous link" and the last should not contain a "Next Link." So we need to improve the
Navigation file so that it can sense if it is on the first page (don't print previous link) or on the last
page (don't print Next link).
In this example, we will perform a test to see if a next link exists, i.e. if there is a line following the
current line in the Index file. Only if there is a next link will we display the Next hyperlink on the
page. We will also test for a previous line to determine whether to print the previous link.
Again, you may want to save the existing version of CL-Navigation.asp as CL-
Navigation02.asp so you can review the exact syntax of the second content linker Try-It-Out.
If you are downloading files from Wrox, the file below will be CL-Navigator03.asp, rename it
to CL-Navigation.asp so that you don't need to change all of your links within each product
page.
1. Open the file CL_Navigation.asp and amend it to the following, then save.
2. <%
3. Response.Write "<HR>"
4. Dim MyPageNext
5. Dim MyPagePreviousious
6. Dim MyDescriptNext
7. Dim MyDescriptPrev
8. Dim MyListCount
9. Dim MyListIndex
10. Dim objNL
11. Set objNL = Server.Createobject("MSWC.NextLink")
12. MyPageNext = objNL.GetNextURL("CL-NewProductsTour.txt")
13. MyPagePrevious = objNL.GetPreviousURL("CL-NewProductsTour.txt")
14. MyDescriptNext = objNL.GetNextDescription("CL-
NewProductsTour.txt")
15. MyDescriptPrevious = objNL.GetPreviousDescription("CL-
NewProductsTour.txt")
16. MyListCount = objNL.GetListCount("CL-NewProductsTour.txt")
17. MyListIndex = objNL.GetListIndex("CL-NewProductsTour.txt")
18.
19. If MyListIndex > 1 Then
20. Response.Write "Back: <A HREF='" & MyPagePrevious & "'>" &_
21. MyDescriptPrevious & "</A> <BR>"
22. End If
23.
24. If MyListIndex < MyListCount Then
25. Response.Write "Next: <A HREF='" & MyPageNext & "'>" &_
26. MyDescriptNext & "</A> <BR>"
27. End If
%>
How It Works
The whole thing works because we are able to use our NextLink object to obtain the following
information from the index file:
The number of URL lines in the index file
The number of the URL that is currently shown
The first difference from previous CL-Navigation files is that we create two more variables:
Dim MyListCount
Dim MyListIndex
We fill the first using the method objNL.GetListCount which returns a number that ASP
obtains by counting the lines in the Index file:
MyListCount = objNL.GetListCount("CL-NewProductsTour.txt")
Note that the Count will be the same for all of the pages.
Then, objNL.GetListIndex returns the number of the URL line for the current page:
MyListIndex = objNL.GetListIndex("CL-NewProductsTour.txt")
For example, in the file CL-Tie.asp, this line looks up CL-Tie.asp in the index file, CL-
NewProductsTour.txt, and returns the value 3 (because CL-Tie.asp is the third on the list).
Armed with these values we can use logic to say that if we are not on the first page (that is,
MyListIndex is greater than 1), then we should show a Previous hyperlink:
If MyListIndex > 1 Then
Response.Write "Back: <A HREF='" & MyPagePrevious & "'>" &_
MyDescriptPrevious & "</A> <BR>"
End If
We can also say that, provided our current URL number is less than the total number of URLs
(that is, MyListIndex is less then MyListCount), then we should show a Next hyperlink:
If MyListIndex < MyListCount Then
Response.Write "Next: <A HREF='" & MyPageNext & "'>" &_
MyDescriptNext & "</A> <BR>"
End If
If the Webmaster changed the order of these pages in the index file the pages would
automatically compensate their Next/Previous appearance and descriptions, all from the magic of
the Content Linker.
We can then get the number of the last page, since we recognize that N is equal to the current
value of MyListCount. To use the GetNthURL method, we need to know how to tell the method
the number that we want. This transfer of information is done by a second parameter after the
name of the index file separated by a comma. For example, to get the URL of the first page we
use:
MyFirstPage = objNL.GetNthURL("NewProductsTour.txt", 1)
And to get the URL of the last page we use our a variable as the second parameter:
MyEndPage = objNL.GetNthURL("NewProductsTour.txt", MyListCount)
The differences between this file and our previous version start with the addition of two more
variables:
Dim MyFirstPage
Dim MyLastPage
We fill the first variable with a URL returned by the GetNthURL method, where we specify to use
the first page:
MyFirstPage = objNL.GetNthURL("CL-NewProductsTour.txt",1)
The second variable is filled with the URL returned when we ask for the last page in the index file.
Since at design time we don't know how many pages are in the file, we specify the number sitting
in the variable MyListCount.
MyLastPage = objNL.GetNthURL("CL-NewProductsTour.txt",MyListCount)
Now we can include a Home link if we are not on the first page already.
If MyListIndex > 1 Then
Response.Write "<A HREF='" & MyFirstPage & "'>Home</A><BR>"
Response.Write "Back: <A HREF='" & MyPagePrevious & "'>" &_
MyDescriptPrevious & "</A> <BR>"
End If
And we add an End link only in the cases where we are not already on the last page.
If MyListIndex < MyListCount Then
Response.Write "Next: <A HREF='" & MyPageNext & "'>" &_
MyDescriptNext & "</A> <BR>"
Response.Write "<A HREF='" & MyLastPage & "'>End</A><BR>"
End If
As long as we have the index file made up, what else could we do with it? Well, we could use that
information to make a hyperlinked table of contents for the tour of new products. We will create a
loop that reads the first URL and description out of the index file and writes it as a hyperlink on
the page, then puts in a <BR> (line break) tag, and then repeats these steps for each URL in the
index file. Since we can easily find out how many URL lines exist in the IndexFile with the
GetListCount, we will use the For…Next syntax here.
How It Works
The instance is called objNL. We then store the total number of URL lines in the index file, in the
variable MyLastCount:
MyLastCount = objNL.GetListCount("CL-NewProductsTour.txt")
Now we are ready to loop. Our loop will iterate once for each of the values between 1 and
MyLastCount inclusive:
For URLcounter = 1 To MyLastCount
Within each iteration of the loop, we give the value of URLcounter to the method getNthURL to
acquire the URL and description corresponding to the value of URLcounter. That data is
deposited in the two variables:
MyCurrentURL = objNL.GetNthURL("CL-NewProductsTour.txt", URLcounter)
MyCurrentDescription = objNL.GetNthDescription("CL-
NewProductsTour.txt", URLcounter)
Then, we use Response.Write to create hyperlinks for each page in the tour. Remember that
we are in the middle of ASP code here, not HTML. Therefore, we need to have ASP actually write
out the four characters"<BR>" to the page that HTML will send out to the browser. As you can
see, we do that using the Response.Write method:
Response.Write "<A HREF='" & MyCurrentURL & "'>"
Response.Write MyCurrentDescription & "</A>"
Response.Write "<BR>"
We mentioned in Chapter 7 that you could use a Server variable, HTTP_USER_AGENT, to help
detect which type of browser is viewing your page. However, life can be made much easier with
the Browser Capabilities component, which translates from the USER_AGENT, into a list of
features that a browser supports. Each feature can be tested for True (supported) or False (not
supported) before using the feature. You can create an instance of the Browser Capabilities
component as follows:
Set objBCap = Server.CreateObject("MSWC.BrowserType")
You can then use it by testing for True/False for support of dozens of features, for example:
If objBCap.Tables = "True" Then
' code that uses tables
Else
' code that avoids tables
End If
Recall that If...Then checks if the expression is "True". In the above code we wrote out a test,
but since the contents of objBCap will be either "True" or "False" we don't have to write an
equation. We can just use:
If objBCap.Tables Then
' code that uses tables
Else
' code that avoids tables
End If
All of the entries in Browscap.ini are optional, however it's important that we always include
the default section. If the browser in use doesn't match any in the Browscap.ini file, and no
default browser settings have been specified, all the properties are set to "UNKNOWN".
; we can add comments anywhere, prefixed by a semicolon like this
[IE 5.0]
browser=IE
Version=5.0
majorver=5
minorver=0
frames=True
tables=True
cookies=True
backgroundsounds=True
vbscript=True
javascript=True
javaapplets=True
ActiveXControls=True
Win16=False
beta=False
AK=False
SK=False
AOL=False
crawler=False
MSN=False
CDF=True
DHTML=True
XML=True
The brackets of the [HTTPUserAgentHeader] line define the start of a section for a particular
browser. Then each line defines a property that we want to inspect through the browser
capabilities component, and its value for this particular browser. The properties list depends on
the Browscap.ini, the above list for IE5 is about as complete as you will find. You can find
what properties are available by opening your browscap.ini in any text editor and searching
for the property of interest.
The Default section lists the properties and values that are used if the particular browser in use
isn't listed in its own section, or if it is listed but not all the properties are supplied. In more
sophisticated Browscap.ini files there are parent/child relationships so families of browsers
can inherit characteristics.
Having grasped how the Browscap.ini file can translate a User_Agent into
True/False/Unknown properties, it's time to actually see the Browser Capabilities component in
use. This example checks to see whether or not the browser supports VBScript, and displays the
appropriate message. This example can be modified to direct the user to different pages,
depending on the response given by the browser.
1. Open up your favorite editor, type in the following code and save this page as
BrowserCap.asp:
2. <% Option Explicit %>
3. <HTML>
4. <HEAD>
5. <TITLE> Browser Capabilities Component Example </TITLE>
6. </HEAD>
7. <BODY>
8. <%
9. Dim objBCap,blnVBScriptOK
10. Set objBCap = Server.CreateObject("MSWC.BrowserType")
11.
12. blnVBScriptOK = objBCap.VBScript 'save the value in a variable
13.
14. If blnVBScriptOK Then
15. Response.Write "This browser supports VBScript"
16. Else
17. Response.Write "This browser doesn't support VBScript"
18. End If
19. %>
20. </BODY>
21. </HTML>
22. Open up this page in Internet Explorer version 4.0 or higher:
23. Now view the same page in Netscape version 4.0 or higher:
How It Works
We first set up two variables for the object itself and a Boolean variable that will hold true or false
for the ability to support VBScripting:
Dim objBCap,blnVBScriptOK
Then we get the true/false property for VBScript and place it into the second variable:
blnVBScriptOK = objBCap.VBScript 'save the value in a variable
If the VBScript property holds the value True in browscap.ini, then we can assume that this
browser supports VBScript:
If blnVBScriptOK Then
Response.Write "This browser supports VBScript"
That's all there is to it. Of course, we can use the properties to do other things. One of the favorite
techniques is to load a different index page for a site, depending on what features the browser
supports. If our site has a set of pages using frames, and a different set using only simple text, we
can check the browser's ability to display frames when it first hits our site, and redirect it to the
appropriate index page.
Other Components
There are three sources of additional components. Microsoft has components beyond those
discussed in this chapter, and you can view them several ways. If you use Visual InterDev you
can click on Menu:View/Toolbox, then click on Server Objects. Alternatively if you use VB you can
create a project and click through Project/References/scroll to "MSWC…" and add them. They are
then viewable in the object browser.
A second source is to create your own. This topic is introduced later in this book, and covered in
depth in Professional ASP 3.0 (ISBN 1861002610), Peter Wright's Beginning VB6 Objects and
Rockford Lhotka's Visual Basic 6 Business Objects.
A third source is to investigate the third-party components that are for sale from such
sources as:
www.aspxtras.com
www.aspalliance.com/components
www.aspStudio.com
Summary
Web site developers that have preceded you have found that almost everyone shares a need for
some types of code, such as rotating ads. Microsoft has written code to perform these tasks and
made it available as Server Components. To use a component you must first create an instance
of the component using Server.CreateObject("MWSC.ComponentName"). Then you can
use that server with myComponent.method or myComponent.property.
The first component rotates advertisements on a page without making any changes directly on
the page. The number and source of ads can be changed in a scheduler file. In that same file, we
can also change the frequency of appearance for each ad.
To direct users through a set of pages on a site we can use the content linker component. Once
we make a list of pages, we can get the URL and description of the next and previous pages from
the content linker. A webmaster can easily change the order of pages, additions, and deletions in
one text file.
We finished with an examination of the browser capabilities component which relies on a file
which reads the browser type from the server variable, then sets a range of properties such as
the ability to use <TABLE> tags to either true or false. These properties can be tested, and the
user redirected to code which best fits his browser.
Keep in mind that many more components are available. Several sources review or sell these
components. As your skills increase, you will begin to write your own components to meet the
specific needs of your business.
Chapter 12: ASP and Data Store Access
Overview
We've reached an important juncture in this book. In the chapters up to and including Chapter 11,
everything we've discussed and coded has involved data stored in local variables within the ASP
page, or captured (for example, by an HTML form) and stored in one of the collections of the ASP
Request method. These information storage techniques are excellent for the purposes of
temporary data storage within an ASP page or application. But clearly, neither of these
techniques is going to be suitable for storing large amounts of information, or for storing data
indefinitely. Therefore, we need to look to other data storage methods.
Many companies use databases of one sort or another to store the data required in the creation
of their web pages, and also to store data that is entered by the end-users and captured by the
web pages. But it goes further than databases – there aren't many of us who store all of our data
on databases. Most companies have data sources of many other formats, such as mail systems,
legacy mainframes, spreadsheets and word-processed text files. Data stored in these formats
might also benefit from being published on the web – and so we'd like to be able to extend
database access techniques to these other forms of data.
Until recently, the idea of having a common method of data access – which could access the data
held in all these diverse applications and formats – was a far off dream. Microsoft uses the term
Universal Data Access (UDA) to refer to their strategy for dealing with this problem. As part of
its UDA strategy, Microsoft developed a technology known as OLE-DB, which has brought this
dream much closer to reality.
Moreover, this dream affects ASP – because we can use OLE-DB in ASP, via a set of
programming interfaces known as the ActiveX Data Objects, or ADO. Through the WWW,
intranets and other browser-based applications, the humble Web browser is becoming one of the
most widely-used interfaces between data and end-user.
The first 11 chapters of this book have demonstrated how to put active scripting into our ASP
pages; in the next few chapters, we'll look at how we can use this scripting capability to access
data stores and populate our pages dynamically with the most current, up-to-date information.
This is the real significance of the word 'Active' when we talk about 'Active Server Pages'. In this
chapter, we will begin our study of data access by concentrating on the data store and how to
connect to one. So we are going to look at:
What we mean by a data store
What ODBC is
What OLE-DB is
How ADO fits in
Exactly what a database connection is
How to use the ADO Connection object
Databases: Are they a Thing of the Past?
I guess some of you are hoping that databases, along with their seemingly cryptic methods of
holding your information in the rows and fields of various tables, are destined for the waste bin.
Their archaic and rigid structures and their esoteric terminology can be very confusing to the
novice. I mean: who honestly uses normalization in their day-to-day jobs?!
Databases aren't as bad as all that, once you start using them – and we won't be seeing the back
of database technology for quite some time, if ever. If you've never been initiated into the world of
databases, then let's do it now.
There's a field for my name, two fields for the first two lines of my address, a field for the city…
and so on.
Important In database-speak, a record is a collection of data containing information
from each field, relating to a specific entry. For example an Address Book
database takes the form of a table consisting of the columns (or fields)
"Name", "Address", "Phone Number". Then each row of the table is
referred to as a record.
Standardization of Data
How does the database ensure that each employee's record is stored in the same way? We don't
want a situation where one employee's name is stored across two fields while another's name is
stored in a single field; or where half the employees have their entire address packed into a single
field.
Databases standardize the way that information is stored. If you are obliged to put your first and
last name information into a single column – then everybody else who uses the database has to
adhere to the same template as well. So in this case, each employee record contains a single
Name field, in which the employee's full name is stored – no matter how many names they might
have. Some employees might have an email address while others don't, but if you include an
email field then you will have the option of entering an email address for every employee.
Keys
How is the database designed so that we can tell two different records apart? Principally, we
could look at the data contained in each one. Ah, but what if there are two employees with the
same name? How do we set about differentiating between the Chris Ullman who works on the
fifth floor in the budgeting department, and the Chris Ullman who works in the basement writing
technical books? You could look at the telephone numbers, which are likely to be different – but
that's not guaranteed.
Instead, we use keys. A key is a unique identifier – each record in the database table has a key
that is guaranteed to be unique from all the other keys in the key column:
Key values can be numeric or alphanumeric so long as they're unique within the key column. If
your database table has a key, then you can use it in other tables to link them back to other
information stored in your database. For example, I can use the Employee Key values in the
following Books table of the database to refer to the information contained back in the Employees
table:
Thus, the values in the Author Key field simply refer back to the values in the Employees table –
thus identifying the author of each book uniquely and without ambiguity. From this table you
should be able to deduce that the author of this chapter was also author of the best-selling titles
Instant HTML and IE5 Dynamic HTML Programmer's Reference, and that it was the other Chris
Ullman who wrote the less well-known Budgeting for Buffoons. You might also be wondering why
there is a second key in the Books table – the Book Key field. This key belongs to the Books table,
and is a unique identifier for the books in the table. Remember, an author might have written
more than one book and we still need to be able to differentiate each book.
The reason we usually choose to store information in different but related tables like this – and
not to store it in one great big table – is because it can help us to avoid logging the same
information multiple times. The Book table here doesn't need to store all the personal details of
the author of each book – instead, it stores a simple key value. It means that my details aren't
repeated three times in the Books table – they're just stored once in the Employees table.
It also means that if my telephone number changes, the database administrator won't need to
trawl through the Books table looking for the books that I wrote, in order to update that data
multiple times. Instead, they can just go straight to my record in the Employees table, where they
need to make exactly one update! So, keeping this kind of repeatable information separate
means that you can simplify admin tasks dramatically, by ensuring that you only need to change it
in one place. In fact, the process of carrying out this simple piece of common sense is the nuts-
and-bolts of the normalization process! See how terminology can often obscure the most
straightforward of tasks…
So, is there a method that you can use to get information from a database onto your web pages –
regardless of whether the database type is Microsoft Access on Windows 98, or Oracle on UNIX?
Up until very recently the best answer to this question was to use ODBC, but this technology
restricted the user to certain types of data store (mainly databases). Fortunately a better, broader-
based solution is now at hand in the form of OLE-DB, the data access technology of choice for
this book. However, in order to fully understand OLE-DB you really need to see what preceded it.
So let's start with ODBC and the idea of universal access to one type of data store: the database.
What is ODBC?
Open DataBase Connectivity, or ODBC, is a standard for accessing data. It was designed to
allow the programmer to use a common set of routines to access the data stored in databases,
regardless of the type of database in which the data was stored. This meant that once the
programmer was connected to the database using ODBC, they could manipulate the data without
worrying exactly where the data was stored, or which type of database was storing it. It provided
interface transparency – so the programmer could access an Oracle database in the same way
that they accessed a SQL Server database:
There's no common format for saving databases to file, so you can't save a database using one
database application and then directly open the same file using a different database application
(in the way that a word-processed file in the .txt format can be opened and read as plain text in
NotePad, WordPad, Microsoft Word, or whatever).
Fortunately, all sorts of database applications store data using the structure we've already seen:
tables, records, and keys. ODBC allows you to get at this data without worrying about the nuts
and bolts of the hosting database application.
So, ODBC allows you to get at the basic information held in any database. This gives the
programmer freedom to concentrate on the functionality of the application without worrying too
much about the underlying data, or even how to access it.
Universal Data Access
So databases store information in a form that is accessible in a uniform way via ODBC. But what
happens when you want to access data that's stored in one of the following formats:
The point is that, aside from all the databases in the world, there's a wealth of other data out there
that's stored in other formats. There's a generic property of all the receptacles shown above,
including databases – they're all used to store data. It might be a spreadsheet containing your
company finances, or a text file containing a report on the conference you visited last month, or
an email system and its accompanying mail messages – but it's all data. We use the generic term
data store to refer to a receptacle that contains data.
Important Any persisted collection of information is a data store.
There's another common factor linking all these data stores: we might want to access the data
contained within and use it in our web pages and other applications! In this book, we're
particularly interested in how we can access data stores from our ASP pages, and use their data
to influence the appearance and content of our dynamic web pages.
These next few chapters will cover the techniques required to do just that. In our introduction to
data access from ASP pages, we will use a database as the data store – but the techniques can
be used to get at many different types of data store. Therefore, we'll be using the term 'data store'
from now on whenever we refer to a generic type of data source.
So the question is one of how to access the data contained within these data stores. There's a
problem with using ODBC here: generally, the information contained within each of the other
media doesn't fit neatly into a database-type format – and more often than not, ODBC can't help
you get at that kind of data.
In other words, the notion of database access isn't enough to fulfil the dream of universal data
access – we need a way of getting at the other forms of data too. So, how can we get at the
contents of your data stores quickly and easily?
Microsoft's UDA strategy has yielded a technology that has the potential to access the data
contained in any kind of data store. This technology is known as OLE-DB.
What is OLE-DB?
OLE-DB is the next step in the evolution of the anonymous data store. As well as being more
generic than ODBC, Microsoft has done a great deal of work to ensure that OLE-DB is faster and
easier to use than ODBC. Eventually it may well replace ODBC, although that won't be for a long
time yet, if only for the reasons that you often have to rely on third parties writing new OLE-DB
providers and then when they are available they are often more expensive than existing ODBC
drivers. So, consequently, there's a lot more ODBC drivers out there still in use.
The following diagram begins to build up a picture of data access using OLE-DB:
As you can see, the idea behind OLE-DB is very similar to the idea behind ODBC – but in fact it
allows access to a much broader range of data stores. In fact, you'll notice that OLE-DB even
supports database connections through ODBC – so that effectively your generic OLE-DB layer
will allow you to connect to your legacy databases through your existing ODBC connections.
At the time of writing, Microsoft itself has made available quite a number of OLE-DB providers for
different types of data store, including data providers for their Access, SQL Server, Oracle,
Exchange Server, Excel and Foxpro. If you're interested in other external OLE-DB providers
here's a list of some of the companies currently in the market:
Don't worry if you're not familiar with some of these names. The point to take from all this is that,
although this is a fairly new technology, it's got a lot of industry support.
As we said above, the data consumer is just something that uses the data that the data provider
provides. So, in this book, the data consumers will be our ASP pages (or more specifically, the
ADO objects within our ASP pages that will manipulate the data for display on the page). In
another context, we might use the OLE-DB data providers to provide data for other data
consumers, such as an application written in a language like Visual Basic or Visual C++.
ASP and OLE-DB
In fact, each OLE-DB data provider is a unit of code, written in a language such as C++ or Java,
which uses OLE-DB objects to provide the instructions required to communicate and pass data
between the data store and the data consumer. Once you know this, you may be tempted to ask:
"Why don't we use the OLE-DB objects directly within our ASP pages, and cut out these OLE-DB
providers?"
There's a good reason: the OLE-DB objects themselves are very low-level objects. Scripting
languages like VBScript (and even languages like Visual Basic), are simply not sufficiently
powerful to allow us to manipulate the OLE-DB objects (although, of course, languages such as
C++ and Java are!). That's why we take advantage of the data provider/data consumer
mechanism, to pass the data between the data store and the ASP page across a number of
intermediate layers.
We've already discussed the data providers – but what of the data consumer? It comes in the
form of a set of objects known as the ActiveX Data Objects (or ADO). ADO is an interface that
allows our ASP pages to talk to OLE-DB. So, when we use ASP to talk to a data store, we're
actually using ASP to talk to ADO, which in turn talks to OLE-DB, which in turn gets information
from our data store.
How does ADO fit into overall structure? The ADO layer sits neatly between the application itself
and the OLE-DB layer. In our case, we'll refer to the ADO objects explicitly within the code of our
ASP pages, instructing them to read records, update data, and carry out other tasks that relate to
the data in our data stores:
If you've done any database programming in Access or Visual Basic then you'll have come
across Data Access Objects (DAO) or Remote Data Objects (RDO). If so, that's OK – in fact, it'll
stand you in good stead for ADO, because ADO is a superset of DAO and RDO and is much
easier to understand. If not, that's OK too – we'll explain and demonstrate ADO over the course of
the next few chapters, and we'll be using it in our ASP pages to access data stores.
ADO and ASP are Different Technologies!
Don't fall into the trap of assuming that ADO is part of ASP, or that it's designed specifically for
use with ASP! It's true to say that ADO is the ideal tool to use for achieving data access from ASP
pages, and that ADO is shipped as part of the IIS 5.0/ASP 3.0 package. But ADO is more generic
than that. If you're planning to write other data-dependent applications, in languages such as
Visual Basic, Java or Visual C++, there's nothing to stop you from using ADO in those
applications too.
Note In fact, you can use ADO with any COM-compliant programming language.
So where does ADO come from? In fact, ADO is one of a suite of components, which are known
collectively as the Microsoft Data Access Components (or MDAC). This set of components has
enjoyed a release schedule that is separate to that of IIS/ASP. You can download the latest
version of ADO from Microsoft's web site – at the time of writing, you'll find it at:
http://www.microsoft.com/data/download.htm.
The version numbering of MDAC reflects the version numbering of ADO – so that MDAC 2.1
contains ADO 2.1, MDAC 2.0 contains ADO 2.0, an so on. Here's a quick potted history of ADO,
and how the availability of different versions relates to the history of ASP:
ADO has gradually evolved over these releases into the product we'll meet over the next few
chapters. The latest release – ADO 2.5 – sees the addition of two brand new objects (Record
and Stream), which are designed to give us extra capabilities for accessing data stores.
However, the main point to understand is that the object models of ASP and ADO are quite
separate. It's important not to consider them as a single package – instead, they should be
treated as separate but complementary technologies. ASP enables dynamic web sites, ADO
allows you to use data stores as part of a dynamic web site.
The five main objects enjoy a "flat" hierarchy – this means that we can create any ADO object we
need in our ASP code, without the need to create a hierarchy of parent and grandparent objects.
For example, we can use a Recordset object to make a direct request from the data store. In
this case, there's no need to create an explicit Connection object in our code first – ADO does
the necessary work under the hood.
The five main objects are shaded in the above diagram. There are four subsidiary objects –
Property, Parameter, Field and Error (and their associated collections, Properties,
Parameters, Fields and Errors). Only three of the four collections are shown. The
Properties collection was deliberately left off, so that you can easily see the interaction
between the main objects. The relationship between the Properties collection and the other
objects is shown here:
In this chapter, we're going to be working with two types of database application – Microsoft
Access, and the cut down version of SQL Server, namely the MSDE:
Access 2000 is available as part of the Office 2000 Professional or Premium editions –
older versions of Access are available with older editions of the Office suite. Access is the
desktop database for home users, and isn't intended for deployment when a large number of
concurrent users will access data.
SQL Server is Microsoft's leading Windows database and data-warehousing package.
The MSDE (Microsoft Data Engine) is a SQL Server 7.0-compatible data server. MSDE
ships with any version of Office 2000 that contains Access 2000 (Professional, Premium and
Developer) – if you have any of these you'll find the appropriate files on your Office 2000
installation disks (as we'll explain in a moment).
Important If you have a copy of Microsoft Visual Studio, then you can download
MSDE free from the Microsoft web site at
http://msdn.microsoft.com/vstudio/msde/. During the
download process, you will be required to register your Visual Studio
product and provide the Product ID – which can be found in the About
box on your Visual Studio product's Help menu.
You won't need both types of database application for this book. We have made available sample
databases for both, and the examples in Chapters 12–14 will work with both types of database,
unless stated otherwise. If you are in a position to choose between the two, then we recommend
that you use Access for these examples, purely on the grounds that it is both easier to use and
simpler to set up.
However, if you're planning to graduate to anything that might be used within a corporate
environment, then you might prefer to use MSDE – as the SQL Server compatible engine is much
better suited to many-user applications. Just bear in mind that MSDE is the more complex system
to install and use.
Note Don't worry if you are working with a different database application, such as
Oracle. You will have to set up the actual database data and configure the
connection correctly but after that the scripts outlined in the next few chapters
should work without further changes.
If you're unfamiliar with MSDE, and you're intending to use it with this book, here are some tips
on installation that will help you to get it up and running. We'll cover installation from the Office
2000 route first; then from the Visual Studio route.
A little red square denotes that the service is stopped; a green triangle denotes that the service is
started. Double click on it and the following dialog will appear:
If you're planning to use MSDE, and you've installed it using either of the 'Try It Out's above, then
you're ready to set up the data file. Download the file sqldb.zip from the Wrox Press web site –
this contains a single data file called Movie2000.mdf, which you should extract to C:\MSSQL7\
Data.
If you want to use SQL Server then you need to import the Movie2000 database. There are
several ways to do this. One is to open the .mdb from Access 2000 and use the Upsizing Wizard.
If you have Access 97, the upsizing tools are available for download from the following web site:
http://www.Microsoft.com/accessdev/prodinfo/AUT97dat.htm
The wizard is very easy to follow and will create the database on SQL Server. Another way is to
use the Import and Export Data utility on SQL Server (effectively, the Data Transformation Services
wizard).
Testing our Data Store
We're not going to waste any time explaining details, and some of the code you'll see won't be
explained until the next chapter, but this short section of code shows how simple it is to get data
from a data store.
How It Works
As we said at the top of this example, we won't study every detail of this example at this stage in
the book. We'll see more about some of the features seen here later in this chapter, and in
Chapters 13 and 14. But for now, there are a few points to get your teeth into.
This line creates an ADO Connection object; we're going to refer to this object in our code by
giving it the name objConn.
To build the connection to the data store via an OLE-DB provider, we must supply the relevant
connection information – such as the name of the OLE-DB provider and the location of the data
store– to the Open method of the Connection object. The Open method will use this information
to open a connection.
Note In this example, we've used a variable called strDatabaseType in the code,
which you can use to specify the database type that you're using on your local
system. Select one of the lines:
strDatabaseType = "Access"
strDatabaseType = "MSDE"
Note Comment out the other. The code will use your selected value in the If…Then
statement to select the appropriate connection details.
For the Access example database, we achieve this using the following code:
objConn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:\datastores\Movie2000.mdb;" & _
"Persist Security Info=False"
Alternatively, if you're using an MSDE database application to supply the data then you can
achieve the same using the following code:
objConn.Open "Provider= SQLOLEDB;Persist Security Info=False;" & _
"User ID=sa;Initial Catalog=Movie;" & _
"Initial File Name=C:\MSSQL7\Data\Movie2000.mdf"
Note This example will only work if you haven't already set a password for the sa
account. In any corporate environment, or with data you wouldn't want to
compromise, you should set a password, and provide it in the connection
string.In order to simplify the examples in these three chapters, we have not
done this.
Notice that MSDE requires more information than the Access database.
The output on the screen comes from using our Connection object to open a Recordset
object (which will contain data retrieved from the Movies table of the data store), and then
displaying that data as part of the web page:
objRS.Open "Movies", objConn, adOpenForwardOnly, adLockReadOnly,
adCmdTable
While Not objRS.EOF
Response.Write objRS("Title") & "<BR>"
objRS.MoveNext
Wend
Note Don't worry about the recordset manipulation for now – we'll cover it in detail in
Chapter 13.
When we've finished with the connection, we can Close it and release it from memory (we do the
same for the recordset):
objConn.Close
Set objConn = Nothing
At its very simplest, that's all there is to displaying data from a data store. As you can see, this
program has just three steps:
Making a connection to the data store
Displaying the data
Closing the connection
But, we don't need to create a 'connection' when we open up a document or spreadsheet using
Word or Excel. So why do we even need to establish a 'connection' from the ASP page to the
data store? Let's focus on the physical act of connecting your data store.
What is a Connection?
This might seem pretty obvious, but a connection is the thing that links the ADO objects (in your
code) to the data store. We can understand it by comparing it to the way your telephone works.
You can use the same telephone (at different times) to call your bank, or the local cinema, or the
Weatherline service – and get information on your bank details, the current film releases, and
your local weather. Each time, you're just creating a connection from your telephone to the
service in question – and when you've finished you cut off the connection by terminating the
phone call.
A database connection works in much the same way. At one end, replacing the telephone, we
have the ADO objects – specifically the ADO Connection object. At the other end, replacing the
bank teller or the Weather Office's recorded message, we have one or other data store. In
between, we have a connection that ties them together for as long as they need to communicate.
When the Connection object has finished communicating with one data store, you can
disconnect (using the Close method as we did in the previous example). Then, if you like and as
long as you don't remove the object from memory, you can use the same Connection object to
connect to another data store (just as we can use the same telephone to call different services at
different times).
Of course, if you had lots of telephones (and telephone lines) then you could call lots of different
services at the same time (although you'd probably need to ask some of your office-mates to help
with all the ensuing conversations!). You can do the same with Connection objects. You can
have lots of Connection objects in your code, with each one connected to a different data store.
For example, an Access database is identified by the name and location of the .mdb file. SQL
Server, on the other hand, is designed to handle more (and larger) databases, and there is often
more than one machine running SQL Server – so in order to uniquely identify a database, it could
be necessary to provide enough information to identify the data server as well as the name of the
database. However, you may have noticed that this wasn't necessary in our example. This is
because MSDE is a reduced version of SQL Server and doesn't require the same amount of
information.
Every time you communicate with a data store, you'll need some form of connection. Sometimes
you'll create the connection yourself; other times you might allow the system to create it for you.
Either way, you're using a connection. Before we can connect to a data store, we need some way
of knowing what it is and where it is. There are three ways to supply this information when
creating a connection:
Connection Strings
Data Link Files
Data Source Names
We'll look at each of these in turn, and we'll also consider why some of them are preferable to
others.
Connection Strings
A connection string is a simple character string that lists all of the information needed to
connect to a source of data. Of the three methods listed above, they're probably the most difficult
to use because you have to write the strings yourself, instead of using some neat wizard.
However, they give you the most power. We used connection strings to specify the connection
details for the .mdb and .mdf data stores in the Connect.asp example above. A typical
connection string will contain some or all of the following key pieces of information (dependent on
the type of data store we are connecting to):
Provider: the type of OLE-DB provider used in the connection.
Driver: the type of ODBC driver, such as the ODBC Driver for Microsoft Access or
ODBC Driver for Microsoft SQL Server (if you're using ODBC directly instead of OLE-DB)
Initial File Name or Data Source: the physical database path and file name
Initial Catalog: the name of the database
User ID: the user name needed to connect to the database (sa is the default for the
administrator user name)
Password: the password of the user specified in the User ID field above
Persist Security Info: a Boolean, set to True if you want Windows to remember
your password for you
Writing a connection string is just a case of joining the various pieces of information together –
separating consecutive pieces with semi-colons. Let's have a look at a few sample connection
strings, to get a feel for how they are constructed to suit a particular set-up.
Microsoft Access uses the Microsoft Jet engine, so here the OLE-DB provider we've specified is
for the Jet engine (rather than Access itself). We've also specified the location and name of the
Access database, as the Data Source, and the Persist Security Info Boolean.
If we were accessing the same database using the ODBC driver for Access (instead of an OLE-
DB provider), then we might use the following instead:
"Driver={Microsoft Access Driver (*.mdb)}; DBQ=C:\datastores\
Movie2000.mdb"
This time we specify the ODBC driver (using Driver) and the database name and location
(using DBQ).
For a SQL Server-type database, the information required in the connection string is different
again. Here's the connection string we used to connect to the Movie2000.mdf data file in the
Connect.asp example above:
"Provider= SQLOLEDB;Persist Security Info=False;" & _
"User ID=sa;Initial Catalog=Movie;" & _
"Initial File Name=C:\MSSQL7\Data\Movie2000.mdf"
This adds the Initial Catalog and the user ID to the required information as well.
If the Movies database were properly installed into a SQL Server (rather than being accessed via
MSDE as we're doing here), then it would be different again. In that case, a connection string
such as the following should do the trick:
"Provider= SQLOLEDB;Data Source=MyDataMachine;" & _
"Database=Movie;User ID=sa;Password="
You're probably starting to get the idea – the structure of the connection string is dependent on
the type of data store you're trying to connect to.
We've already come across the notion of SSIs – remember that an SSI is a way to include the
contents of one file within another. When ASP sees the #INCLUDE command, it looks for the
FILE argument, which specifies the SSI (the file to be included). Then it places the contents of
the specified SSI into the first file, in place of the #INCLUDE command (see the example below).
Thus, the contents of the SSI behave as though they were typed directly into the first file.
In the context of database access, we can use SSIs to store our connection string details. Using
this technique, we can write the connection string into an SSI, and then include the SSI into each
ASP page that needs it. Thus, the connection string is only defined in a single place.
Note We recommend that you avoid file extensions such as .txt and .inc when
naming your SSI files. Instead, always use the file extension .asp for you
SSIs, which (as we explained in Chapter 10) provides additional security for
your SSI code.
For example, we could create a new SSI file called DataStore.asp, with the following contents:
<%
strConnect = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:\datastores\Movie2000.mdb;" & _
"Persist Security Info=False"
%>
Here, DataStore.asp is a single central file that contains your connection details. Now, we can
include this into each ASP page that uses such a connection, by writing the following line into
each ASP page:
<!-- #INCLUDE FILE="DataStore.asp" -->
If the connection details change (for example, if the location of the data store changes, or if we
manage to acquire a more efficient OLE-DB data provider), we don't need to edit each-and-every
affected ASP file: instead, we just change the contents of DataStore.asp.
So let's go back to our example, and rewrite it so that the connection string is stored in its own
little SSI.
In Connect2.asp, the cumbersome connection details have been replaced with a simple
#INCLUDE command that inserts the connection string definition as required. Once we've
performed that include, the strConnect variable is available for us to use with the Connection
object's Open method (and anywhere else, if we want to).
The connection string is that which is defined in the SSI, DataStore.asp. If you ever need to
update any of the information contained within the connection string, then you only have to do it
once – within DataStore.asp.
In Windows Explorer in Windows 98 or NT 4.0, you could create a data link file by selecting the
New option from the File menu – and thus generate and store the necessary connection string
information in a UDL (Universal Data Link) file. Since then, Microsoft has deemed the ready
accessibility of this functionality as 'confusing' to novices, and has thus partially removed it from
Windows 2000.
In other words, it's still there – but creating a UDL in Windows 2000 is just a little less convenient
than it was in Windows 98 and NT 4.0. So how do you go about using it now? Let's see…
2. In Windows Explorer, find MovieLink.udl and right-click on it. From the resulting
menu, select the Properties option. This will present you with the MovieLink.udl Properties
dialog.
3. Select the Provider tab and select the OLE-DB provider you wish to create a data link for
(select Microsoft Jet 4.0 OLE DB Provider for an Access database, or Microsoft OLE DB
Provider for SQL Server for MSDE):
4. Now select the Connections tab. If you selected the Jet provider (for an Access database)
in the previous screenshot, you'll get something that looks like this. Enter the full folder
path to the Movie2000.mdb file, and check the Blank password check box, as shown here:
Alternatively, if you're using MSDE then the Connections tab will look like this. Here, you
need to supply a server name (the name of the machine that is hosting the data), user ID
and password, and specify that you're actually attaching a database file:
5. Click on Apply and then follow this with OK to dismiss the dialog.
6. Open up Notepad and view the MovieLink.udl. This is what it looks like if created in
MSDE (I've used Format | Word Wrap for this screenshot, just so that you can see the entire
contents of the string):
How It Works
There's two ways you can make this work. First, you could copy the connection string from the
.udl file, and paste it into your ASP code, as shown here:
objConn.Open "Provider=SQLOLEDB.1;Persist Security Info=False;" & _
"User ID=sa;" & _
"Initial Catalog=Movie;Initial File Name=C:\MSSQL7\Data\
Movie2000.mdf"
Note Note that in the above code fragment, I've broken the connection string at
various points, using a string concatenation character, &, and a line
continuation character, _, to make the code more legible.
Alternatively, you could reference the data link file by its file name in your ASP code:
objConn.Open "File Name=C:\InetPub\wwwroot\BegASPFiles\MovieLink.udl"
However, DSNs are now considered to be an outdated method of establishing a data connection.
Unfortunately they use ODBC drivers – so you lose many of the advantages that come with using
the OLE-DB providers, because DSNs don't support them. As we've remarked before, OLE-DB is
the way that Microsoft are encouraging people to go – it is faster and more efficient, and you're
going to have to get used to it anyway in the end.
However, at the moment, DSNs are still in everyday usage – many people still use this method of
creating a connection in preference to either explicit connections strings or UDLs, simply because
they're so simple to use. Therefore, you should be aware of how to create one.
Creating a DSN
You can use the ODBC Data Sources administrator to create a DSN automatically for you – you
supply the information, and give it an identifier. Then you can use the identifier within your ASP
code to access the DSN (and hence the data).
In Windows 2000, you'll find the ODBC administrator by selecting Start | Settings | Control Panel,
then choosing Administrative Tools. There, double-click on the Data Sources (ODBC) icon. You'll be
presented with the following dialog:
Yours may look slightly different from this, depending on which data sources are set up. The
drivers shown above (and others, such as SQL Server, which isn't shown here) are supplied with
a variety of Microsoft products, such as Microsoft Office.
The User DSN tab (above) shows all data sources for the user who is currently logged on. This
allows you to have data sources that are only available for selected people who log onto the
machine. This is no good for ASP, since ASP can only use System DSNs. So let's have a look at
the System DSN tab:
A data source that appears in this tab is available to anyone who logs onto the local machine –
including ASP itself. The screenshot above shows the System DSN tab on my machine – as you
can see, this machine already has one SQL Server data source and one Access data source.
Now click the Select… button to get the Select Database dialog – use this to locate the
Access database Movie2000.mdb. (You could click the Create button to create a new
database.)
3. If you now click OK you'll see that your new data source has been added to the list.
How It Works
Once you've created a DSN using this method, you can reference it within a connection string,
like this:
objConn.Open "DSN=Movie"
Instead of providing the normal connection string information, you simply pass the identifier of
your DSN to the Open method; then ADO will look up the DSN and use the connection
information contained there. It's very simple – but remember it can only be used for ODBC driver
connections.
Note Again, we should remember that DSNs are a largely outdated method of
specifying connection details, mainly because they use ODBC drivers instead
of OLE-DB providers to handle the data. We include them here only because
you may come across DSNs when working with legacy systems. We won't be
using DSNs in this book.
The Connection Object
So now we know what a connection is, it's time to look at the ADO object of the same name. The
Connection object is what ADO uses to store our information about the data store connection.
In fact, it actually represents a unique session with the data store. As we mentioned earlier, this
means that we can use a single Connection object to connect to different data stores at
different times. This implies that a Connection object can use different providers at different
times.
Moreover, we can use a number of Connection objects to connect to different data stores at the
same time (and we can even have multiple Connection objects simultaneously connecting to
the same data store!). So, for example, we might have an ASP page with two Connection
objects, called objConn1 and objConn2. One could use the OLE-DB Jet provider to connect to
an Access database, while the other could use the OLE-DB SQL Server provider to connect to a
SQL Server database.
This may seem obvious – but it's an unfortunate truth that not all OLE-DB providers support
exactly the same functionality. This shouldn't really be a surprise – after all, we can expect some
data store applications to be more powerful than others.
This difference in capability between providers shouldn't be a big problem to you – especially in
the early stages of ASP and ADO development. However, it's something that you should be
aware of as you build your skills.
This uses the CreateObject method of the Server object to create an instance of the
Connection object – in much the same way, we've used the same method to create instances
of other objects in earlier chapters. The programmatic identifier (or ProgID) for the ADO
Connection object is ADODB.Connection. So that we can use the object within our code,
we've given it a name – objConn.
Opening a Connection
So, the two lines above are enough to create a Connection object. However, we've done
nothing with it yet. Just because we have created a Connection object, that doesn't mean that
we're connected to the database! In order to actually establish the connection we use the Open
method of the objConn Connection object. The syntax for using the Open method is:
In fact, we used this in the examples we've seen so far in this chapter. For example:
objConn.Open strConnect
Here, we've only specified the first argument – the connection string – and the other arguments
assumed default values. In fact, all four arguments are optional arguments – we don't have to
specify any of them at the time we call the Open method. Instead of the above line, we could
achieve the same effect by setting the connection string using a special property of the
Connection object – its ConnectionString property. After that we call the Open method
without any arguments, like this:
objConn.ConnectionString = strConnect
objConn.Open
Here's an example where we use the Open method directly, passing the user ID and password as
well as the connection string:
objConn.Open strConnect, "ChrisU", ""
Note If you pass user ID/password details in the second and third arguments like
this, but you also specify user ID/password details in your connection string
then– according to the ADO documentation – the results are unpredictable. It is
wise to avoid this kind of potential confilict of information by choosing one
method of input only and sticking to it.
The above examples assume that strConnect is the connection string, like the one we created
in DataStore.asp earlier in this chapter. But of course, we could equally well write an explicit
connection string as the first argument instead:
objConn.Open "Provider=SQLOLEDB;Persist Security Info=False;" & _
"User ID=sa;" & _
"Initial Catalog=Movie;Initial File Name=C:\MSSQL7\Data\
Movie2000.mdf"
This doesn't actually remove the object from memory; so you can Open it again if you need to.
Once you've closed the connection, you can change the connection string and other properties,
and use the same object to open a different connection.
Alternatively, if you've finished with the object you can remove it from memory, by setting the
name of the object to Nothing:
Set objConn = Nothing.
The Properties Collection
You've seen one of the properties of the Connection object – the ConnectionString
property – in the code fragments above. However, there's a whole lot of information behind the
Connection object that we haven't met – and some of which we'll never need! But it's useful to
know that the names and values of all the connection properties can be accessed from a single
location – the Connection object's Properties collection. The Properties collection
contains a Property object for each property that the connection supports.
A collection is much like an array (we covered arrays in Chapter 4, and we met the collections of
the ASP Request object in Chapter 7). You can look through the elements of the Properties
collection in much the same way as we can look through the elements of the ASP
Request.Querystring collection (or any of the other collections of the Request object). Let's
try this out, and then we'll examine how it works.
We're going to establish a connection to the database; we won't pass any data across the
connection, but instead we'll just examine the properties of the established connection.
Create a new file, and type in the following HTML and script. Don't forget that this file, like all the
code in the book, can be obtained from the Wrox Press web site at http://www.wrox.com, so
if your typing is like mine then you might prefer to download it instead of typing it in:
<%
Option Explicit
Dim strConnect
%>
<!-- #INCLUDE FILE="DataStore.asp" -->
<HTML>
<HEAD>
<TITLE>ADO Connection Properties</TITLE>
</HEAD>
<BODY>
<TABLE BORDER=1>
<TR>
<TD><B>Property</B></TD><TD><B>Value</B></TD>
</TR>
<%
Dim objConn ' Connection object
Dim objProp ' Property object
As you can see by scrolling down the page, there are a lot of properties in this collection! But
there's no cause for alarm: you don't need to know about them all in order to continue and, in fact,
you'll probably find that you'll rarely need to use most of them in your entire programming career.
But it's useful to know that they're there.
How It Works
First of all we specify Option Explicit – as it is good practice for catching typographical errors
in the code – and declare the strConnect variant ready for our connection string. Then we
include the data store SSI – just as we did in Connect2.asp – to define the value of
strConnect:
<%
Option Explicit
Dim strConnect
%>
<!-- #INCLUDE FILE="DataStore.asp" -->
Now we begin a table that will show the properties and their values:
<TABLE BORDER=1>
<TR>
<TD><B>Property</B></TD><TD><B>Value</B></TD>
</TR>
Once the table header is created, we need to create the table body. For this, we loop through all
of the properties using a For Each … Next statement – each iteration through the loop will look
at each property in turn and write a single row of the table. In preparation, we declare a couple of
variants – one for the ADO Connection object, and one for the ADO Property object.
<%
Dim objConn ' Connection object
Dim objProp ' Property object
Once the connection is open we can start looping through the Properties collection. The For
Each … Next statement is perfect for iterating through collections, because the control variable
(in this case objProp), which references each member of the collection in turn, can also be used
inside the loop. Inside this loop, we write the property's Name and Value into cells in the table:
' loop through the properties
For Each objProp In objConn.Properties
Response.Write "<TR>" & _
"<TD>" & objProp.Name & "</TD>" & _
"<TD>" & objProp.Value & " </TD>" & _
"</TR>"
Next
Note We've included a non-break space here – the bit. It's there to ensure
that each cell in the table contains at least one character, even if the property
value itself is empty. It's just a formatting trick to make the table look tidier.
And finally we can close the connection and clean up (and complete the table with a closing
</TABLE> tag):
' now close and clean up
objConn.Close
Set objConn = Nothing
%>
</TABLE>
That's all there is to it. You'll find this technique of looping through a collection quite useful, and
you will see it again later.
You can use the Properties collection to find out what functionality is supported on a
connection. For example MSDE allows you to have a maximum row size of 8060 characters,
while Access only allows 4049. Admittedly, this is unlikely to be a problem, but you could check
the Maximum Row Size property to find this out. To do this you don't need to loop through the
whole collection – you can just access the element you need directly, like this:
Response.Write "Cols = " & objConn.Properties("Maximum Row Size")
This uses the same collection, but you are retrieving a Connection's property value using the
property name.
The Errors collection contains Error objects, in much the same way as the Properties
collection contains Property objects. Each Error object contains several properties that you'll
need when looking at errors:
Property Description
Number The number of the error
Description The description for the error
Source Identifies the object that raised the error
SQLState Holds the SQL error code
NativeError Holds the database-specific error code
Using these properties you'll be able to find out, in more detail, what error occurred so that this
can be reported back to the user. Let's see how to do this.
Try It Out – The Errors Collection
1. Open up your editor, create a new file and add the following HTML and script:
2. <%
3. Option Explicit
4. Dim strConnect
5. %>
6. <!-- #INCLUDE FILE="DataStore.asp" -->
7. <HTML>
8. <HEAD>
9. <TITLE>ADO Errors</TITLE>
10. </HEAD>
11. <BODY>
12.
13. <%
14. On Error Resume Next
15.
16. Dim objConn ' Connection object
17. Dim objProp ' Property object
18. Dim objError ' Error object
19.
20. ' create the connection object
21. Set objConn = Server.CreateObject ("ADODB.Connection")
22.
23. ' and open it
24. objConn.Open strConnect
25.
26. ' now we can execute some SQL
27. objConn.Execute "SELECT MissingColumn FROM MissingTable"
28.
29. ' Errors means the count will be greater than 0
30. If objConn.Errors.Count > 0 Then
31.
32. ' loop through the errors
33. For Each objError in objConn.Errors
34. Response.Write "<TABLE BORDER=1>" & _
35. "<TD>Error Property</TD>" & _
36. "<TD>Contents</TD>" & _
37. "<TR><TD>Number</TD><TD>" & _
38. objError.Number & "</TD></TR>" & _
39. "<TR><TD>NativeError</TD><TD>" & _
40. objError.NativeError & "</TD></TR>" & _
41. "<TR><TD>SQLState</TD><TD>" & _
42. objError.SQLState & "</TD></TR>" & _
43. "<TR><TD>Source</TD><TD>" & _
44. objError.Source & "</TD></TR>" & _
45. "<TR><TD>Description</TD><TD>" & _
46. objError.Description & "</TD></TR>" & _
47. "</TABLE><P>"
48. Next
49. Else
50. ' no errors
51. Response.Write "There were no errors."
52. End If
53.
54. ' now close and clean up
55. objConn.Close
56. Set objConn = Nothing
57. %>
58. </BODY>
59. </HTML>
60. Save the code as ConnErrs.asp.
61. Ensure that the SSI file DataStore.asp, which we gave earlier in this chapter, is also in
the same folder.
62. Type the URL into your browser, and view the page.
This is exactly what we expect to happen, because there's a deliberate error in the code. Whilst
the SQL statement we executed is syntactically correct, it's requesting a non-existent column in a
non-existent table.
How It Works
Once again we start with the include file, and give the .asp file a header:
<%
Option Explicit
Dim strConnect
%>
<!-- #INCLUDE FILE="DataStore.asp" -->
<HTML>
<HEAD>
<TITLE>ADO Errors</TITLE>
</HEAD>
To ensure that the ASP script is not terminated when an error occurs, we need to use the On
Error statement. This is similar to one that you may have seen used in Visual Basic and VBA,
but has less flexibility. All we can do is Resume Next, to continue processing at the next
statement:
<BODY>
<%
On Error Resume Next
Next we declare the variables. This time we declare a variable that will represent an Error
object. Then we create the connection and open it:
Dim objConn ' Connection object
Dim objProp ' Property object
Dim objError ' Error object
Then we can execute our SQL statement, which contains the deliberate error. We haven't
covered the Execute statement yet – this will be part of the next chapter. For now, we'll just use
it to force the error:
' now we can execute some SQL
objConn.Execute "SELECT MissingColumn FROM MissingTable"
The Count property of the Errors collection tells us how many errors there have been. We can
test this before going into our error display section of code:
' Errors means the count will be greater than 0
If objConn.Errors.Count > 0 Then
If there are errors, we can start looping through the Errors collection, using the same technique
as we used on the Properties collection. We build up an HTML table containing all of the error
information that we need to see:
' loop through the errors
For Each objError in objConn.Errors
Response.Write "<TABLE BORDER=1>" & _
"<TD>Error Property</TD>" & _
"<TD>Contents</TD>" & _
"<TR><TD>Number</TD><TD>" & _
objError.Number & "</TD></TR>" & _
"<TR><TD>NativeError</TD><TD>" & _
objError.NativeError & "</TD></TR>" & _
"<TR><TD>SQLState</TD><TD>" & _
objError.SQLState & "</TD></TR>" & _
"<TR><TD>Source</TD><TD>" & _
objError.Source & "</TD></TR>" & _
"<TR><TD>Description</TD><TD>" & _
objError.Description & "</TD></TR>" & _
"</TABLE><P>"
Next
How It Works
That's all there is to it. Notice that the SSI file doesn't open the connection, and so it doesn't close
the connection either: responsibility for this is taken by the calling ASP page.
The UseErrors.asp file is familiar too. The first thing you notice is that we include two files –
DataStore.asp for the connection details, and Error.asp for the error routine.
<!-- #INCLUDE FILE="DataStore.asp" -->
<!-- #INCLUDE FILE="Errors.asp" -->
Much of the rest of the code is the same as that in ConnErrs.asp. The only other difference is
at the point where we call the new error routine, passing in our open connection:
' now check for errors
CheckForErrors (objConn)
This gives us the same result as before. Easy, huh? Moreover, you now have a simple generic
error routine that you can use from any .asp file to display information about your ADO errors.
This saves having to write it each time, and makes it easier to update.
Summary
We've covered quite a lot of ground in this chapter, and although we haven't really done a lot of
database-type things, it's been important to get some of this groundwork covered. Essentially, in
this chapter we have studied connections between applications and data stores, and we've
learned that:
We use a connection to pass data between an application (such as an ASP page) and a
data store, in the same way that two telephones communicate via a connection
The data store can talk to an OLE-DB data provider, which in turn communicates with a
data consumer
In our case, the data consumer will be a set of interfaces called the ActiveX Data Objects
(ADO). We use these objects in our ASP code
The main ADO objects are called Connection, Command, Recordset, Record and
Stream
The ADO Connection object is an object that we use to represent the physical
connection between the ASP page and the data store
Along the way, we met the old standard of ODBC – which was a standard for achieving access to
databases. And we looked at what OLE-DB can do for us, and noted that we can think of ADO as
just a high-level wrapper for OLE-DB.
Remember that ASP and ADO are quite separate technologies – they perform quite different
tasks and can exist independently of one another. For the purpose of creating dynamic web
pages, however, ASP and ADO provide very complimentary functionality.
Having laid a firm foundation on the subject of connections, it's time to put them to good use by
manipulating some data. In the next chapter, we'll meet the ADO Recordset object and see how
we can use it to organize the information that we retrieve from the data store, and the information
that we send to the data store.
Chapter 13: Using Recordsets
Overview
In the previous chapter we spent a lot of time looking at the connection to the data store. While
it's fundamental to the process of passing data between our ASP pages and our data stores, it's
not really very exciting – what we really want to see is lots of data in your ASP pages. For that,
we need to build on what we've learned about ADO so far. So, in this chapter we will:
Examine what recordsets are, and how we use them
Explain the notion of a cursor, and meet the different types of cursor
Understand the concept of 'locking' a data store
Learn how to move back and forwards through the records of our recordset
See how we can search the recordset for individual records
Hide all the items in a recordset that don't meet a specific criterion
Get more information about the recordset
In this chapter we'll really begin to get to the heart of using ADO for data access within our ASP
pages. We'll be looking at how you can run queries and stored procedures, to both return and
insert data. We'll also be looking at ways to build HTML tables automatically from a set of data.
Before we start, we should just highlight that data can pass between the data store and the ASP
page, in either direction. To keep things fairly simple, the examples in this chapter will all involve
reading data from the data store and using it within our ASP pages. In the next chapter, we'll look
at how we can write data from the ASP page to the data store, and hence keep a permanent copy
of data that is generated by the user, or by the ASP, or by scripting logic as part of the page.
The Recordset Object
Let's recall the players in the ADO 2.5 object model again:
So far, of the main ADO objects, we've looked only at the Connection object and how it
represents a real connection between the (ASP) application and the data store. Of course, if
information is to pass between two places (such as an ASP page and a data store), then there
needs to be a connection between them – and the ADO Connection object is a representation
of that connection which we can use in our code.
But how do we manage the data, once we've got it from the data store into our page? Happily, it's
organized into a fashion that reflects its original format in the data store, and that is relatively easy
and intuitive to work with. In our code, we can manipulate the data through another of the main
ADO objects – we can use a Recordset object.
As you can see, the Recordset object sits right in the middle of our ADO object diagram – which
reflects the fact that it is probably the most heavily used ADO object in code. After all, it
represents the data that we're working with – which is, in a sense, the raison d'être of ADO.
What is a Recordset?
In the previous chapter, we talked about the notion of an employee record being a collection of
(one or more) values, for different types of data that all relate to a particular thing. We gave the
example of the employee record:
This is just a single record. This record contains a number of pieces of data – one for each of the
fields 'Employee Key', 'Name', 'Address 1', etc. Each piece of data is information about this
employee, on the subject of that field.
Now imagine a whole collection of records just like this one. Each record pertains to one
individual employee, and the information in that record is information about that employee.
Moreover, each record has the same structure – that is, it represents values of the same fields
('Employee Key', 'Name', 'Address 1', etc.) in the same order:
This is a set of records – or a recordset. This recordset has three records in it – one record each
for the information of Messrs Masters, Minter and Morrissey. In order to define this recordset,
we've chosen a particular set of fields (i.e. the 'classes' of information that we've selected in the
top row of the table) and some criterion specifying which records we're interested in (e.g. here we
wanted to know about employees whose surname begins with 'M', but none of our other
employees).
In ADO, we represent a recordset – the data received when we query the database – by using its
Recordset object. In fact, an ADO Recordset object is more than just a set of related data, like
the table shown above – it has functionality to allow us to manipulate data, add, remove or hide
records, search for data within the record, and so on. We'll meet much of this functionality during
the course of this chapter.
Recordsets and Cursors
In addition, an ADO Recordset uses something called a cursor. A cursor is a 'pointer', which
points to one of the records in the recordset, thus indicating our current position in the recordset.
Important Every active Recordset object has a cursor, and at any given time, the
cursor is pointing to exactly one of the records in the recordset.
What's the cursor for? Predominantly, it's there to help us to find our way around the records of a
Recordset object. We can move the cursor so that it points to exactly the single record that
we're interested in; and then we can use the names of the fields to single out individual 'cells' of
information. There are various ways of moving the cursor around – for example, we can search
the recordset for a particular record, or we can use special Recordset methods called
MoveFirst, MoveLast, MoveNext and MovePrevious (which we'll meet later in the chapter).
For example, in order to find John Morrissey's telephone number we can instruct the cursor to
move to Mr Morrissey's record (either by 'searching' for that record, or – in this case – using the
MoveLast method). In the following table, the asterisk (*) on the left-hand side indicates the
position of the cursor:
Then we can ask for the Phone field of the current record.
Creating a Recordset
That's all very well; now how do we go about creating a recordset? Well, ADO is quite flexible and
consequently there's more than one way. If you want to create an explicit Recordset object in
your code then you'll need to define a variable and then set it equal to a new Recordset object,
like this:
Dim objRS
Set objRS = Server.CreateObject("ADODB.Recordset")
Note If this looks familiar to you it's because it's very similar to the syntax we used
for creating an explicit Connection object in our code, in the previous chapter:
Dim objConn
Set objConn = Server.CreateObject("ADODB.Connection")
Once again, the two shaded lines above are enough to create a Recordset object; but the
object is not connected to a database and it doesn't yet contain any data! Let's see how to put
some data into the Recordset; and then we'll exercise our knowledge in an example.
Recordsets and Connections
It's clear from the previous chapter that the connection is fundamental part of the practice of
passing information between our ASP page and our data store. The connection is the route by
which the information is transferred from one to the other. If there's no connection, then there's no
way for the data to be transported.
So, when we're trying to get data from the data store into a Recordset object (or vice versa), we
need to consider the connection too. However, the necessity for a connection doesn't imply that
we need to create an explicit ADO Connection object in our code.
This is the technique we used in the Connect.asp example in Chapter 12. What's the
advantage of doing it this way? Well, connecting to a data store is quite 'expensive' – it's a task
that takes a relatively long time to complete. So if you need to use several recordsets in your
code, or if you're going to use one recordset to make a number of queries, it is best to only
connect to the data store once. In this case, create an explicit Connection object and use it
whenever you need to use the data store connection.
What's happening here? Well, we've explicitly created a Recordset object in our code, and
we've used its Open method to forge a connection between our code and the data store. This
action is enough to create a physical connection, and to use objRS in the same way – yet there
is no Connection object in sight!
In fact, there is an ADO Connection object – but ADO creates it under the covers, and (at the
moment) we can't see it in our code. That's great – ADO creates the extra objects we need, so
we don't need to worry about any kind of 'object hierarchy'. It keeps the logic simple and our code
tidy.
If we never need to use the underlying Connection object in our code, this is perfect – it's a very
tidy way of letting ADO deal with it for us. And if we decide that we would like to use it, we can
give it a name in our code by referencing it via the Recordset object's ActiveConnection
property:
Dim objRS, strConnect
strConnect = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\
Movie2000.mdb;"
Once we've done this, we can treat objConn just as if we had created it explicitly using the Set
command.
Note This notion of a 'flat' hierarchy extends to the other ADO objects too. We can
specify connection details directly to a Recordset object (cutting out the need
for an explicit Connection object); equally, you'll find that it's possible to
specify the details of a SQL command directly to a Connection or
Recordset object (cutting out the need for an explicit Command object).
This just gives us greater flexibility in our code. The interdependence between
objects (like the relationship shown here between the Connection and
Recordset objects) means that they can be handled explicitly in the code, if
that's appropriate, or left for ADO to handle under the covers.
How It Works
The first few lines of code are familiar to us – we used them in most of the examples in Chapter
12. The Option Explicit command demands that we define all our variable names, and we
follow that by including the DataStore.asp SSI that contains our connection string definition:
<%
Option Explicit
Dim strConnect
%>
<!-- #INCLUDE FILE="DataStore.asp" -->
<HTML>
<HEAD>
<TITLE>ADO Recordset Object</TITLE>
</HEAD>
<BODY>
Next, we define three constants, which we'll use in a moment with our Recordset object:
Const adOpenForwardOnly = 0
Const adLockReadOnly = 1
Const adCmdTable = 2
As you'll see shortly, we'll pass these constant values to the Recordset object when we open it
– they tell the Recordset object about the type of recordset we want ADO to create for us. Don't
worry about the different types of recordset available for now – all will be explained later in the
chapter.
Once the object is created, we can open it. For that, we use the Open method of objRS:
objRS.Open "Movies", strConnect, _
adOpenForwardOnly, adLockReadOnly, adCmdTable '
now open it
The Recordset object's Open method has five parameters, and as you can see, we have
specified a value for each of them here:
The first parameter is the source of the data. In this case it's the Movies table from our
database.
The second parameter is the connection information. We need to tell the recordset that
we want a connection to the Movie2000.mdf or Movie2000.mdb database. But we don't
need to pass a Connection object – instead, we can just pass the connection string and
ADO will set up the Connection object under the covers. So here, we've passed the
connection string, strConnect
The last three parameters use the constants that we defined earlier on in the page:
The third and fourth parameters tell ADO what type of recordset to use – they define two
characteristics of the required recordset, called the cursor type and the lock type. We'll be
looking at these recordset characteristics later in the chapter.
The fifth parameter states that Movies is a database table.
The recordset is now open, and requested data has been copied from the database into our
recordset. Now, we're in a position to examine the contents of the recordset – and to do that, we'll
simply step through each record and write the contents of its Title field to the browser.
In order to step through the recordset, we use the cursor. Happily, when we open the recordset,
ADO points the cursor at the first record. So in order to examine each record, we just need to
create a loop in which we:
Examine the current record (i.e. the record to which the cursor is currently pointing); then
Move the cursor to the next record in the recordset
Starting at the first record, we'll just do that repeatedly until we run out of records. How do we
know when we've run out of records? The Recordset object has a property called EOF ('end-of-
file'), which is a Boolean value. It is True when the cursor runs past the last record in the
recordset, and it's False otherwise. So, while we're stepping through the recordset, the value of
EOF is False; if we step past the last record then EOF will be set to True. Hence, we repeat the
loop while EOF is Not True.
That's all there is to it, and this is a process you'll become familiar with during this chapter.
These constants were then used later in the code, to define particular features of the recordset:
objRS.Open "Movies", strConnect, adOpenForwardOnly, adLockReadOnly,
adCmdTable
Here, we use these constants as arguments to the Open method – they tell the Open method
more about the type of Recordset object we want. The integer values of the constants are
determined, and these values are interpreted by ADO – which goes away and forms a particular
type of recordset. In this case, they specify that we want a recordset with a forward-only, read-
only cursor and that we're asking for an entire table (don't worry about what that means just now
– we'll learn more about it later in this chapter).
Well, ADO doesn't mind which one you use. However, consider what happens when a colleague
comes to read the code we've written. Your colleague would find the second version rather more
difficult to understand – the integers 0, 1, 2 don't have any intuitive meaning, so it's rather less
obvious that we're trying to open a forward-only, read-only recordset with a query in the form of a
table.
In other words, we use constants in ADO because they make our code easier to write and to
read. That's why the ADO constants are given special names – to reflect the purpose of the
integer values that they represent.
If you ever write applications using languages such as Visual Basic or Visual C++, you'll find that
these constants are automatically available to you once you reference the ADO type library.
In order to save us from having to explicitly define these constants we can make use of the ADO
type library in our ASP code. The ADO type library is contained in a file called msado15.dll,
which you should be able to find on your web server machine. The default location for this file is
C:\Program Files\Common Files\System\ado. To use the ADO type library, you need to
use a line such as the following:
<!-- METADATA TYPE="typelib" FILE="C:\Program Files\Common Files\System
\ado\msado15.dll" -->
You can include this METADATA statement in each .asp file that uses ADO constants where it's
required (as we'll do in this book). Alternatively, you can put it in the global.asa file, in which
case the constants will be available to every Web page in the application.
Using the msado15.dll type library is the recommended way to make the ADO constants in
your code. We'll be demonstrating this in the remaining examples in this book.
adovbs.inc
If you'd like to see the values of the constants contained in the ADO type library, you can view
them in the file adovbs.inc. This file is also contained in the default directory (probably C:\
Program Files\Common Files\System\ado). If you want to view them, make a copy of the
adovbs.inc file (into another directory) and open up the copy in Notepad. For example, along
with the definition for adOpenForwardOnly, you'll find the constants for the other values we
could use in its place:
'---- CursorTypeEnum Values ----
Const adOpenForwardOnly = 0
Const adOpenKeyset = 1
Const adOpenDynamic = 2
Const adOpenStatic = 3
Important Be careful not to change the contents of adovbs.inc! After all, this file is
a global store for ADO constants.
Although it's no longer recommended, it's possible to use this file as a server-side include (SSI)
file, to insert the ADO constant definitions into your .asp pages. To do this, you'd use a line such
as this:
<!-- #INCLUDE FILE="C:\Program Files\Common Files\System\ado\adovbs.inc"
-->
Note One disadvantage of this method is that it makes your ASP page larger –
because it defines nearly 400 constants, most of which you usually won't need.
That doesn't happen when we use msado15.dll – because it's a dynamic link
library (DLL), so the constants are only defined as they're needed.
To summarize this short section: the ADO constants are there for use with all of the ADO objects,
and make reading and writing ADO code much easier. In the remainder of the book, we'll use the
ADO type library (msado15.dll) to define our ADO constants, via the METADATA statement
shown above.
Now let's move back to the main subject of this chapter – the Recordset object.
Characteristics of the Recordset Object
Perhaps the best way to appreciate some of the important characteristics of a recordset is to
consider the method that we use to create a recordset – the Open method. Perhaps the
strangest-looking line in our last example – and certainly the longest – was the line that used the
Open method to open our recordset:
objRS.Open "Movies", strConnect, adOpenForwardOnly, adLockReadOnly,
adCmdTable
We looked briefly at the five parameters, but we didn't study them in detail. So let's start work on
the demystification of the Recordset object by looking at the syntax of the Open method:
Let's break down the parameters and examine each of them in more detail.
What is a Source?
The source is where the data comes from. In the example's we've seen so far, we've always
used a table name. But the Source parameter could take the form of a SQL statement:
objRec.Open "SELECT * FROM Movies", ...
We'll be looking at stored procedures, queries and the Command object a little later.
In fact, you don't have to use the first parameter of the Open method to specify the source.
Instead, you can set the Source property directly:
objRS.Source = "SELECT * FROM Movies"
objRS.Open
Instead, you could use a Connection object, like we did in the Connect.asp example in
Chapter 12:
objConn.Open strConnect
objRS.Open "Movies", objConn, adOpenForwardOnly, adLockReadOnly,
adCmdTable
We discussed this earlier in the chapter. Essentially, you'd use the first technique if you're only
making a single query to the data store – because it saves us from creating an explicit
Connection object in our code, and keeps the code more tidy.
By contrast, you'd use the second technique if you were making a number of queries to the same
data store. You can create the connection once using the Connection object, and then use the
open connection each time you need it:
objConn.Open strConnect
objRSTitles.Open "SELECT Title FROM Movies", objConn
objRSLinks.Open "SELECT * FROM MovieLinks", objConn
...
or
Set objRS.ActiveConnection = objConn
Although the two lines above appear to achieve the same thing, we get the same subtle
difference. In the first, a connection string is passed in, and this causes a new connection to be
created. In the second, the existing Connection object (objConn) is used as the connection.
We've also very gently hinted that there are different types of cursors. When we open the
recordset, we can specify the cursor type – and this will affect the available functionality of the
recordset that is returned to us. For example, if we select a forward-only cursor (as we've done in
the examples so far), we'll get a recordset whose cursor is only able to move forwards through
the records.
Note In addition to the cursor type, there are other factors that also affect the
available functionality of the recordset – including the lock type and the cursor
location. We'll be looking at what these things are, and what they mean,
shortly.
Recordset Characteristics
So before we consider the available values for the cursor type, let's consider some of the
characteristics that we may want our recordset to display. This will help us decide what cursor
type and lock type we'll need when we use the Open method in future.
Updateable vs Non-Updateable
Are you planning to open a recordset in order to change the data within it? If not, then it makes
sense to use a non-updateable (or read-only) recordset to read the data. Using a non-
updateable recordset means that the data provider can simply send the data to you and forget
about it. You're telling it not to expect any changes – this means that it doesn't need to keep track
of what you are doing. This can give performance benefits.
By contrast, if you're planning to change the data in your recordset, then you'll need your
recordset to be updateable.
Scrollable vs Non-Scrollable
When you're moving the cursor through the records in your recordset, will you need to move
backwards as well as forwards? If so, you'll need a scrollable recordset. The cursor of a
scrollable recordset allows both backward and forward movement of the cursor.
If you're only going to need forward movement of the cursor – as in the Recordset.asp
example earlier in this chapter – then a non-scrollable recordset will suffice. A non-scrollable
recordset can also give performance benefits over a scrollable one, because the recordset
doesn't have to keep track of the data once the cursor has moved passed it.
Keyset vs Non-Keyset
Most tables in a database have some kind of unique key – and this is the heart of a keyset
recordset. Even those that don't have a visible (defined by us) unique key will have a unique key
that the database maintains. When you request a non-keyset recordset you get all of the data
back; while a keyset recordset just returns the unique keys, and only fetches the data itself at the
time you request that record.
Note In fact, this is a generalization: in reality you'll probably find a set of records
returned as well. The idea is that if all of the keys, plus data contained in the
first few records, are returned then there is very little delay in getting the first
set of data. If you move to a key that is not within the current block, then
another block of data is fetched. The advantage of a keyset recordset is that
the recordset only has to keep track of the keys, which are generally small,
rather than a large amount of data.
Dynamic vs Static
This characteristic determines the availability of particular records in the recordset, at a specific
time. A static recordset contains only those records that were available when the recordset was
created. The records are cached in local memory and the recordset will be unaware of any
changes to data in the data store, made by other users. For example, another user could
subsequently delete a record included in your static recordset when it was created, but this will
not be reflected in your recordset.
In contrast, a dynamic recordset will accurately represent any changes made to the data, either
by you or by another database user. You can think of a dynamic cursor as a "window" onto the
data in the database. The rows are loaded into the recordset "as requested" (and not cached
locally, as for a static cursor). This means that if new records are added, deleted, or changed by
other users while you are accessing the database they will become visible when these records
scroll into the part of the recordset you are viewing. The recordset changes dynamically, in
demand to the records you are actually managing at the time.
So it all boils down to how you see the records and how you navigate through the records. Let's
look at the ADO cursor types, and how they relate to the characteristics we've outlined here.
ADO Cursor Types
The updateability of your recordset isn't directly related to the cursor type – that's more the
territory of the lock type (which we'll come to shortly). However, the other three characteristics are
closely related to the list of possible cursor types we have to choose from. There are four in total,
as follows:
Forward-only (adOpenForwardOnly): this is the default type and gives you a non-
scrollable recordset. This is often perfect in ASP code, because we can often find ourselves
just stepping through a list of records in order to display each of them on the browser. In that
case, we just need to start at the first record and move forwards through the recordset until
we get to the end – we don't need to move backwards. It's also static, so changes to the
underlying data are not represented.
Static (adOpenStatic): this is similar to a forward-only recordset, except that it is
scrollable, so you can move back to previous records as well as moving forwards.
Dynamic (adOpenDynamic): the recordset is fully dynamic, and lets you see additions,
amendments and deletions that are made by other users. It's fully scrollable so you can
move around the recordset any way you like.
Keyset (adOpenKeyset): this is similar to the dynamic recordset, but you can't see
records that other users add, although you can see changes to existing records. Any records
that other users delete become inaccessible.
All you have to bear in mind is what you actually want to do with your records. If you just want to
step through them one at a time, then a forward-only cursor is the one you need. If you want to
scroll backwards too, but still don't want to make any changes, then you need a static cursor. If
you need to be able to see any changes you make to your recordset, then a dynamic or keyset
cursor is required (otherwise you may need to Close and re-Open it).
Alternatively, you can also the set the cursor type directly, using the CursorType property like
this:
objRS.CursorType = adOpenForwardOnly
Note You can read the CursorType property at any time, but you can only set it
before the recordset is assigned a live connection.
Cursor Location
The location of your cursor can also have an effect on how your recordset operates. By cursor
location we don't mean the record that the cursor is currently pointing to – rather, we mean the
body that's responsible for creating the cursor – the client or the server.
Important 'Client' and 'server' here refer to the relationship between the data
consumer that's using the data (in our case, ADO), and the data provider
that's providing it (in our case, OLE-DB). Thus client=application;
server=data provider.
Particular functionality will be available depending on which you choose. If you want a dynamic
cursor, you must choose server-side. If you choose a client-side cursor then it will be a static
cursor.
Certain methods won't work on server-side cursors and you can't do things such as creating local
indexes, which can only be done on the client. However, at least 90% of methods will function the
same on both the client and server, but it is something to look out for.
What is Locking?
We're all familiar with locking. If you lock your front door then it prevents burglars from walking off
with your TV and video recorder. If you lock your cellar door then it stops your teenage children
drinking your much-valued 1961 Chateaux Petrus! The same applies to records in a data store –
if you lock the records then it prevents other people from changing them.
The locking type is closely related to whether or not the recordset is updateable. If you're using
your recordset to query a data store, then the recordset is a copy of the records that you
requested in the data store. That means that there's one copy of the record in your recordset and
another copy of the record in the data store. Then, updating a record is a two-stage process:
First, you edit the copy of the record that's contained in your recordset
Second, you update your changes to the copy of the record that's in the data store
Here's where the locking comes in: you can choose to lock the data store copy of the record while
you're making your changes – and keep other users from touching them. There are four types of
locking you can use:
Read-only (adLockReadOnly): This gives you a non-updateable recordset. No locking
is performed, since you can't change the data in a read-only recordset. This is the default.
Pessimistic (adLockPessimistic): This gives you an updateable recordset, in which
the lock type is very protective. In this case, the copy of the record that exists in the data
store is locked as soon as you start editing it. This means that no one else can change the
record until you release the lock, which is after you finished editing the record and have
committed the update.
Optimistic (adLockOptimistic): This also gives you an updateable recordset, but the
lock type is a little more carefree. In this case, the copy of the records in the data store
remains unlocked while you're editing your changes within the recordset. The data store
records are only locked when you update your changes. So, if you choose this setting you're
assuming that no one else will edit the record while you are editing it. If this does happen
then the person who commits their update first will "win". The first person will successfully
update the record. The second person's initial state will be checked and found to differ for the
current state of the database and the change will be rejected.
Optimistic Batch (adLockBatchOptimistic): Batch update mode allows you to
modify several records, and then have them updated all-at-once, so this only locks each
record as it is being updated.
Again, we can also set the lock type by using the Recordset object's LockType property:
objRec.LockType = adLockReadOnly
Note Again, you can read the LockType property at any time, but you can only set it
before the recordset is assigned a live connection.
What are Options?
The last attribute in the list is known in the documentation as the Options. This rather unhelpful
description does nothing to explain that this attribute is used to define what type of data source is
being referred to. For example:
objRec.Open "Movies", strConnect, adOpenForwardOnly, adLockReadOnly,
adCmdTable
The source here is a database table called Movies – and so we use the constant adCmdTable
to tell ADO that Movies is a database table.
In other words, the Options parameter tells the recordset what form the data source will take.
Are you using a database table, or a SQL query, or a stored procedure… or perhaps you're not
sure? This parameter allows you to tell ADO how to evaluate the request, and hence enable it to
make the most efficient use of its resources.
The Options parameter doesn't map directly to a property of the Recordset object, in the way
that the cursor type and lock type do. However, it does map onto the CommandText property of
the Command object. We'll be looking at this in the next chapter.
Using the Recordset Object
So now we have a better understanding of some of the recordset's characteristics, we can get to
grips with some of the exciting functionality that makes it really easy to manipulate data. In the
remainder of this chapter, we'll see how many of the Recordset object's methods and properties
work, and we'll see them in action in a series of examples. We'll start with BOF and EOF.
There are two properties whose values provide that information – they're called BOF (beginning-
of-file) and EOF (end-of-file). Both are Boolean values, so each holds a value True or False
which reflects the current position of the cursor.
To understand, let's consider a recordset that contains four records. The following diagram shows
the same recordset six times – the four boxes represent the four records and the gray rectangles
represent the cursor stepping through the records of the recordset. Notice that, in addition to the
four records in our recordset, there are two additional 'positions' (one at the beginning and one at
the end). They aren't records – they're just imaginary 'markers' that represent the extremes of the
recordset:
We've seen already that this can be very useful. We've already used the EOF property as the
control that allows us to step through every record and to stop as soon as we move past the last
one:
While Not objRS.EOF ' now loop through the
records
Response.Write objRS.Fields("Title") & ", "
objRS.MoveNext
Wend
The BOF and EOF properties work just like this, provided there's at least one record in the
recordset. If there are no records in the recordset, then the 'beginning' and 'end' markers coincide
– and therefore the values BOF and EOF are simultaneously True. (Conversely, if you have a
recordset in which BOF and EOF are simultaneously True, then the recordset has no records in
it!)
These methods don't require any arguments. For example, and as we've already seen, we call
the MoveNext method, pure and simple, like this:
objRS.MoveNext
In addition, to these, there is a fifth method, called Move. The Move method allows us to make the
cursor jump over a specified number of records from its current position (or some other specified
position). It has two parameters, and its syntax is as follows:
Alternatively, if we wanted to jump back three records (and if our recordset doesn't have a
'forward-only cursor type!) then we could use this:
objRS.Move -3
Note that the Start parameter is optional. We'll be looking at some possible values for this
parameter when we look at bookmarks, later in this chapter.
Note Note also that the availability of these methods is dependent upon the
recordset type. For example, it's impossible to use MoveFirst or
MovePrevious on a 'forward-only' cursor, because it involves moving the
cursor backwards! In the case of MoveLast to find the last record in a
recordset you actually have to move one beyond the last record in the
recordset, to discover that it is the last record. Bizarrely, some forward-only
recordsets(such as those used by a SQL Server provider) do allow MoveFirst
as they allow the query to be resubmitted to the server. It's best to be careful if
you plan to do a lot of moving around the recordset.
Later in the chapter we'll meet the Find method, which is another useful
technique for moving the cursor around.
So let's try an example in which we'll exercise these methods. This example provides a departure
from the recordsets that we've seen so far, in that it's the first one we've created that doesn't have
a forward-only cursor.
We'll create a recordset that contains film details taken from the AllMovies table of our
database. Then we'll simply step through all the records in the recordset, displaying the values of
each record's TitleID, Director and Title fields as we go.
We'll add a little extra option that allows the user to decide whether they want to step through the
recordset forwards or backwards, and which record they want to start with. For example, if the
user selects 14 and Reverse, we'll begin our list at the 14th film in the database, not the first; then
we'll step through all the films in the recordset backwards, till we reach the first – then we'll jump
to the very last film and step (backwards) through the rest.
In the course of the action, we'll exercise all of the five Move… methods we mentioned above,
and the BOF and EOF properties too.
1. We're going to use the Movies database again, so make sure you've set it up using the
Movie2000.mdb or Movie2000.mdf database file as described in Chapter 12.
2. We also need the connection string details, so ensure that the DataStore.asp file (from
Chapter 12) is saved into the folder \inetpub\wwwroot\BegASPFiles.
3. Open up your editor, create a new file called Moving.asp, and enter the following into it:
4. <%
5. Option Explicit
6. Dim strConnect
7. %>
8. <!-- #include file="DataStore.asp" -->
9. <!-- METADATA TYPE="typelib"
10. FILE="C:\Program Files\Common Files\System\ado\
msado15.dll" -->
11. <HTML>
12. <HEAD>
13. <TITLE>Working your Way round a Recordset</TITLE>
14. </HEAD>
15. <BODY>
16.
17. <%
18. Dim intChosenRecord, strDirection, strOutputString, intCounter,
intNoOfRecords
19. If Request.Form("ChosenRec") <> "" Then
20. intChosenRecord = Request.Form("ChosenRec")
21. strDirection = Request.Form("Dir")
22. Else
23. intChosenRecord = 1
24. strDirection = "Forward"
25. End If
26.
27. Dim objRS
28. Set objRS = Server.CreateObject("ADODB.Recordset")
29. objRS.Open "AllMovies", strConnect, adOpenStatic,
adLockReadOnly, adCmdTable
30. intNoOfRecords = objRS.RecordCount
31. objRS.Move intChosenRecord-1
32. strOutputString = "<TABLE BORDER=1>" & _
33. "<TR><TD WIDTH=""30%""><B>Director</B></TD>" &
_
34. "<TD><B>Film</B></TD></TR>"
35. If strDirection = "Forward" Then
36. While Not objRS.EOF
37. strOutputString = strOutputString & "<TR>" & _
38. "<TD> " & objRS("TitleID") & ": " & objRS("Director")
& "</TD>" & _
39. "<TD>" & objRS("Title") & "</TD>" & _
40. "</TR>"
41. objRS.MoveNext
42. Wend
43. objRS.MoveFirst
44. For intCounter = 1 To intChosenRecord-1
45. strOutputString = strOutputString & "<TR>" & _
46. "<TD> " & objRS("TitleID") & ": " & objRS("Director")
& "</TD>" & _
47. "<TD>" & objRS("Title") & "</TD>" & _
48. "</TR>"
49. objRS.MoveNext
50. Next
51. Else
52. While Not objRS.BOF
53. strOutputString = strOutputString & "<TR>" & _
54. "<TD> " & objRS("TitleID") & ": " & objRS("Director")
& "</TD>" & _
55. "<TD>" & objRS("Title") & "</TD>" & _
56. "</TR>"
57. objRS.MovePrevious
58. Wend
59. objRS.MoveLast
60. For intCounter = intNoOfRecords To intChosenRecord+1 Step -1
61. strOutputString = strOutputString & "<TR>" & _
62. "<TD> " & objRS("TitleID") & ": " & objRS("Director")
& "</TD>" & _
63. "<TD>" & objRS("Title") & "</TD>" & _
64. "</TR>"
65. objRS.MovePrevious
66. Next
67. End If
68. strOutputString = strOutputString & "</TABLE>"
69. objRS.Close
70. Set objRS = Nothing
71. Response.Write strOutputString
72. %>
73.
74. <BR><HR>
75. <FORM ACTION="Moving.asp" METHOD="POST">
76. <H2>Format the list!</H2>
77. Where do you want ths list to begin? Record
78. <SELECT SIZE=1 NAME="ChosenRec">
79. <%
80. For intCounter=1 To intNoOfRecords
81. Response.Write "<OPTION VALUE=" & intCounter & ">" &
intCounter & "</OPTION>"
82. Next
83. %>
84. </SELECT><BR><BR>
85. Do you want the records to be listed
86. in <INPUT TYPE="RADIO" NAME="Dir" VALUE="Forward" CHECKED><B>
forward</B></INPUT>
87. or <INPUT TYPE="RADIO" NAME="Dir" VALUE="Reverse">
<B>reverse</B></INPUT>
88. order (select one)?
89. <INPUT TYPE="SUBMIT" VALUE="View the list"></INPUT>
90. </FORM>
91. </BODY>
92. </HTML>
93. Save Moving.asp into the same folder: \inetpub\wwwroot\BegASPFiles.
94. Now view the page Moving.asp in your browser. You'll see a long table, listing all 330
records in order from 1 to 330 (you can see the last few rows of the table in the following
screenshot). At the end, you'll see the form that allows us to choose where to start the
listing, and in what order:
95. Select a record from the drop-down list and select either Forward or Reverse. Then click on
View the List, which refreshes the page according to your options:
Try it a few times, just to get a feeling for how the example works. The numbers on the left of the
table rows are the record numbers as they appear in the database and in the table – we've
displayed them here so you can see how the cursor is moving around the recordset.
This is quite a long example, but if you examine the code you'll see that it's not complicated. Let's
start with the very last part of the code, by getting the form out of the way. The form is really very
simple: it asks the user for two values. First, it asks for an integer which will specify which record
appears first in our table, when we submit to refresh the page:
<FORM ACTION="Moving.asp" METHOD="POST">
<H2>Format the list!</H2>
Where do you want ths list to begin? Record
<SELECT SIZE=1 NAME="ChosenRec">
<%
For intCounter=1 To intNoOfRecords
Response.Write "<OPTION VALUE=" & intCounter & ">" & intCounter &
"</OPTION>"
Next
%>
</SELECT><BR><BR>
When we submit the form, this will create an entry in the Request.Form collection, called
ChosenRec. Its value is taken from the value selected in the drop-down list. We've created the
drop-down list using ASP – creating one line of the list for each integer between 1 and
intNoOfRecords. The variable intNoOfRecords was populated earlier on in the page (when
the recordset was open), by using the Recordset object's RecordCount property:
intNoOfRecords = objRS.RecordCount
This creates another variable in the Request.Form collection, called Dir. If we select Forward,
then the cursor will move forwards through the recordset; If we select Reverse, then the cursor will
move backwards through the recordset.
OK, assuming we've made those choices, what happens when we submit them? First, we
#INCLUDE the DataStore.asp file that contains our connection string details, reference the
ADO constant library msado15.dll, and we create the page head:
<%
Option Explicit
Dim strConnect
%>
<!-- #include file="DataStore.asp" -->
<!-- METADATA TYPE="typelib"
FILE="C:\Program Files\Common Files\System\ado\
msado15.dll" -->
<HTML>
<HEAD>
<TITLE>Working your Way round a Recordset</TITLE>
</HEAD>
Now we can set the variables that we'll need. The intChosenRecord and strDirection
variables are used to contain the values passed into the page from the HTML form (and found in
the Request.Form collection):
<BODY>
<%
Dim intChosenRecord, strDirection, strOutputString, intCounter,
intNoOfRecords
If Request.Form("ChosenRec") <> "" Then
intChosenRecord = Request.Form("ChosenRec")
strDirection = Request.Form("Dir")
Else
intChosenRecord = 1
strDirection = "Forward"
End If
Note that the first time you call the page, you don't get a chance to fill in a form – so we set
default values for the intChosenRecord and strDirection variables.
Note The default behavior, as you probably noticed the first time you called the page,
is to start at the first record and iterate in a forward direction.
Let's look more carefully that the five parameters we've chosen here. Since we studied them
earlier in the chapter, they should now be a little less mysterious to you:
The first parameter specifies the AllMovies table of the database
The second parameter is the strConnect connection string which is specified in the
DataStore.asp SSI, and which specifies the location of the database
The third parameter specifies the value adOpenStatic, which requests a static cursor
type. This will allow the forward and backward movement through the recordset that we
need.
Note We could have selected adOpenDynamic or adOpenKeyset – but we're
not interested in viewing changes made to records (either by ourselves or
by other users of the database) so neither are necessary.
The fourth parameter specifies the value adLockReadOnly, which specifies that we
can't use this recordset to make changes to the database. That's fine, because we're only
using it to read data from the database
The fifth parameter specifies the value adCmdTable, which tells ADO that the data
source specified in the first parameter is a database table
Once the recordset is open and contains the necessary data, we can begin to write it all to the
browser. We can build a table to display all the film details. In this example we built a string called
strOutputString, over a sequence of statements, which contains all the HTML for the entire
table. When we've finished writing the string, we Response.Write the whole thing in one go.
So here goes. The first thing to do is position the cursor on the record that will be the first to
appear in the table:
objRS.Move intChosenRecord-1
Now we write the body of the table into our string. The overall structure of this part is as follows:
strOutputString = "<TABLE BORDER=1>" & _
"<TR><TD WIDTH=""30%""><B>Director</B></TD>" & _
"<TD><B>Film<B></TD></TR>"
If strDirection = "Forward" Then ' iterate forward through
the RS
' write one table row for each record from the chosen record to the
end
' jump to the beginning of the recordset
' write one line for each record from the beginning to the chosen
record
Else ' iterate backward through
the RS
' write one table row for each record from the chosen record to the
beginning
' jump to the end of the recordset
' write one line for each record from the end to the chosen record
End If
strOutputString = strOutputString & "</TABLE>"
Let's fill in the gaps here. If we're iterating forwards through the recordset, we do so using the
MoveNext method. When we get to the end of the recordset, objRS.EOF becomes True. At this
stage, we jump to the first record using the MoveFirst method and then iterate through the
remaining records using MoveNext again:
While Not objRS.EOF
strOutputString = strOutputString & "<TR>" & _
"<TD> " & objRS("TitleID") & ": " & objRS("Director") &
"</TD>" & _
"<TD>" & objRS("Title") & "</TD>" & _
"</TR>"
objRS.MoveNext
Wend
objRS.MoveFirst
For intCounter = 1 To intChosenRecord-1
strOutputString = strOutputString & "<TR>" & _
"<TD> " & objRS("TitleID") & ": " & objRS("Director") &
"</TD>" & _
"<TD>" & objRS("Title") & "</TD>" & _
"</TR>"
objRS.MoveNext
Next
On the other hand, if we're iterating backwards through the recordset then we do things the other
way around. Iterate from the chosen records, backwards towards the first record, using the
MovePrevious method. When we get past the first record, objRS.BOF becomes True. Then,
we jump to the last record using the MoveLast method and then iterate backwards through the
remaining records using MovePrevious again:
While Not objRS.BOF
strOutputString = strOutputString & "<TR>" & _
"<TD> " & objRS("TitleID") & ": " & objRS("Director") &
"</TD>" & _
"<TD>" & objRS("Title") & "</TD>" & _
"</TR>"
objRS.MovePrevious
Wend
objRS.MoveLast
For intCounter = intNoOfRecords To intChosenRecord+1 Step -1
strOutputString = strOutputString & "<TR>" & _
"<TD> " & objRS("TitleID") & ": " & objRS("Director") &
"</TD>" & _
"<TD>" & objRS("Title") & "</TD>" & _
"</TR>"
objRS.MovePrevious
Next
And that's just about all there is to it. All that remains is to tidy up the recordset, since we don't
need it anymore:
objRS.Close
Set objRS = Nothing
And finally, we display the HTML form (which we covered at the beginning of this explanation).
Bookmarks
A bookmark is really quite intuitive – it's a way of marking a particular record in the recordset. In
fact it uniquely identifies a record within the recordset so that you can make your cursor jump
straight back to it, from any other place in the recordset.
A bookmark is stored as a variant value (although you'll probably never want to view the
bookmark itself, as its value is really only meaningful to ADO). There are several important facts
to note about bookmarks:
It's possible to have two bookmarks that point at the same record but have different
values. This means that you can't compare bookmarks directly.
You can create two similar or identical recordsets from the same source, and set a
bookmark on the same record of each recordset. But the bookmarks are not the same
Some types of recordset don't support bookmarks. You can find out whether your
recordset supports bookmarks by using the Supports method (the expression
objRS.Supports(adBookmark) returns a Boolean value)
Using Bookmarks
The following section of code demonstrates how we might use bookmarks:
Dim varMyBookmark ' create a variant to hold the
bookmark
... ' code to create the Recordset object
varMyBookmark = objRS.Bookmark ' later, set a bookmark at the
current record,
' and save it to varMyBookmark
... ' some processing of records
objRS.Bookmark = varMyBookmark ' later still, move the cursor to
the record
' specified in the varMyBookmark
bookmark
First, this creates a variant called varMyBookmark. Later, we use the Bookmark property to set
a bookmark at the current cursor position – the bookmark is stored in the varMyBookmark
variant. Later still, we want to return the cursor to the bookmarked record – so we set the
recordset's Bookmark property back to the value contained in varMyBookmark.
This moves the cursor to the third record after the record bookmarked by varMyBookmark. Here,
we're using the Move method's second parameter to specify the starting point for the move, and
the first parameter to specify the offset. So, effectively this says " move the cursor forwards three
places, using the record bookmarked by varMyBookmark as a starting position".
As we saw earlier, we can use the Move method without specifying the second parameter – in
that case, the Move method adopts its default behavior of moving the cursor from its current
position. There are some other special predefined bookmarks that you can use in the second
parameter:
Value Meaning
adBookmarkCurrent Use the current record as the starting position (this is the
default)
adBookmarkFirst Use the first record as the starting position
adBookmarkLast Use the last record as the starting position
Note Note that we don't need to create these
bookmarks. These values are ADO
constants, and are predefined in the
ADO constant library msado15.dll.
For example, to move three records further on from the current position you would use this:
objRS.Move 3, adBookmarkCurrent
or this:
objRS.Move 3
So, to move to the fourth record in the recordset we'd use the following:
objRS.Move 3, adBookmarkFirst
Finding Records
We've seen how we can move the cursor around the recordset using the Move, MoveNext,
MoveLast, MovePrevious and MoveFirst methods. But what happens if we're stepping
through a big recordset, with thousands of records, and the record we're looking for is right in the
middle? Instead of iterating through using MoveNext or MovePrevious hundreds of times,
wouldn't it be much easier if we could use a single instruction to take the cursor straight there?
That's exactly what the Recordset object's Find method can do for us. The Find method is a
full table scan, so with large recordsets it can be rather slow – but it is functional. Its job is to
move the cursor from its current position to a record that fits our description.
Note We'll see in the next chapter an even more effective way of retrieving
information, using SQL.
We'll see the Find method in action shortly, but first we should explain what the parameters are
for. In fact, they're fairly straightforward:
Criteria is a string that contains a set of comparisons, which describe the record that
we're looking for
SkipRecords is the offset from the start position where the search itself should start
(see also Start, below). Default is 0
SearchDirection can be set either to adSearchForward or adSearchbackward, to
specify the search direction. Default is adSearchForward
Start is a bookmark that specifies the start position of the search. Then the parameter
SkipRecords specifies the offset from this position. Default is adBookmarkCurrent (to
start the search at the current record)
So in essence, the Find method starts at the record denoted by Start; skips the number of
records specified by SkipRecords; then it searches in the direction specified by
SearchDirection, and moves the recordset's cursor to the first record it finds that matches the
Criteria.
The first parameter, Criteria, is the most important one here, and if you've ever worked with
SQL (Structured Query Language) then you'll be familiar with the way we use it. The general
form of a criterion is shown below:
Here, recordset_field is the name of the field that we want ADO to search against,
comparison_operator is the type of search, and value is the value that we're looking for.
For example, if we want to search for a film whose title is Pulp Fiction, we could use the
criterion Title = 'Pulp Fiction'. Thus, if we search the recordset objRS for this film,
searching forwards from the current record, we could use a line like this:
objRS.Find "Title = 'Pulp Fiction'"
Note Notice that we've left out the last three arguments, since they are optional – this
causes the default parameter values to be used. The default behavior is to start
the search at the current record, and search forwards through the recordset.
In the example above, we're searching for a string, 'Pulp Fiction' (the comparison isn't case
sensitive). In this case, we must enclose the search string in single quotation marks. You don't
need to do this for fields whose data type is numeric:
objRS.Find "Price = 34.95"
And when searching for dates in an Access database, you should surround the date with # marks
(single quotes for MSDE):
objRS.Find "Birthday = #10/23/98#" ' Note that you need to use single
quotes
' in places of hashes in MSDE
When searching for strings you can also use the SQL keyword LIKE to specify matching records.
For example, the following will find the next film in the database whose title begins with the word
Pulp:
objRS.Find "Title LIKE 'Pulp*'"
We're looking for any string that begins with the characters Pulp, and then continues with any
number of any other characters at all. The * character indicates that we don't mind what comes
after those first specified characters – the * is known as a wildcard. If you are using a database
other than Access, MSDE (or full SQL Server) then the syntax may vary slightly. For example,
Oracle uses a % character as the wildcard (but other than that the method should work in the
manner described).
Consequently, you must be aware of the fact that the position of the cursor will change – even if
the search is unsuccessful. So you might want to use a bookmark to keep a note of the current
record, like this:
varTempBkMk = objRs.Bookmark ' original position
objRS.Find "Title LIKE 'Pulp*'"
If objRS.EOF Then
Response.Write "No records found - " & _
"now moving the cursor back to its original position"
objRS.Bookmark = varTempBkMk
End If
Alternatively, if the director isn't found in the database, then you'll get the appropriate
message.
How It Works
The first page, PromptForDirector.htm, is a pure HTML page that contains a form. It collects
the name of a director that the user types in:
<FORM ACTION="FindDirector.asp" METHOD="POST" id=form1 name=form1>
Type in the name of a film director: <BR>
<INPUT TYPE="TEXT" NAME="director" SIZE=50><BR><BR>
<INPUT TYPE="submit" NAME="send" VALUE="What films did this director
direct?">
</FORM>
We've specified the POST method in the form. So when the user clicks the submit button, the
requested director's name is sent as part of the request for the FindDirector.asp page, where
it finishes up in the director variable of the Request.Form collection. We capture this value in
a local variable, strDirector:
strDirector = Request.Form("director")
In FindDirector.asp, we specify the connection string (within the SSI, DataStore.asp) and
then use it to create and open a recordset:
Dim objRS
Set objRS = Server.CreateObject("ADODB.Recordset")
objRS.Open "AllMovies", strConnect, adOpenStatic, adLockReadOnly,
adCmdTable
Note This is the same recordset, cursor type and lock type that we used in the
Moving.asp example earlier in the chapter. We're using a static cursor,
instead of a forward-only cursor, because some data providers don't support
the Find method for recordsets with a forward-only cursor.
The purpose of the first line is to create the criteria that the Find method will use to search for a
matching record. We're using a string, strCriteria, to store these criteria. After this line, the
strCriteria variant should contain a string such as Director='Quentin Tarantino'.
The second line is the Find method call. For the search criteria, we specify the strCriteria
string in the first parameter. We've left the remaining parameters blank. This means that the
search will begin at the current record (which is the first record in the recordset, since we've only
just opened it) and the search direction is forward.
Now we can examine the results of the search. By checking the value of the EOF property, we
can establish whether the search was successful. If no matching records were found, then the
Find method places the cursor at the end-of-recordset, so EOF is True:
If objRS.EOF Then
Response.Write "The database does not contain any films by the
director " & _
strDirector
Otherwise, the search was successful – which means that the Find method found a matching
record, changed the cursor to point to that record and then stopped searching. We can use this to
write the data in that record to the browser:
Else
Response.Write "<H2>Directed by " & strDirector & ":</H2>" & _
"<B>Title:</B> " & objRS("Title") & "<BR>" & _
"<B>Summary:</B> " & objRS("Summary") & "<BR>" & _
"<B>Genre:</B> " & objRS("Genres") & "<BR><BR><BR>"
End If
And that's it. Note that the Find method is just a tool for pointing the cursor at a particular record.
If you want to use the Find method to find lots of records, you have to call the Find method lots
of times. For example, if you wanted to extend this example to find two relevant records, you'd
need to call the Find method twice:
objRS.MoveNext
objRS.Find strCriteria
Or equivalently:
objRS.Find strCriteria, 1
The MoveNext is important. Having found the first successful record, you'd need to point the
cursor away from it before searching for the next suitable record. That's because the default
behavior of the Find method is to start searching at the current record (which you wouldn't want,
because you would already have found the current record!). So, the sensible thing to do is move
the cursor on one.
If you want to point the cursor at a record that satisfies a specific criterion, then the Find method
is a good way to do it. But if you want to find lots of records that match the same criterion, then
the Find method isn't always ideal. Instead, you can use your search criterion in conjunction with
the Recordset object's Filter property, as we'll see next.
Filtering Records
Filtering involves identifying all the records in the recordset that meet a certain specified
criterion. While you'd expect filtering to be quite similar to finding, ADO considers these two tasks
to be very different. Accordingly, ADO implements these two concepts very differently:
Finding a record involves searching the recordset until we find one record that matches
what we're looking for – then pointing the cursor to that record. Therefore, Find is a method,
whose job is to point the cursor at a specific record
Filtering involves identifying every record that matches what we're looking for. Therefore,
Filter is a property, which specifies exactly which records are visible to our code and
which are invisible
That's right – by setting the Recordset object's Filter property, you can hide some of the
records in the recordset and ensure that the only visible records are the records that satisfy the
criterion.
For example, consider a recordset of movie data from the AllMovies table. By default, the
Filter property is set to the ADO constant adFilterNone, which means that the filter is turned
off – no records are filtered out, and all records in the recordset are visible. Now, we can
implement a filter by setting the recordset's Filter property with our criterion. As an example,
let's filter out all the records of films that weren't directed by Quentin Tarantino:
objRS.Filter = "Director = 'Quentin Tarantino'"
Now, the only visible records are those that were directed by Mr. Tarantino. Now, when you use
methods like MoveNext and MovePrevious, you'll be moving between the records that aren't
hidden by the filter.
Note that the other records in the objRS recordset haven't been deleted from the recordset –
they're still there. It's just that they're hidden. You can reveal them again by removing the filter:
objRS.Filter = adFilterNone
The filter can also take the form of an array of bookmarks, or one of the following ADO constants:
Constant Meaning
adFilterNone Removes the current filter and restores all records to view.
adFilterPendingRecords Shows only those records that have changed but whose
changes have yet to be sent to the data store. This is only
applicable in batch update mode (more on this in the next
chapter).
adFilterAffectedRecords Shows only records affected by the last Delete, Resync,
UpdateBatch or CancelBatch call. (We'll discuss these
calls in the next chapter.)
adFilterFetchedRecords Shows the records from the last call to retrieve records
from the data store.
Let's have a look at an example. We're going to break down our list of films, categorizing them by
the first letter of the film title. In order to do this, we're going to query the data store just once.
Then, for each letter in the alphabet, we'll use the appropriate filter to hide all records except
those whose film title begins with that letter. If we do that 26 times, we'll have covered every letter
in the alphabet!
1. Once again, we're going to use the Movies database, so make sure you've set it up
using the Movie2000.mdb or Movie2000.mdf database file as described in Chapter 12.
2. And again, we'll need the connection string details, so ensure that the DataStore.asp
file (from Chapter 12) is saved into the folder \inetpub\wwwroot\BegASPFiles.
3. Create a new file, called Filter.asp, and add the following code to it:
4. <%
5. Option Explicit
6. Dim strConnect
7. %>
8. <!-- #include file="DataStore.asp" -->
9. <!-- METADATA TYPE="typelib"
10. FILE="C:\Program Files\Common Files\System\ado\
msado15.dll" -->
11. <HTML>
12. <HEAD>
13. <TITLE>Filtering the Recordset</TITLE>
14. </HEAD>
15. <BODY>
16. <%
17. Dim objRS, intLetter, strChar, strCriteria
18. Set objRS = Server.CreateObject("ADODB.Recordset")
19. objRS.Open "AllMovies", strConnect, adOpenStatic,
adLockReadOnly, adCmdTable
20.
21. For intLetter = 1 To 26
22. strChar = Chr(intLetter+64)
23. strCriteria = "Title LIKE '" & strChar & "*'"
24. objRS.Filter = strCriteria
25. If Not objRS.EOF Then
26. Response.Write "<H2>" & strChar & "</H2>" & _
27. "<TABLE
BORDER=1><TR><TD><B>Film</B></TD><TD><B>Director</B></TD></TR>"
28. While Not objRS.EOF
29. Response.Write "<TR><TD>" & objRS("Title") & "</TD>" & _
30. "<TD>" & objRS("Director") &
"</TD></TR>"
31. objRS.MoveNext
32. Wend
33. Response.Write "</TABLE>"
34. End If
35. Next
36. objRS.Close
37. Set objRS = Nothing
38. %>
39. </BODY>
40. </HTML>
41. Save the Filter.asp file into the \inetpub\wwwroot\BegASPFiles folder. Now
browse to that page, to see the categorization of films.
As you can see, we get a number of tables – but they're all populated from the same recordset!
For each table, all we've done is hide the records that we don't want, and put the rest into the
table. Let's have a look at how easy it is.
You can see that the filter has categorized the records in the recordset.
How It Works
We won't go through the connection string and recordset creation code again – you should be
familiar with that by now. Suffice it to say that we only use one Recordset object in this
example, and it only queries the database once. That one query gives us enough data to
populate all of these tables.
All the interesting action occurs within the For … Next loop. We're going to loop through the
same procedure 26 times, once for each letter of the alphabet. We use a variant called strChar
to hold one of the letters of the alphabet and then build the filter criterion from that letter:
For intLetter = 1 To 26
strChar = Chr(intLetter+64)
strCriteria = "Title LIKE '" & strChar & "*'"
Like the last example, we're using a variant called strCriteria to store the string containing
our filter criteria. In this example, in each case the criteria will look something like this:
Title LIKE 'A*'
Note that, once again, we're using our wildcard character (*). This criterion will make visible all the
records whose film titles begin with the letter A, and will hide all other records.
Now we can test the results of the filter. When you apply a filter, the recordset automatically
points the cursor at the first visible record in the filtered recordset. If there are no visible records in
the filtered recordset, then the EOF property will be True – in that case we don't display anything.
Otherwise, we display the visible contents of the recordset, by using MoveNext to step through
the records as we've done in previous examples:
If Not objRS.EOF Then
Response.Write "<H2>" & strChar & "</H2>" & _
"<TABLE BORDER=1><TR><TD><B>Film<B></TD><TD>Director</TD></TR>"
While Not objRS.EOF
Response.Write "<TR><TD>" & objRS("Title") & "</TD>" & _
"<TD>" & objRS("Director") & "</TD></TR>"
objRS.MoveNext
Wend
Response.Write "</TABLE>"
End If
Having written the table, we move onto the next letter in the alphabet:
Next
Notice that we don't need to reset the filter before applying the next filter – we can just replace the
old value of the Filter property with the new value.
But what if you want to use all the fields in a recordset, perhaps to build a table from the
recordset? Well, we could refer to each field individually in the same way, but this becomes
tedious after a while – especially if there are lots of fields in the recordset. For a start, the task of
typing out these field names individually is labor-intensive; moreover, it makes for excessively
long and potentially untidy code.
We all want an easy life. So, let's introduce the Fields collection, which can skim hours off our
development time.
Note While using the Fields collection is fine, if we want to save coding time, you
can't use aliases for the field names. If the field names are cryptic or written in
shorthand notation, like many databases, then those exact names will be
returned when the Fields collection is used.
Each Recordset object has its own Fields collection, which contains an entry for each field in
the current record. That means that you can iterate through each field in the fieldset without
knowing what the names of the fields are. So, for example, we can use this to display the value of
each field with just a few short lines. Alternatively, we can actually use the Fields collection to
find out the names of the fields that are contained in our recordset.
Aha, now we're talking. Let's use this to create a central reusable routine, which will generate an
HTML table showing all the data contained in a recordset.
We're going to create a routine, called RecToTable(), which generates the HTML for a table
containing all the data in a recordset. We'll store this routine in its own file, RecToTable.asp –
then, we'll be able to use it within any ASP page that we write, simply by including
RecToTable.asp as a server-side include (SSI). To demonstrate, we'll write a second page,
called StaffTable.asp, which uses the RecToTable() routine to show the values of all the
fields in all the records of an obscure little table called Staff.
1. We're going to use the Movies database and the strConnect connection string again,
so you'll need to ensure they're set up as for previous examples in this chapter.
2. The first thing to do is write the RecToTable()routine. Create a new file called
RecToTable.asp, and add the following code to it (don't worry about all the new stuff;
we'll look at it after we've seen it working):
3. <%
4. Function RecToTable (objRS)
5. Dim strT ' table html string
6. Dim fldF ' current field
object
7. strT = "<TABLE BORDER=1><TR ALIGN=CENTER>" ' build the table
header
8.
9. For Each fldF In objRS.Fields ' each field as a
table column name
10. strT = strT & "<TD>" & fldF.Name & "</TD>"
11. Next
12. strT = strT & "</TR>"
13.
14. While Not objRS.EOF ' now build the rows
15. strT = strT & "<TR ALIGN=CENTER>"
16. For Each fldF In objRS.Fields ' loop through the
fields
17. strT = strT & "<TD>" & fldF.Value & "</TD>"
18. Next
19. strT = strT & "</TR>"
20. objRS.MoveNext
21. Wend
22. strT = strT & "</TABLE>"
23. RecToTable = strT ' and finally return
the table
24. End Function
25. %>
26. Save the RecToTable.asp file in the \inetpub\wwwroot\BegASPFiles directory.
27. Now create another new file, called StaffTable.asp, and enter the following code into
it:
28. <%
29. Option Explicit
30. Dim strConnect
31. %>
32. <!-- #include file="DataStore.asp" -->
33. <!-- METADATA TYPE="typelib"
34. FILE="C:\Program Files\Common Files\System\ado\
msado15.dll" -->
35. <!-- #INCLUDE FILE="RecToTable.asp" -->
36. <HTML>
37. <HEAD>
38. <TITLE>ADO Fields Collection</TITLE>
39. </HEAD>
40. <BODY>
41. <%
42. Dim objRS
43. Set objRS = Server.CreateObject ("ADODB.Recordset")
44. objRS.Open "Staff", strConnect, adOpenStatic, adLockReadOnly,
adCmdTable
45.
46. Response.Write RecToTable(objRS) ' pass the recordset to the
table function
47. objRS.Close
48. Set objRS = Nothing
49. %>
50. </BODY>
51. </HTML>
52. Save the StaffTable.asp file into the \inetpub\wwwroot\BegASPFiles directory.
53. Start up your browser, and view the page StaffTable.asp.
It's amazing, don't you think? Remember that this is actually produced by an SSI file, so you can
include this in any ASP file where you need a table like this. Let's look at how it works.
How It Works
Let's look at RecToTable.asp first, since it's the one that does the work.
First we declare our function. The RecToTable() function will take a single argument, namely
the recordset from which it will build the table. It needs to be a function because after it has
generated a string containing all the HTML tags and text that make up the table, it will return that
string to the calling routine:
<%
Function RecToTable (objRS)
Next we declare a couple of variables. The first will hold the HTML tags that will make up the
table, and the second will hold an ADO Field object:
Dim strT ' table html string
Dim fldF ' current field object
Now we can really start. We'll begin the HTML string by writing the <TABLE> tag, and the first
<TR> tag for the table header:
strT = "<TABLE BORDER=1><TR ALIGN=CENTER>" ' build the table
header
Remember how, in Chapter 12, we used a For Each … Next loop to iterate through all the
properties in the Properties collection? Well, this is the same. We are looping through the
Fields collection, and using For Each … Next will set fldF to a different Field object each
time we go round the loop. The expression fldF.Name refers to the name of the field. So this
code creates one table cell for each field in the Fields collection, surrounding each field name
with the table cell start and end tags, like so:
<TD>MovieID</TD><TD>StaffName</TD><TD>StaffRole</TD><TD>Note</TD>
Note In a moment we'll use the expression fldF.Value to refer to the value contain
in that field, for the current record.
Once that loop has finished, we can terminate the table header:
strT = strT & "</TR>"
Now we've finished the header line we can start on the rows of data. For each record, we need to
iterate through each Field object in the Fields collection, capture the value of that field (using
fldF.Value) and adding this value to the body of the HTML table. We're going to iterate through
all the records in the recordset, and for each record we'll iterate through the Fields collection –
so this involves a nested loop.
We can start looping through the records, using a While … Wend loop as we've used before,
checking for EOF:
While Not objRS.EOF ' now build the rows
For each record, we need a row in the table, so we add the row tag to the output string:
strT = strT & "<TR ALIGN=CENTER>"
Now we iterate through the fields, adding each field's value (for this record) as a cell in the table.
This is where we use the fldF.Value expression that we mentioned above – the Value
property of the Field object is what holds the value of this field for this record:
For Each fldF In objRS.Fields ' loop through the fields
strT = strT & "<TD>" & fldF.Value & "</TD>"
Next
That takes care of all the fields for one record, so we add the row terminator tag, and move onto
the next record:
strT = strT & "</TR>"
objRS.MoveNext
Wend
And when the While … Wend loop has ended we can terminate the table and return the string-of-
tags-and-text bag to our calling routine:
strT = strT & "</TABLE>"
RecToTable = strT ' and finally return the
table
End Function
%>
So this is actually quite simple. We just loop through the records, and for each record we loop
through the fields. Now let's look at how we test this out in the calling routine, StaffTable.asp.
We have the usual #INCLUDE and METADATA commands, but this time we also have a new
#INCLUDE directive for the RecToTable.asp SSI file that we've just created:
<!-- #INCLUDE FILE="RecToTable.asp" -->
The creation of the recordset is much the same as we've seen before:
Dim objRS
Set objRS = Server.CreateObject ("ADODB.Recordset")
objRS.Open "Staff", strConnect, adOpenStatic, adLockReadOnly,
adCmdTable
The recordset, objRS that we've just created contains the data from the Staff table of the
movies database. Now here's the new bit: we use Response.Write to write the contents of the
generated string to the HTML stream:
Response.Write RecToTable(objRS) ' pass the recordset to the table
function
When we call RecToTable(), we pass the recordset objRS that we've just created as a
parameter to the function. As we know, RecToTable() uses this recordset to generate a
character string containing all the HTML for the table, and returns this character string on its
completion. The returned character string is then sent straight to the HTML stream (and hence to
the browser) via the Response.Write method.
That's it. You can include RecToTable.asp in any file and use it to create a table.
Note The sample files (available for download from http://www.wrox.com)
contain an extra file, RecToTableFormat.asp; this file is much the same the
same as RecToTable.asp, but has the added bonus that it formats the values
according to their types. So a currency value, for example, is formatted with the
currency sign, etc.
Arrays of Rows
We'll look at one more method of the Recordset object in this chapter. The GetRows method
returns a multi-dimensional array, which contains the data of whatever fields you specify, from a
specified number of records in the recordset. Its syntax is:
All three parameters are optional. In its simplest form, you can use the GetRows method in a line
like this:
varMyArray = objRS.GetRows
In this case, GetRows will adopt its default behavior – which is to returns an array containing all
field values, for each record between the current record and the last record inclusive. When all of
the rows are returned, then the cursor is set to point to the end of the recordset and EOF is set.
Let's look at the three parameters:
The Rows parameter allows us to restrict the number of records that will be returned in
the array. You can specify an integer here, or you can use the constant adGetRowsRest to
return all the rows including and subsequent to the current record. (Default is
adGetRowsRest.)
The Start parameter is a bookmark (or one of the adBookmark constants shown
earlier), which allows us to specify which record to start on. (Default is
adBookmarkCurrent.)
The Fields parameter represents the fields whose values are to be placed in the array.
Fields can be a single field name, or a single ordinal value (i.e. an integer reflecting the
position of the field within the Fields collection). Alternatively, it can be an array of field
names or an array of ordinal values. (Default is 'all fields'.)
The array that GetRows returns is sized automatically to fit the number of fields and records
returned by the method.
OK, let's give it a try. We'll create a recordset of data, and then use it to generate an array of data
(via the GetRows method). Then, just to prove that it works, we'll close the recordset and display
the data using the array.
1. We're going to use the Movies database and the strConnect connection string again
so you'll need to ensure they're set up as for previous examples in this chapter.
2. Start up your editor, and enter the following code for the file GetRows.asp:
3. <%
4. Option Explicit
5. Dim strConnect
6. %>
7. <!-- #include file="DataStore.asp" -->
8. <!-- METADATA TYPE="typelib"
9. FILE="C:\Program Files\Common Files\System\ado\
msado15.dll" -->
10. <HTML>
11. <HEAD>
12. <TITLE>ADO GetRows Method</TITLE>
13. </HEAD>
14. <BODY>
15.
16. <%
17. Dim objRS ' recordset object
18. Dim avarFields ' array of fields
19. Dim intRow ' current row
20. Dim intCol ' current column
21. Dim intLastRow ' ordinal of last row in the array
22. Dim intLastCol ' ordinal of last column in the array
23.
24. ' create and open the recordset object
25. Set objRS = Server.CreateObject ("ADODB.Recordset")
26. objRS.Open "Movies", strConnect, adOpenKeyset, adLockReadOnly,
adCmdTable
27. ' get the data
28. avarFields = objRS.GetRows
29.
30. ' now close the recordset and clean up
31. objRS.Close
32. Set objRS = Nothing
33.
34. ' now use the array to display thre data
35. intLastCol = UBound(avarFields, 1)
36. intLastRow = UBound(avarFields, 2)
37. For intRow = 0 To intLastRow ' loop through the
array
38. For intCol = 0 To intLastCol
39. Response.Write avarFields(intCol, intRow) & " "
40. Next
41. Response.Write "<BR>"
42. Next
43. %>
44. </BODY>
45. </HTML>
46. Save the GetRows.asp file in the \inetpub\wwwroot\BegASPFiles folder.
47. View the page from your browser:
As you can see this just displays every field in every row of the Movies table.
How It Works
We'll skip past the (now familiar) header code, and the variable declarations. The recordset is
created much as before:
Set objRS = Server.CreateObject ("ADODB.Recordset")
objRS.Open "Movies", strConnect, adOpenKeyset, adLockReadOnly,
adCmdTable
Now we call the GetRows method. We're not passing in any arguments, so this will extract all
records into our arrray:
avarFields = objRS.GetRows
Now we've captured the data in the array, we don't need the recordset anymore, so we can get
rid of it:
objRS.Close
Set objRS = Nothing
Now we'll display the data, directly from the array. We use the VBScript function, UBound() to
find out how many elements there are in each dimension of the array (the first dimension of the
array is columns, the second is rows):
intLastCol = UBound(avarFields, 1)
intLastRow = UBound(avarFields, 2)
And then we can iterate through both dimensions in a nested loop, printing out the values:
For intRow = 0 To intLastRow ' loop through the array
For intCol = 0 To intLastCol
Response.Write avarFields(intCol, intRow) & " "
Next
Response.Write "<BR>"
Next
You might be wondering why we didn't use the GetRows method in RecToTable() to create the
HTML table. There are two reasons for this:
First, RecToTable() generated a table header with the names of the fields; the
GetRows method can't help us with that, because the array that it generates doesn't contain
the field names (it only contains the field values for each record)
Second, the array takes up memory – since we already have the fields and values in the
recordset, what's the point of copying them all to an array and then copying them out to the
table? In the RecToTable example, we just created the table straight from the recordset
When are the best occasions to use the GetRows method? Well, it's probably not a good idea to
use it for large recordsets, but for small ones, or even small sets of data from a large recordset,
it's fine. You may find that you need to use this method when you end up with a recordset cursor
that is not scrollable. For example, if you are using ADO and Oracle and execute a parameterized
query from the Command object, the recordset cursor provided is read-only and totally non-
scrollable. How, then, do you navigate backwards and forwards through the records? The answer
is that you extract the rows into an array using GetRows and navigate through the array instead.
Another occasion when you may want to use this method is with custom functions or routines that
accept an array as a parameter, but couldn't cope with a recordset.
Summary
We've covered an awful lot of ground in this chapter – looking at the most important aspects of
the ADO Recordset object in the process. We've understood the concepts of a record, a field
and a recordset in their broader senses, and seen how ADO models the recordset using a
Recordset object. We can create a Recordset object using the Server.CreateObject
method. One way of populating the recordset with data is to use its Open method – to connect it
directly to a data store and grab some data. We've also seen much of the functionality that comes
with an ADO recordset:
We use a cursor to find our way around the recordset. The cursor always points to one of
the records in the recordset, and there are four different types of cursor. Some cursor types
are more resource-hungry than others
A Recordset object can have one of four lock type settings. Our choice of lock type is
dependent on whether or not we intend to change the data in the data store, on whether we
want to see existing changes, and on the existence of other people using the data store.
Some lock types are more resource-hungry than others
There are various methods that we can use to point the cursor at different records. For
example, the Move, MoveFirst, MoveLast, MoveNext and MovePrevious methods allow
us to move in forwards and backwards directions
The Find method allows us to search for a particular record that fits our criterion. If the
search is successful, it points the cursor at that record (and if it fails, it points the cursor at
BOF or EOF)
The Filter property allows us to hide all the records that don't specify a particular
criterion
The Fields collection contains one Field object for each field in the recordset. We can
use a loop to iterate through it in the same way as we iterate through any other collection
The GetRows method allows us to create an array containing data from the recordset
Along the way, we've seen that the ADO object model has an essentially flat hierarchy. We've
also met the ADO constants, and seen that they can help us to create more readable code.
The Recordset is really at the heart of ADO, and as you use ASP more you'll probably discover
you use it to do one main task – namely generating dynamic pages from a recordset. It's quite
possible to create web sites that are almost entirely data bound, having only a few ASP pages –
with all of the data generated from a data store using recordsets. Updating the site simply
becomes a matter of updating the data store. In fact, much of the Wrox Press web site is
generated in this way. Obviously you won't be limited to this task, but you'll find that they can take
away a lot of the effort involved in site maintenance.
In the next chapter we look at more advanced data handling techniques. For example, you'll find
out how we go about updating the data store, or getting back information from data stores other
than databases, and this will conclude our whistle-stop tour of ADO.
Chapter 14: Advanced Data Handling Techniques
Overview
This chapter is the final one in our ADO trilogy. It brings together the remaining objects in the
ADO object model and demonstrates finally how we can get our information back to our web
pages from other sources, such as text files as well as our databases.
In all there are five standalone objects in ADO 2.5, yet we've spent the last two chapters
discussing just two of them. That's because the Recordset and Connection objects are at the
heart of all we do. However, if we want to do anything more than just ask simple questions of the
data store, then we'll need the functionality offered by the other objects in ADO 2.5. We can't
hope to do anything more than touch very briefly upon some much larger subject areas and point
you in a direction where you'll be able to learn more, but this shouldn't detract from the fact that
the subjects we will discuss in this chapter are fundamental to you using databases in web pages.
In this chapter we'll discuss:
The Command object
A quick introduction to SQL
Stored procedures
The Parameters collection
Adding and updating your databases using the Recordset object and SQL
Customized data access
Semi-structured data and the Record and Stream objects
The Command Object
In the examples we've seen so far in Chapters 12 and 13, we've been using the ADO
Connection and Recordset objects to make a connection between our ASP page and our
data store, and then to retrieve data from the data store for use in the page. In those examples,
every request for data was effectively given by executing a command against the data store.
When we use the phrase 'executing a command against the data store', we mean that we're
instructing the data store that we want to perform some task with its data. So far, all our examples
have used the same type of command – each time, we've been telling the data store that we wish
to select certain data (and have that data returned to the page within a recordset). For example,
when we specified the appropriate criteria within the parameters of the Recordset object's Open
method, we were selecting which data we wanted to see within our recordset:
objRS.Open = "AllMovies", strConnect, adOpenforwardOnly, adLockReadOnly,
adCmdTable
So, although we didn't mention it at the time, in Chapters 12 and 13 we were executing
commands against the data store. There are other types of command too: for example, we might
use a command to tell the data store to insert a new record, or to update the contents of a
record, or to delete an existing record.
Although we can use the Connection and Recordset objects to execute such commands,
ADO also provides an object called the Command object, which is specially designed for the
purpose of specifying and running commands against the data store.
In terms of managing and executing commands, we can get much greater flexibility and
functionality from the Command object, because it allows us to run much more complex queries –
enabling us to make the most of things like stored procedures and parameters, as we'll see
later. But before we leap into such lofty topics, we'll do two things:
First, we'll warm to the Command object by having a look at how it's used in its simplest
form
Then, we'll have a look at how to use a query language called SQL to write commands
This statement creates a new Command object called objCommand. This should look familiar;
we've already used the same technique to create Connection and Recordset objects.
Executing a Command
In order to run a command, we use the Command object's Execute method. The Execute
method has three parameters:
All three arguments are optional – if you omit any of them then the Execute method will adopt
the default value for that parameter. Let's have a look at them:
The RecordsAffected parameter is for action queries (those that insert, update or
delete data and, thus, don't return a recordset). If you want to know the number of records
that were affected by the command, then you can pass a variable name into this parameter –
when the command is complete, the number of affected records will be placed into this
parameter.
Note It's worth noting that this is the first ADO method we've met that is capable
of returning information to you within one of its parameters.
The Parameters parameter holds an array of parameters that are to be passed to the
command. This means that we can write a generic command and use it lots of times. Each
time we execute the command, we can make it specific by using the command's parameters
to pass in the information specific to this one Execute call. Don't confuse this with the
Parameters collection, which we'll be looking at in more detail later in the chapter when we
look at stored procedures and queries. Also bear in mind that a parameter value that you
provide in the Parameters parameter will override a value specified in the Parameters
collection.
Note We write our generic commands using a query language called SQL. We'll
meet SQL properly after the following example.
The Options parameter is like the Options parameter in the Recordset.Open
method. It specifies the type of command being run.
In this case, we've specified the name of the database table, Movies, as the command text.
(We'll see later that we can use SQL to create more complex queries than this.) Then we use the
Execute method to execute the command. Notice that, in this code fragment, the first two
arguments have been omitted; however, we've left the two commas in because we want to
specify a value for the third parameter. The third parameter itself is used to specify the type of
command being run – and so, since the command text is in the form of a table name, we've used
the ADO constant adCmdTable to reflect that (this gives ADO the opportunity to optimize the way
the command is executed).
Instead of the two lines above, we could achieve the same result by taking an extra line to set the
command type, using the CommandType property, like this:
objCommand.CommandText = "Movies"
objCommand.CommandType = adCmdTable
objCommand.Execute
This time, we don't need to specify the command type as a parameter of the Execute method,
because we'd already specified it by setting the CommandType property explicitly.
If we want to use that recordset elsewhere in our program, we should give it a name so that we
can reference it. We can do that by using the Set statement to assign the result of the Execute
statement to a variant of our choice:
objCommand.ActiveConnection = strConnect
objCommand.CommandText = "Movies"
objCommand.CommandType = adCmdTable
Set objRS = objCommand.Execute
So, now we've arrived at a set of statements that make a lot of sense, given that the code is just
returning the Movies table. Let's give it a go in an example page.
This sort of flexibility among the ADO objects is another demonstration of the 'flat hierarchy' of the
ADO object model. Using a Command object doesn't depend on having first created a
Recordset, or a Connection object. By contrast, if you've got an existing Recordset object
then you can use it to run a command (as we've done here) – so you're not forced into creating a
new Recordset object if you don't want one. Using ADO objects is a mix'n'match scenario – and
it's useful, because it means that you're not forced to create any more objects in your code than
you actually intend to use.
While we're talking of the flexibility of the ADO objects, let's return briefly to the Connection
object and see how that handles commands.
The Connection object's Execute method is a little different in its syntax from the Command
object's Execute method, even though the two methods have the same name. Don't get mixed
up in this section! The full list of parameters for the Connection object's Execute method is:
The parameters here are slightly different to the parameters of the objCommand.Execute
method – and they're in a different order. Here:
The CommandText parameter can contain the command text (so it corresponds to the
Command object's CommandText property)
The RecordsAffected and Options parameters work just like their namesake
parameters in the Command object's Execute method
So in fact, we don't really need to revisit this, because the functionality provided is very similar to
that we've already seen for the Command object. We've included it here to reinforce and
demonstrate the flexibility of the 'flat hierarchy' of the ADO object model once again.
So far in this book, the only commands that we've used have been 'select' commands – in which
we select particular records and/or fields from a table (or indeed, the whole table), and have the
data store return those data in the form of a recordset. But the process of selecting fields and
records is much more subtle and clever than we've shown so far. Moreover, as we hinted in the
first paragraphs of this chapter, data access isn't only about selecting data from the data store –
we want our commands to be able to do other things too.
In order to learn more about these different types of commands, we need to know a little of the
basic language in which they are submitted – and that language is SQL.
As we've already said, all SQL does is provide you with a format for retrieving the information you
want from the database.
SQL seems to have a reputation as being a difficult language, but don't be put off by it – getting
the hang of it is really quite simple. In fact, SQL is a declarative language. The term 'declarative'
means that we use SQL to tell the computer what it is we want, and then we let the machine
decide how best to achieve the correct result. We, as programmers, never need to see the
details; all we see is the result.
Note By contrast, other languages (such as Visual Basic,COBOL,C++ or Java) are
procedural languages. These languages are characterized by statements,
which tell the computer exactly what to do in a structured step-by-step way.
We've already seen something close to a SQL command. For example, we used something very
like a SQL statement when we used the Recordset object's Filter method in Chapter 13:
objRS.Filter = "Director = 'Quentin Tarantino'"
Consider the logic behind this: it's asking the Recordset object to select every record whose
Director field contains the exact string Quentin Tarantino (and to hide any other records).
Even more appropriately, we could use a SQL query when we are requesting data from the data
store in the first place:
objRS.Open = "SELECT TitleID, Title FROM AllMovies", strConnect
This statement tells the data store to select the values of the TitleID and Title fields for all
the records in the AllMovies table, and to pump that data into a new Recordset object. In fact,
this command uses two SQL keywords – SELECT and FROM. We'll see more of this SQL syntax
as we progress through this chapter – but as you can see, it's not too difficult to read.
As we said earlier, we've been performing SELECT-type commands in all of the ADO examples
so far in this book. For example, in the Command.asp example above we used the following
command to 'select' every single record and field from the Movies database table:
objCommand.CommandText = "AllMovies"
objCommand.CommandType = adCmdTable
Set objRS = objCommand.Execute
In fact, we can use a SQL SELECT statement to perform exactly the same thing:
objCommand.CommandText = "SELECT * FROM AllMovies"
objCommand.CommandType = adCmdText
Set objRS = objCommand.Execute
Note that the SQL SELECT command is the only one of these four commands that returns a
recordset. The other commands are not concerned with fetching data from the data store –
instead, they're all concerned with making changes to the data store, and as such they don't
return any data. Collectively, the INSERT, UPDATE and DELETE commands are known as action
commands or action queries.
In the course of this chapter, we'll see all four of these SQL commands in action. We'll deal with
SELECT first, and see how it gives us more flexibility for selecting data – so we don't have to
select entire tables all the time! Then we'll move on to UPDATE, and later in the chapter we'll look
at how to use INSERT and DELETE to create and destroy records.
So, to extract the list of movie titles from the Movies table, we'd write a SELECT command that
queries the database for the Title field of every record in the AllMovies database table, like
this:
SELECT Title FROM AllMovies
We're not limited to querying the data store for one field at a time – we can ask for as many fields
as we like (so long as each field we request is a field that belongs to the specified table!).
Querying for more than one field is simple – we just list each field name, separated with commas.
The following command queries the Cast table for a list of records of characters (held in the
CastRole field), along with the actors who portrayed those characters (the CastName field):
SELECT CastRole, CastName FROM Cast
What if we want to query the database for the data contained in all of the fields in a table? Well,
you can do it by using the wildcard * in your query, like this:
SELECT * FROM AllMovies
This returns the data in every field of record in the AllMovies table. The * symbol is effectively
a shorthand which means 'all fields'.
Of course, the amount of time it takes to complete a query is related to the amount of data you
ask for. So, if you ask for all fields then don't be surprised if your query takes a long time to
complete! It's much better to write commands that only query those fields that you're actually
going to use. Only ask for all fields if you really do need the data in all of those fields.
Important When you're writing SQL SELECT commands, don't query the data store
for more data than you need. It's a good practice that helps to keep your
queries brief, your code tidy and your web server's resources free.
This is just an extension of the original SELECT … FROM … clause – we've appended a WHERE …
subclause at the end. And again, you'll see that it's very easy to read. The WHERE … subclause
allows us to specify what kind of records we're interested in. For example, if we wanted a list of all
the films in the AllMovies table that were directed by Quentin Tarantino, we could specify that
we're only interested in those specific records:
SELECT Title FROM AllMovies WHERE Director = 'Quentin Tarantino'
Note You might notice that the Director Name is surrounded by single quotes. This is
because the name is a text string. You might be wondering why we don't use
double quotes – after all when using variants, that's what we would do
normally. You must consider that, here, the whole SQL statement will be
appear in double quotes, which are used to denote where the SQL statement
starts and ends. So, in place of double quotes in a SQL statement, we use
single quotes instead.
The <condition> part of this SQL command is actually something we've seen before. It works
in the same way as the Criteria parameter of the Recordset object's Find method and
Filter property:
strCriteria = "Director = 'Quentin Tarantino'" ' set criteria
objRS.Find strCriteria ' apply criteria
Here, we've just extended the <condition> clause to take in the films of both directors. Let's
actually see how you could run this last statement in an ASP script.
How It Works
We start by declaring variants for the Command and Recordset objects, and creating an
instance of the Command object:
Dim objCommand, objRS
Set objCommand = Server.CreateObject("ADODB.Command")
Note that we don't need to create a Recordset object using Server.CreateObject – we'll get
a real Recordset object shortly, when we use the Execute method.
Now we can set the Command object up ready to execute the command. This involves assigning
values to the ActiveConnection, CommandText and CommandType properties:
objCommand.ActiveConnection = strConnect
objCommand.CommandText = "SELECT Title, Director FROM AllMovies " & _
"WHERE Director LIKE 'Quentin Tarantino' " &
_
" OR Director LIKE 'Francis Coppola'"
objCommand.CommandType = adCmdText
For the connection, we're using the strConnect connection string, defined in DataStore.asp.
For the command text, we're using a more complex SQL command now – of the form SELECT…
FROM…WHERE…. We want to retrieve a recordset containing the Title and Director fields of
the AllMovies table; but we're only asking for those movies directed by Tarantino or Coppola,
so we're using the WHERE clause to reflect that. The query is in the form of a text string, so the
command type is adCmdText.
Note There's an interesting technicality regarding the WHERE clause here – note that
we've used the LIKE keyword (an expression parser on character fields) in
place of the equals sign (=). You can try the text-based condition Director =
'Quentin Tarantino' instead; but if you're using SQL Server (with OLE-
DB or ODBC providers), you'll find that it causes a syntax error. SQL Server
doesn't allow you to use text, ntext or image data types in the WHERE clause
except with the LIKE keyword. Not all providers are as fussy as this (Access
isn't) – but it's something to be aware of. We'll use the LIKE keyword because
it's more widely accepted.
Now we run the command using the Execute method, and store the results in the Recordset
object (here, note that we're using the Set statement because we're assigning an instance of an
object to the objRS variable):
Set objRS = objCommand.Execute
Once we've got our recordset we don't need to keep the Command object any longer. So we'll get
rid of it:
Set objCommand = Nothing
Lastly, we display the contents of our recordset, using the EOF property to control the While…
Wend loop as we've seen in previous examples:
While Not objRS.EOF
Response.Write objRS("Title") & " was directed by " &
objRS("Director") & "<BR>"
objRS.MoveNext
Wend
By contrast, multi-table databases are common, and there are a number of reasons for designing
databases in this way. The most obvious reason is that these tables contain many different types
of data – a single table containing all these different types of data would have more than 20
different fields, which would be rather unmanageable, as you'll see if you try to view the entire
contents of the AllMovies table in one go.
There are other reasons. Single-table data storage can appear disorganized, with apparently
unrelated fields stored in adjacent columns. Also, they can be very inefficient – potentially, a
single table database can end up with lots of repeated data. We first encountered these issues in
the early sections of Chapter 12. Let's briefly consider how these issues relate to our
Movies2000 database.
If you're a cinema expert, then you'll know that the film Pulp Fiction was directed by Quentin
Tarantino, featured Amanda Plummer, John Travolta and Samuel L. Jackson (in the roles of
Honey Bunny, Vincent Vega and Jules), and had a release certificate 'R' in the United States and
'18' in the United Kingdom. If we stored this information within a single table in the database, we'd
have a set of records like this:
There are two problems with storing the data like this. First, there's a lot of unrelated data all
contained in the same table – it just doesn't demonstrate good organization. Second (and
possibly more importantly), there's a lot of repetition. Therefore, in Movies2000, we have
dissected this data into four related tables like this:
MovieID Title
67 Pulp Fiction
MovieID Director
67 Quentin
Tarantin
o
When you consider that there are 330 films in our database, and 22 different fields, you can see
how a set of tables is easier to use and vastly more efficient than a single table. In this case, we
just use each movie's unique identifier, as defined in the Movies table, to indicate to which movie
the data relates. The unique identifier for Pulp Fiction is 67.
The consequence of this is that we need to be able to write a command that can query data from
two or more database tables at the same time. Fortunately, SQL provides us with a number of
ways of doing this – by way of a table join. SQL even provides a keyword, JOIN, just for the
purpose.
In fact, there are many different types of join in SQL, but we are only going to cover one of them –
an inner join. This allows us to write a command that queries two or more tables at the same
time, returning the results of the query as a single recordset.
So, let's put aside the AllMovies table, which we've used in the past, and show how we can
write an example that pairs up movies with their directors using only the much smaller Movies
and Directors tables of our database.
Try It Out - Running a SQL SELECT statement that joins two tables
In order to do this, we'll write a new command that queries the two tables and returns a single
recordset. We'll create a new example by simply adapting the code from SQLSelect.asp.
1. Create a copy of the file SQLSelect.asp – call the copy SQLSelectInnerJoin.asp.
2. Instead of selecting all the data from the AllMovies table, we'll select the text from a
JOIN of smaller tables. So open up SQLSelectInnerJoin.asp, and change the
following highlighted lines:
3. ...
4. <%
5. Dim objCommand, objRS
6. Set objCommand = Server.CreateObject("ADODB.Command")
7.
8. objCommand.ActiveConnection = strConnect
9. objCommand.CommandText = "SELECT Movies.Title,
Director.DirectorName " & _
10. "FROM Movies INNER JOIN Director ON Movies.MovieID=
Director.MovieID " & _
11. "WHERE (Director.DirectorName LIKE 'Quentin Tarantino' " &
_
12. " OR Director.DirectorName LIKE 'Francis Coppola') "
13. objCommand.CommandType = adCmdText
14.
15. Set objRS = objCommand.Execute
16. Set objCommand = Nothing
17.
18. While Not objRS.EOF
19. Response.Write objRS("Title") & " was directed by " & _
20. objRS("DirectorName") & "<BR>"
21. objRS.MoveNext
22. Wend
23. objRS.Close
24. Set objRS = Nothing
25. %>
...
26. Save these changes. Now use your browser to view the page:
How It Works
By amending only one line in our script, we've completely changed the whole query. Let's have a
look at that query line again:
objCommand.CommandText = "SELECT Movies.Title, Director.DirectorName "
& _
"FROM Movies INNER JOIN Director ON Movies.MovieID =
Director.MovieID " & _
"WHERE (Director.DirectorName LIKE 'Quentin Tarantino' " & _
" OR Director.DirectorName LIKE 'Francis Coppola') "
There's a lot in this command but when we break it up you'll see that it's quite simple to
understand. The first thing to note is that the command still has the form SELECT…FROM…
WHERE… – it's just that the table (in the FROM clause) is a little more complex. Let's look at the
FROM clause first.
The FROM clause begins by saying that we'll create an INNER JOIN of the Movies and
Director tables. Each record in that table is created by joining a record from the Movies table
to a record of the Director table, where the MovieID field of the first is equal to the MovieID
of the second:
... FROM Movies INNER JOIN Director ON Movies.MovieID =
Director.MovieID ...
Here, we need to use the 'dot notation' to indicate the table to which each field belongs – so
Movies.MovieID refers to the MovieID field of the Movies table. Here's what the inner join
does:
From the resulting table, we'll select the fields corresponding to the Title field of the Movies
table and the DirectorName field of the Director table:
SELECT Movies.Title, Director.DirectorName ...
However, we don't want every record. We only want those records that correspond to the films of
Tarantino and Coppola:
... WHERE (Director.DirectorName LIKE 'Quentin Tarantino'
OR Director.DirectorName LIKE 'Francis Coppola')
Having created the join, and indicated the required fields and conditions, the resulting data is put
into a single recordset. Then it's just a case of using the default field names to access the values
from the recordset. The default field names are taken from the field names of the database
tables:
While Not objRS.EOF
Response.Write objRS("Title") & " was directed by " & & _
objRS("DirectorName") & "<BR>"
objRS.MoveNext
For example, if you were so inclined you could go through the AllMovies table and change
every Director's name to your own:
UPDATE AllMovies SET Director = 'Chris Ullman'
Of course, it wouldn't be very easy to undo a change like this, so we don't advise that you try it!
The above line can be quite brutal, because it makes the requested change to every single
record. Fortunately, the UPDATE command also allows us to specify the exact condition under
which records in the table will get changed. The format for this is:
OK, once again we've changed the value in the Director field of the AllMovies table, but this
time only for any record whose title is Pulp Fiction. As it happens, there's only one record of
this description in the AllMovies table – and the Director field of that one record is updated
from Quentin Tarantino to Chris Ullman. As in the earlier examples, we've used the LIKE
keyword in place of the equality operator (=), though it provides the same functionality.
When you perform an update, you'll probably want to know whether it succeeded and how many
records it changed – and in the following example we'll demonstrate how to do that too (you might
recall, from our discussions at the beginning of the chapter, that it's the RecordsAffected
parameter of the Execute method that allows us to do this).
Then, in order to restore the accuracy of the database, we'll change these values back from our
fictional value to Quentin Tarantino.
1. We're going to use the Movies2000 database and the strConnect connection string
again, so you'll need the appropriate database files and SSI included (as we have for the
other examples in this chapter).
2. Open up your favorite ASP editor, and create the following page (called
SQLUpdate.asp):
3. <%
4. Option Explicit
5. Dim strConnect
6. %>
7. <!-- #include file="DataStore.asp" -->
8. <!-- METADATA TYPE="typelib"
9. FILE="C:\Program Files\Common Files\System\ado\
msado15.dll" -->
10. <HTML>
11. <HEAD>
12. <TITLE>Using SQL's UPDATE Command and the ADO Command
Object</TITLE>
13. </HEAD>
14. <BODY>
15. <%
16. Dim objComm, intNoOfRecords
17. Set objComm = Server.CreateObject("ADODB.Command")
18.
19. Response.Write "<B>Harry the Ham directs Tarantino films?
</B><BR>"
20. objComm.ActiveConnection = strConnect
21. objComm.CommandText="UPDATE AllMovies SET Director = 'Harry the
Ham' " & _
22. "WHERE Director LIKE 'Quentin Tarantino'"
23. objComm.CommandType=adCmdText
24. objComm.Execute intNoOfRecords
25. Response.Write "This UPDATE command has affected " & _
26. intNoOfRecords & " records<BR><BR>"
27.
28. Response.Write "<B>Tarantino returned to his rightful place as
director:</B> <BR>"
29. objComm.CommandText="UPDATE AllMovies SET Director = 'Quentin
Tarantino' " & _
30. "WHERE Director LIKE 'Harry the Ham'"
31. objComm.Execute intNoOfRecords
32. Response.Write "This UPDATE command has affected " & _
33. intNoOfRecords & " records<BR><BR>"
34. Set objComm = Nothing
35. %>
36. </BODY>
37. </HTML>
38. Save it as SQLUpdate.asp in the usual place – \inetpub\wwwroot\BegASPFiles.
39. Now view the page in your browser:
The results indicate that for the first command, two records were affected—and the two records
for the two Tarantino files in the AllMovies table were updated, with the imposter Harry the
Ham instated as director of these films. The second command locates the records that claim that
Harry the Ham is director, and reinstates Quentin Tarantino as director. Okay, let's look through
the code.
How It Works
We're using a command that does not return a Recordset object; in fact, we don't need a
recordset at all in this example, so we don't create one. All the changes that we make are made
directly, to records that exist the database.
However, we are aiming to return the number of records that have been updated, so we need to
define a variable which will hold that total, called intNoOfRecords. We also create the
Command object:
Dim objComm, intNoOfRecords
Set objComm = Server.CreateObject("ADODB.Command")
Next we set all the properties for the first of our commands. The ActiveConnection property
takes the value of the connection string, strConnect. The CommandText property takes the
value of the UPDATE command. And the CommandType property is adCmdText, to reflect the fact
that the command is a SQL statement:
objComm.ActiveConnection = strConnect
objComm.CommandText="UPDATE AllMovies SET Director = 'Harry the Ham' "
& _
"WHERE Director LIKE 'Quentin Tarantino'"
objComm.CommandType=adCmdText
The command text itself says that it will locate every record in the AllMovies table whose
Director field holds the value Quentin Tarantino, and it will change that value to Harry
the Ham.
Now we execute the command, and return the number of records affected within the variable
intNoOfRecords:
objComm.Execute intNoOfRecords
To prove that intNoOfRecords really does hold the number of affected records, we'll use it in
our report of the command's effect:
Response.Write "This UPDATE command has affected " & _
intNoOfRecords & " records<BR><BR>"
This gives us a good way to double-check that the command performed the actions that we
expected it to perform. In this case, we know that there are two Tarantino films in the AllMovies
table, because we've seen them in an earlier example. So if this variable contains a value other
than 2, we know that something has gone wrong. If the command has failed in any way, then
intNoOfRecords won't hold a value. In this case, we've checked by printing the value out, but
you might do something subtler, like this:
If intNoOfRecords = 2 Then
Server.Execute Successful.asp
Else
Server.Execute SomethingWentWrong.asp
End If
Next we set about changing those two records back. The ActiveConnection and
CommandType properties are already set, so we only need to change the CommandText property
before executing this second command:
objComm.CommandText="UPDATE AllMovies SET Director = 'Quentin
Tarantino' " & _
"WHERE Director LIKE 'Harry the Ham'"
objComm.Execute intNoOfRecords
Again, we can display the number of affected records:
Response.Write "This UPDATE command has affected " & _
intNoOfRecords & " records<BR><BR>"
But sometimes these queries are rather unwieldy, and sometimes we use them very often. So
wouldn't it be nice if we could write a query, give it a name, store it in the database, and then just
use the query's name in our ASP code? Well, yes it would – and this functionality is already
available in the form of stored procedures (as they're known in SQL Server) and queries (as
they're known in Access) – they're ready-to-run SQL statements.
Note Throughout this chapter, we'll refer to them as stored procedures.
We're not going to teach you how to write stored procedures in this book. That's really beyond the
scope of this title and belongs in the realms of a book on your favorite database server. However,
we will see what a simple stored procedure looks like, and we'll demonstrate how to use it within
an ASP page.
Notice that we've used a different CommandType value to reflect the fact that this command is in
the form of a stored procedure, and not a table or a command string. Other than that, the
command structure is just the same as we've seen previously. At this level, there's no difference
between an Access query and a SQL Server stored procedure.
Using Parameters
You might think that using stored procedures give you less flexibility because you can't customize
them as easily – but this is where the Parameters collection comes in. A parameter is the
means by which we can pass a value into a stored procedure, or receive an output parameter
from a stored procedure – in just the same way we pass parameters to functions. So, at design
time we write a generic query in the form of a stored procedure; and then at runtime we can use
its parameters to pass user-supplied information to and from it.
This will present you with the following view. Note, in the Criteria field, the string [Director], which
indicates that this query expects a parameter – and that the query will search for records whose
Director field contains the value of that parameter.
If you wish, you can view the parameters for the query (select Parameters from the Query menu)
and from section; here you are able to specify a data type for the parameter name.
We'll test this out in an ASP page, in a moment; first, let's look at a stored procedure in MSDE.
If we wanted to create a stored procedure equivalent to the Access query described above, we'd
write a SQL command like this:
CREATE PROCEDURE usp_FilmsByDirector
@Director VarChar(50)
AS
SELECT *
FROM AllMovies
WHERE Director = @Director
ORDER BY Title
This SQL Server stored procedure has the same functionality as the Access query above, so
we've given it a similar name: usp_FilmsByDirector. We've already created this stored
procedure for you within the Access .mdb file and within the .mdf file for MSDE users. If you are
using MSDE you will have to view the stored procedure using an ADP file, which is also available
from the Wrox web site (ADP is an Access front-end for MSDE databases). Again, see Appendix
L for more details on ADP.
Here the parameter is @Director (note that, in SQL Server-based data stores, parameters are
preceded by the @ sign) and is a character string of length 50.
Error Type:
/BegASP/accQueryParam.asp, line 30
Alternatively, if you're querying for data via a SQL Server-based stored procedure, and your code
doesn't pass required parameter values, then the OLE-DB provider for SQL Server will tell you
something like this:
Error Type:
Microsoft OLE DB Provider for SQL Server (0x80040E10)
Procedure 'usp_FilmsByDirector' expects parameter '@Director', which was not supplied.
/BegASP/StoredProcParam.asp, line 26
In each case, the error message tells you quite clearly that it is expecting a parameter, and that
you didn't supply one.
The Parameters collection is a collection of the ADO Command object (as you'll see if you flick
back through to the beginning of Chapter 13, to remind yourself of the ADO object model
diagram). In other words, the Parameters collection (and its Parameter objects) is only
accessible through the Command object. Because parameter-based stored procedures require
Parameter objects, this means that you can't use ADO to call parameter-based stored
procedures except through the Command object. (By contrast, it's possible to call non-parameter-
based stored procedures through the methods of the Connection or Recordset object.)
Let's demonstrate a simple parameter-based query. There are a couple of examples in the
following pages that do this:
In the first example, we'll write an ASP page that uses the qryFilmsByDirector
Access query (which is built into Movie2000.mdb database file) to ask for data from the
Movie2000.mdb database.
In the second, we'll write an ASP page that uses the usp_FilmsByDirector stored
procedure (which is built into Movie2000.mdf) to query the MSDE database for the same
data.
They're not very different – we've presented the code separately but we'll explain them both
together. Choose which of these two examples you want to go with (according to whether you're
using Access or MSDE) and away we go.
Notice that this shows only the films in which the Director field is equal to Quentin
Tarantino – the query has picked out all the Tarantino films stored in the AllTitles table of
the database. We'll explain the structure of the code in a moment. First, we'll try another one, this
time using the MSDE.
You might like to try changing the value of the strDirector string from Quentin Tarantino
to Woody Allen, just to prove that you can control the parameter value using the ASP logic –
and more importantly, without amending the stored procedure itself.
How It Works
As we said before introducing the code, these two examples are very similar – the only difference
being in the name of the precompiled query/stored procedure, and in the name of the parameter.
There's no change to the header, so we'll just skip that, and jump straight into the meat of the
code. First, we declare the variants that we'll need – for the three ADO objects and a string to
hold the parameter value. Then we set one of them, objComm, to point to an ADO Command
object:
Dim objRS, objComm, objParam, strDirector
Set objComm = Server.CreateObject("ADODB.Command")
Note We don't need to set the other two ADO objects yet – that will come in a
moment.
Now we can start to set up all the properties of our Command object. We start with the
ActiveConnection, CommandText and CommandType properties (that we've met in previous
examples). This is the code from the example that uses Access (the MSDE example uses
extremely similar code:
objComm.ActiveConnection = strConnect ' fill in the command
properties
objComm.CommandText = "qryFilmsByDirector"
objComm.CommandType = adCmdStoredProc
The ActiveConnection is set equal to the connection string found in strConnect; the
CommandText is set to the name of our Access query or stored procedure; and the
CommandType is set to the ADO constant adCmdStoredProc, to indicate to ADO that we're
using a stored procedure.
The next step is to create our parameter. For each parameter we need, we'll add an ADO
Parameter object to the Parameters collection. This process has two stages: in the first stage
we create a standalone Parameter object and define it by applying values to its properties; and
in the second step we append our Parameter object to the Parameters collection.
Here's the first of these two stages. We use the Command object's CreateParameter method to
create a standalone Parameter object, and we give it the name objParam:
Set objParam = _
objComm.CreateParameter("Required Director", adVarChar,
adParamInput, 50)
We've passed four values to the CreateParameter method – giving the parameter name
(which is Required Director for Access and @Director for MSDE), its data type
(adVarChar signifies a character string), the input/output type (adParamInput signifies that it's
an input parameter – more on that later) and the maximum size of the value (50 characters in this
case, though you can give a byte value).
Note We'll look at the CreateParameter method in more detail after we've
completed this example.
Now we've built the parameter, we can append it to the Parameters collection by using the
Command object's Append method:
objComm.Parameters.Append objParam
Next, we'll give the input parameter a value. In this case we're passing a variable that contains a
character string:
strDirector = "Quentin Tarantino" ' you can change this if you
like
objComm.Parameters("Required Director") = strDirector
We only need one parameter so we can now execute the query. To do that, we use the Execute
method, as we've seen before. Because we're selecting data, the results of the query are
returned to us in an ADO Recordset object. We'll use the variant objRS to point to that
Recordset object:
Set objRS = objComm.Execute ' execute the command and generate the
recordset
Now we've got our recordset, the job of our Command and Parameter objects is complete. We
don't need them again in this example, so we'll set the variants to Nothing:
Set objComm = Nothing ' don't need the Command and Parameter
objects
Set objParam = Nothing ' ... so we can clean them up
Creating Parameters
Let's look at that CreateParameter method in more detail. The full syntax for the method is as
follows:
In fact, all five of these parameters are optional. In the examples above, we specified four of them
but not the fifth – choosing instead to specify the parameter's value after we'd appended the
Parameter object to the Parameters collection. There are many other ways to go about this.
For example, you might choose to create the Parameter object without setting any of its
properties, and then set the properties in several subsequent lines of code:
Set objParam = objComm.CreateParameter
objParam.Name = "@Director"
objParam.Type = adVarChar
objParam.Size = 50
objParam.Direction = adParamInput
objParam.Value = "Quentin Tarantino"
At the other extreme, you might choose to use the Append method first, to add an 'anonymous'
Parameter object to the Parameters collection, which is then created and defined within the
same statement using the CreateParameter method:
objComm.Parameters.Append objComm.CreateParameter("@Director", _
adVarChar, adInput, 50, "Quentin
Tarantino")
Output Parameters
As well as returning data (which we've seen), SQL Server stored procedures are able to return
values in the form of output parameters. For example, using the Movie2000 database, suppose
we wanted a count of all movies in the database that were made by a particular director.
One way of obtaining this value would be via a SQL stored procedure like this one:
CREATE PROCEDURE usp_NumberOfMovies
@Director varchar(50)
AS
SELECT COUNT(*)
FROM AllMovies
WHERE Director LIKE @Director
When we run this stored procedure from our code, it will return a recordset containing just one
column and one row (and hence one cell). The single cell in that recordset would contain the
value we're after, which in this case is 2. We could call this by using the Execute method of the
Command object, and passing in the name of the director in whom we are interested.
But there's another way – we can cut out the need to deal with a recordset, and use an output
parameter instead. For this, we'd use a slightly different stored procedure, like this:
CREATE PROCEDURE usp_NumberOfMovies
@Director varchar(50),
@Number int OUTPUT
AS
SELECT @Number = COUNT(*)
FROM AllMovies
WHERE Director LIKE @Director
Here, the second parameter is an OUTPUT parameter, which means that during the course of the
stored procedure we can assign a value to that parameter, and that value will be returned to the
calling program when the procedure has finished executing. Our ASP code to call this stored
procedure might look something like this:
Dim intNumber, objComm
Set objComm = Server.CreateObject ("ADODB.Command")
objComm.ActiveConnection = strConnect
objComm.CommandText = "usp_NumberOfMovies"
objComm.CommandType = adCmdStoredProc
objComm.Parameters.Append objComm.CreateParameter("@Director",
adVarChar, _
adParamInput, 50, "Quentin Tarantino")
objComm.Parameters.Append objComm.CreateParameter("@Number", adInteger,
adParamOutput)
objComm.Execute
intNumber = objComm.Parameters("@Number")
Here, we've used the Append and CreateParameter methods twice – once to append the
input parameter @Director to the Parameters collection, and once to append the output
parameter @Number to the collection. Note that the order of parameters in the collection is the
same as the order of parameters in the stored procedure. In particular, for the second parameter
we've specified that the Direction parameter should be adParamOutput (to match the fact
that the second parameter of the stored procedure is an OUTPUT parameter).
You can also see that, in the ASP code, we haven't specified a value for the output parameter.
However, after the command has been executed the output parameter will hold a value, and this
value is returned from the stored procedure.
Return Values
In addition to returning output parameter values, a stored procedure can also return a return
value. This is a special kind of parameter, which does pretty much the same job as an output
parameter, but it allows us to assign a data type to the value that we have returned. If you tried
this with the output parameter in the example above, then if you tried to assign a type other than
adInteger (which is defined within the stored procedure) you'd create an error.
When a stored procedure returns such a value, then it is always the first-named parameter (i.e.
the 'zero parameter' – parameter lists are zero-based) in the Parameters collection. When we
append a return value parameter to the Parameters collection, we distinguish it by specifying a
Direction of adParamReturnValue:
objComm.CreateParameter("Return", adVarChar, adParamReturnValue, 8)
This creates a return value parameter that is a character string of eight characters. Because the
return value is the first in the Parameters collection, you must append it before appending any
other Parameter objects.
Important Let me emphasize once more that the order in which you append
parameters to the Command object is very important. The order of
parameters in the stored procedure must be the same as that in the
Parameters collection, and a return value parameter (if there is one)
must always be first.
Once the command has run you can access this parameter like any other:
strReturn = objComm.Parameters("Return")
or:
strReturn = objComm.Parameters(0)
ADO then contacts the data provider and updates the Parameter objects in the Parameters
collection for you. You can then access a parameter by ordinal number (or name), assign a value
to it and execute the command. For example:
objComm.Parameters.Refresh
objComm.Parameters(1).Value = "Quentin Tarantino"
This allows you to avoid getting bogged down by the details of the parameters. In particular, if
you're working with a single Command object and a number of different stored procedures, then
the Refresh method is a quick-and-clean way to set up your Parameters collection with the
appropriate parameters. You can do this just before each new stored procedure call. It's also
extremely useful if you are having problems in setting the correct data types and sizes. If you find
that you're getting a lot of errors because your parameter settings aren't right, try using a
Refresh instead and then print out the data types and values from the properties of the resulting
Parameter objects.
The Refresh method is a useful tool, but it does come at a price in terms of performance. Each
time you call the Refresh method, ADO makes a trip to the data store to extract the parameter
information. The more you use it in your code, the slower your code will run. Also, you should be
aware that some data sources, such as SQL Server 6.5, don't support the Refresh method. As
such, it's well worth considering in a development environment, but when you come to write the
code for your public web site then it's probably worthwhile using the CreateParameter and
Append methods to build your parameters.
Modifying Data
We've covered a myriad of ways of getting data out of data stores, and we've touched on just one
technique for changing the data in a data store (via the Command object's Execute method, and
a SQL UPDATE command). But as you've probably guessed by now, ADO gives us other ways to
"change" the contents of a data store.
The act of changing the data in the data store can itself be broken up into three types of changes
– updating existing data, adding new data, and removing existing data. These are the three
activities we referred to earlier in the chapter as the action commands. We can perform all three
of these activities using the Command object – using the UPDATE, INSERT and DELETE SQL
commands, respectively. However, we can also perform them using the special properties of the
Recordset object, or by passing the appropriate SQL command to the Connection object's
Execute method.
We can't demonstrate all these possibilities in this book, so we're going to be selective. In this
section, we'll see how to add and delete records from a recordset (and ultimately the data store)
through the methods and properties of a Recordset object. Later, we'll perform similar tasks by
writing some SQL commands and using them with the Command object.
As usual we start with a couple of variable declarations, and then create a Recordset object:
Dim objRS, intIDForNewRecord
Set objRS = Server.CreateObject ("ADODB.Recordset")
Then we open the recordset, so that it contains the data from the Movies table. Notice that this is
a static recordset with optimistic locking. All our other recordsets have been read-only, but that
won't do if we are intending to make changes to the data store:
objRS.Open "Movies", strConnect, adOpenStatic, adLockOptimistic,
adCmdTable
With the recordset open, we're going to move directly to the last record in the recordset, make a
note of its MovieID value, and add one to that value. We'll use this in a moment to create the
new record:
objRS.MoveLast
intIDForNewRecord = objRS("MovieID") + 1
Now we can add the new data to the recordset. The AddNew method creates a new, empty
record within the recordset:
objRS.AddNew ' add a new record
This gives us a new record in the recordset, and so we can add some new data to its fields. And
here's a clever bit: when the AddNew method has added the new record to the recordset, it
assumes that we're going to do some work on it – so it also points the cursor at the new record.
So, we're ready to add the new data straight away:
objRS("MovieID") = intIDForNewRecord
objRS("Title") = "Psycho"
For the MovieID field, we're using the value that we assigned to intIDForNewRecord that we
calculated a moment ago. This is an integer value which is one more than the MovieID of the
previous last record in the recordset. In this case, that should be enough to keep the MovieID
fields of this table unique.
Note In Access, MSDE, and other database software, it's possible to configure the
fields that must contain unique values so that these values are automatically
generated whenever a new field is created. For simplicity, we've avoided that
here. In Chapter 15, the Classified.mdb database file uses autogenerated
field values.
Now, at this stage, we've only added the data to the recordset – the data store hasn't seen this
new record yet. In order to tell the data store of our changes, we must call the Update method:
objRS.Update
Now we'll prove that the new record really has entered the database. To do this, we'll Close the
recordset to disconnect it from the database, then Open it again to reconnect (this time using a
forward-only read-only cursor, and using a SQL command to select only the new record):
objRS.Close
The newly opened recordset contains fresh data from the database – so if it contains a record
with the MovieID value equal to intIDForNewRecord, we can conclude that the earlier
Update method successfully added our new record to the database. The browser output will
confirm that for us. Finally, we can clean up the Recordset object:
objRS.Close ' now close and clean up
Set objRS = Nothing
For example, this amends the record in the recordset by changing the value of the Genre field to
Thriller, and then updates the database with that change:
objRS("Genre").Value = "Thriller"
objRS.Update
It's also worth noting that if you make changes to your recordset and then call any of the Move…
methods, the Move… method call will implicitly call the Update method and update the data
store. So for example, the following two lines have the effect of changing the value in the Genre
field, updating that change in the database and then moving the cursor to the first record in the
recordset:
objRS("Genre").Value = "Thriller"
objRS.MoveFirst
There's one exception to this, which is when you're working in batch update mode.
Batch Updates
If you're working in batch update mode, it means that you can make multiple changes to multiple
records in the recordset and then send all of those changes back to the data store in one go. In
other words, ADO doesn't update the data store every time you move from one record to another
– it just waits until you use the UpdateBatch method to update the entire batch of changes.
If you want to use batch update mode, you need to set the LockType property of the recordset to
be adLockBatchOptimistic. For example, you can do this in the fourth parameter of the
Open method when you open a recordset:
objRS.Open "Movies", strConnect, adOpenStatic, adLockBatchOptimistic,
adCmdTable
Having made some changes to the recordset, you can choose to update the data store by calling
the UpdateBatch method. The UpdateBatch method has one optional parameter, which (if
you use it) can take one of the following ADO constant values:
adAffectCurrent, which means that the update will note which record the cursor is
currently pointing to, and will only write changes relating to that record
adAffectGroup, which means that the update will write changes to records that match
the current Filter property
adAffectAll, which means that the update will write all pending changes (this is the
default value)
objRS("Genre").Value = "Horror"
objRS.MoveNext ' doesn't update
objRS("Genre").Value = "Comedy"
objRS.UpdateBatch adAffectAll ' updates all changes since the
previous update
Cancelling Changes
You'll notice that, when you make changes to a record in the recordset, those changes aren't
updated on the data store immediately. In fact, they're not made until you call another method
(such as Update or MoveFirst, for example). This means that, having made the changes on
the recordset, you have a chance to undo those changes before they reach the recordset. In
order to undo those changes, we use the CancelUpdate method:
If blnMakeTheChange = "True" Then
objRS.Update
Response.Write "Data store updated"
Else
objRS.CancelUpdate
Response.Write "Changes to recordset undone; changes to data store not
made "
End If
The CancelUpdate method undoes all the changes made to the recordset since the last time
the data store was updated.
If you're making changes in batch update mode, the get-out clause involves the CancelBatch
method instead:
objRS.CancelBatch adAffectAll
Like UpdateBatch, the parameter is optional and can take any of the values
adAffectCurrent, adAffectGroup or adAffectAll.
This statement deletes the record that the cursor is currently pointing to. The record is deleted
immediately (unless you are in batch update mode – in which case the Delete requests remain
pending until you call the UpdateBatch method). If you're deleting records in batch update
mode, you can filter the recordset using adFilterPendingRecords to show only the records
that have been deleted.
Let's look at an example in which we delete a record. I don't know how many times you ran the
AddNew.asp example above, but every time you refreshed the page you will have added another
record to the Movies table (each new record having a unique MovieID value and a Title of
'Psycho'). Whether you created one new record, or lots of them, we don't really want to include
Psycho in our database so let's delete any records referring to Psycho now.
We start by opening a recordset that contains all the records of the Movies table:
objRS.Open "Movies", strConnect, adOpenDynamic, adLockOptimistic,
adCmdTable
Now we apply a filter to hide all records except those whose title is 'Psycho':
objRS.Filter = "Title = 'Psycho'"
Now we'll loop through the filtered records. We'll display the MovieID of each record, and then
we'll delete it from the recordset:
Response.Write "We'll delete all of the following records:<BR> "
While Not objRs.EOF
Response.Write objRS("MovieID") & "<BR>"
objRS.Delete
objRS.MoveNext
Wend
We're not working in batch update mode (because the recordset was not opened using
adOpenBatchOptimistic), so we don't need to call the UpdateBatch method – the deletions
are passed to the database automatically.
Now, we'll check that it worked. We'll close the recordset and reopen it with fresh information from
the database, asking for any records of Psycho in the database. There shouldn't be any such
records, and therefore EOF should be true and we'll get the output message we want:
objRS.Close
Here, <fields> is a list consisting of none, some, or all of the fields on the table, and <values>
is a list of the values that are to be assigned to those fields. Be careful to get the <fields> and
their corresponding <values> in the same order! Here's an example:
INSERT INTO Movies (MovieId, Title) VALUES (331, 'Psycho')
Note Bear in mind that the MovieId field is intended to be a unique identifier
(although in this database it is not actually defined as a primary key) in the
Movies table so each value entered should be unique. We can only enter the
value '331' here because we know for a fact that this value doesn't currently
exist for this field. A much better way of maintaining a unique field, as you will
see in Chapter 15, is to let the database generate the value.
If you omit a field, then a NULL (unknown) value will be inserted into that field. Beware that some
fields are specifically unable to accept a NULL value (such as a primary key field) – and if you
don't specify a value for such a field then your command will be rejected. The existence of 'non-
NULL' fields within your database will depend on how your database was designed.
The SQL DELETE command is even easier to use. It takes the following format:
You can only use DELETE to delete one or more whole records from the database. In particular, if
you want to remove values from a number of the fields in a record then DELETE is the wrong
thing to use – you need to assign specific 'empty' values to those fields instead.
We can use the DELETE command to delete multiple values at the same time, so be careful when
specifying criteria. Make sure that your criteria are specific enough – otherwise you may find
yourself deleting records that you really wanted to keep. If we were to DELETE the above record
from the table we could use:
DELETE FROM AllMovies WHERE Title LIKE 'Psycho'
This would delete the record that we created with the INSERT command above (and it would also
delete any other records in the AllMovies table whose Title value was equal to Psycho!). To
give a brief demonstration, let's look at an example that inserts a record, displays it and then
deletes it.
How It Works
We'll ignore the lines that set up the database, as they'll be very familiar by now. The first line of
interest is where we set up the first of our three commands – the INSERT – which inserts an entry
for Psycho into the Movie2000 database:
objComm.ActiveConnection = strConnect
objComm.CommandText = "INSERT INTO Movies(MovieId, Title) VALUES (331,
'Psycho')"
objComm.CommandType = adCmdText
We also use the CommandType property to indicate that the command is a SQL statement. We
then execute our INSERT command, and specify a variable to capture the value of the
RercordsAffected parameter, which will inform us of how many records were affected by the
action. We can use that captured information in our report of the command's execution:
objComm.Execute intNoOfRecords
Response.Write "The INSERT command has been executed; " & _
"Number of records inserted = " & intNoOfRecords &
"<HR>"
Now we set up the second command – the SELECT command, which will select only our new
record from the data store:
objComm.CommandText = "SELECT * FROM Movies WHERE Title LIKE 'Psycho'"
Note that we don't need to set the ActiveConnection and CommandType properties – they will
retain the values we set earlier on in the page.
The SELECT statement looks for any movies with the title 'Psycho' and displays all of the
information in the associated record. We then execute the command. Of course, because it's a
SELECT it returns a recordset, which we capture:
Set objRS = objComm.Execute
Now we'll quickly report the contents of the recordset, just to show that the new record really does
exist in the data store:
Response.Write "The SELECT command has been executed, " & _
"and has selected the following records: <BR>"
While Not objRS.EOF
Response.Write "MovieID = " & objRS("MovieID") & _
"; Title = " & objRS("Title") & "<BR>"
objRS.MoveNext
Wend
objRS.Close
Set objRS = Nothing
After we've written this report we've finished with the objRS recordset, so we remove it from
memory.
Lastly, we re-assign the CommandText property to the third of out SQL commands – the DELETE
command, which deletes every record whose title is 'Psycho'. Again, we don't need to re-assign
the values of the ActiveConnection and CommandType properties. We execute the command
and return the number of records affected to demonstrate the success of the operation:
objComm.CommandText = "DELETE FROM Movies WHERE Title LIKE 'Psycho'"
objComm.Execute intNoOfRecords
Response.Write "<HR>The DELETE command has been executed, " & _
"Number of records deleted = " & intNoOfRecords &
"<BR>"
One of the great things about the ASP/ADO combination is that we can use it to offer the user as
little or as much control of the page's content as we like. In this example we'll write a page that
displays data from the AllMovies table – but allows the user to decide exactly what data they
want to see. When they've submitted their choices, we'll use them to build up a SQL SELECT
statement, and thus create a table of their records.
The user interface in this example is blunt and direct. There's no subtlety about it – it just asks the
user to select the name of a director (using a text box) and one or more field names (using a set
of checkboxes). However, when you design your own pages, you don't have to be so rough-and-
ready: you can design a more attractive, easy-to-use interface that still offers choices to the user.
Underneath, you can submit those choices using much the same technique we see here.
This shows the table of data requested by the user; here, you can see that I selected the director
Woody Allen and six of the available fields, and the table shows all that data. (Below it, we've
displayed the <FORM> again so that the user can submit another query).
Pretty cool eh? And pretty simple too. Let's examine it a little more closely.
How It Works
This page is clearly in two parts. At the bottom of the page is an HTML form that uses some ASP
to prompt the user to tell us what data he wants to see. Before that, at the top of the page, is a
block of ASP that examines the most recently submitted request, and produces a table that
displays the results of that request. The table at the top of the page is generated within a big If…
Then…End If statement, which checks whether or not there is a request to deal with (if there's
no request, then we don't bother generating a table). So the outline of what we are doing is this:
Check the Request.Form collection to see whether the user selected any
checkboxes
If <there is at least one checkbox selected> Then
Check which checkboxes were selected, and build a SQL command string
If <a particular director was requested> Then
Append director details for SQL command string
End If
Use SQL command string to create a recordset
Use recordset to display the table
End If
Write empty textbox to browser
Use recordset to write checkboxes to browser
Allow user to Submit request
Let's look at how we capture the user's request first – that's the second half of the page. The
HTML form will submit data to a refreshed version of FindByDirector.asp, passing it to the
Request.Form collection:
<FORM NAME=MovieInfo ACTION="FindByDirector.asp" METHOD="POST">
...
</FORM>
Inside the form, the data collection is in two parts. First, we want the name of a director, so we
create a text box for this purpose:
Enter the Director to find:
<INPUT TYPE="TEXT" NAME="Director"><BR><BR>
Second, we want to know which fields the user wants to know about. For this, we create a
checkbox for each field in the AllMovies table. One way to create this list is to query the
AllMovies table directly from the database, and iterate through the Fields collection of the
resulting recordset, making one checkbox for each field:
Please select which fields you would like:<BR>
<%
' Create a recordset on the AllMovies table
Set objRS = Server.CreateObject ("ADODB.Recordset")
objRS.Open "AllMovies", strConnect, _
adOpenForwardOnly, adLockReadOnly, adCmdTable
' Create a checkbox in the form for each field in the recordset
For Each objField in objRS.Fields
Response.Write "<INPUT TYPE=CHECKBOX NAME=" & vbQuote & "Field" &
vbQuote & _
" VALUE=" & vbQuote & objField.Name & vbQuote & ">"
& _
objField.Name
Next
objRS.Close ' clean up
Set objRS = Nothing
%>
As you can see, that involves creating a recordset, populating it with data from the database,
using its Fields collection, and then cleaning up the Recordset object when we've finished.
This will give us a set of checkboxes like this:
<INPUT TYPE=CHECKBOX NAME="Field" VALUE="TitleID">TitleID
<INPUT TYPE=CHECKBOX NAME="Field" VALUE="Title">Title
We mustn't forget to add a SUBMIT button to the form (and also a RESET button, for good
measure):
<BR><INPUT TYPE=SUBMIT VALUE="Find"><INPUT TYPE=RESET VALUE="Clear">
The user types text into the text box, checks a few checkboxes and then clicks the Find button.
That causes the page to be reloaded, with the requested data passed to the Request.Form
collection.
That deals with the second part of the page. Now to the first part – the request handling and table
display. Whenever the page is loaded (or reloaded), the first thing we need to know is whether we
have a request to deal with. To do that, we'll check to see whether there are any values in
Request.Form("Field"). If so, then it means the user clicked at least one checkbox; if not, it
means that no checkboxes were checked (or that the page is being requested for the first time):
If Request.Form("Field").Count > 0 Then
Assuming this condition returns True, then we have a request to deal with, and so we execute
the contents of the If…Then…End If block. Before we build the recordset, we'll need to
analyze the request to find out what the user asked for – we'll use this to build a SQL SELECT
command, which we'll store in a string called strSQL. First, iterate through the contents of
Request.Form("Field"), and add the substring "field_name," to the strSQL string for
each field that was chosen:
' Find out which fields are to be selected
strSQL = ""
For intCount = 1 to Request.Form("Field").Count
strSQL = strSQL & Request.Form("Field")(intCount) & ", "
Next
Next, remove the final comma and space at the end of the string we just generated:
strSQL = Left(strSQL, Len(strSQL) - 2)
Next, append SELECT at the beginning and FROM AllMovies at the end:
strSQL = "SELECT " & strSQL & " FROM AllMovies"
Now the string in strSQL is starting to look like a proper SELECT command! Next, we'll check
whether the user asked for a particular director, and use that criterion as a WHERE clause:
If Request.Form("Director") <> "" Then
strSQL = strSQL & " WHERE Director LIKE '%" &
Request.Form("Director") & "%'"
End If
Now use the completed SQL command to query the database, and produce a recordset:
Set objRS = Server.CreateObject ("ADODB.Recordset")
objRS.Open strSQL, strConnect, adOpenForwardOnly, adLockReadOnly,
adCmdText
Now we can display the contents of the recordset on screen. We'll use a shortcut here – by
borrowing the RecToTable() routine that we wrote in Chapter 13. It is conveniently contained in
the RecToTable.asp SSI file, which we included at the top of the page using a #INCLUDE
directive. All we need to do is call the RecToTable() function, passing the name of our
recordset as parameter:
Response.Write RecToTable(objRS)
The RecToTable() routine returns a string containing all the HTML for the table, so we just
Response.Write it to the browser. Then we just clean up the recordset:
objRS.Close
Set objRS = Nothing
End If
Manipulating Data in your non-database Data Store
In Chapter 12, our first ADO chapter, we said that one of the main strengths of OLE-DB is its
ability to retrieve information from any data store – not only from databases. However, for the
examples so far in these chapters we've used a database as our data store. We haven't
deliberately sidestepped the issue of using other types of data store, it's just that in order to
extract data from other types of data store, it's helpful to first understand the principles of data
retrieval from a database. It's also because the principle of data retrieval from other forms of data
storage is much more complex than with databases.
The technology that is added in ADO 2.5 to enable you to make use of other forms of data store
is still in its relative infancy and quite difficult to get working across servers on a network.
However, it forms the cornerstone of a new Microsoft technology, namely Distributed Author
Versioning or DAV for short. In fact, IIS 5.0 is referred to as a WebDAV server. WebDAV is
actually a specification that uses extensions to the HTTP 1.1 protocol and, thus, can be used on
any platform. WebDAV is of great interest because it enables someone browsing on the client to
move files on the server, or change the contents of files on the server using these objects. It
allows distributed authoring of documents on the Web. It can also equally apply to e-mails on a
remote mail server or to any other form of data store for which there is an OLE-DB provider. As
the technology is still evolving and not supported by many web servers, we won't be spending
that much time on it. However, as an important part of future development, it merits some
coverage.
The two new objects that enable WebDAV, and come with ADO 2.5 are:
The Record object: this can be used to represent a record in a recordset, or it can
represent a file or even a folder in a file system
The Stream object: this can be used to represent the most basic denomination of data –
that is, either binary data or just plain text
Note As we've said, ADO 2.5 is the version of ADO released with Windows 2000. If
you're using another operating system then you'll be able to acquire ADO 2.5
as part of the MDAC 2.5 download from the Microsoft web site. Watch out for
more up-to-date editions of ADO in the future!
Detailed discussion of these two objects is really beyond the scope of this book. However, the
following pages serve to introduce their purpose and usage.
Note If you'd like to know more about the ADO Record and Stream objects, the
titles Professional ASP 3.0 (Wrox, ISBN 1-861002-61-0) and Professional ADO
2.5 (Wrox, ISBN 1-861002-75-0) provide progressively more detailed
explanations.
Semi-Structured Data
The main purpose of these two new objects is to facilitate the movement of data access
techniques towards the new arena of semi-structured data. Up to now, we've been considering
databases, which have a rigid structure of tables, rows, and fields. Data held within spreadsheets,
mail servers (and so on) doesn't conform to the same rules. For example, consider the folders on
the mail client that we use at Wrox Press. We store copies of each feedback message we receive
in a folder system – the messages are categorized by subject area, and then by book title, and
finally by chapter. The mail folder structure that reflects this categorization looks something like
this:
In fact, this system of mail folders resembles nothing so much as the file and folder system you
might see if you started Windows Explorer and browsed your own hard drive. The point is that the
data contained in here certainly wouldn't fit neatly into a database – yet the data storage system
still has a well-defined structure.
In this example, we replace the notion of a table row that represents a single record with a new
idea, whereby each node in this folder structure is considered to represent a record. Thus, each
folder (such as the ASP folder in the diagram above) is a record in its own right.
As you can see, the ASP folder contains several subfolders or subnodes – we can refer to these
as children. As each node can have several children, these children will be grouped together into
a recordset.
What we're saying is that the ASP folder is a record in its own right, but it is also a field name in
the Web recordset. Let's now take a closer look at the ADO object used to represent these
records and fields, namely the Record object.
In order to use a Record object, we have to populate it with the data that we want to work with –
and once again, we can do that using the Open method of the Record object.
This method takes two arguments: the first is the file or folder that we're looking for, and the
second is the URL to which any actions will apply. In this case, the record will represent the file
named Delete.asp, which it expects to find in the physical directory corresponding to the
BegASP/ virtual folder on my web server. Having opened the Record object on that folder or file,
we can use it to access the different field names in the record. This information is stored within a
Fields collection.
Rather than explain in laborious detail, it's easier to look at how you can access this information
with a quick example.
How It Works
We start by initialising two variables. One will be used to refer to an instance of the Record
object, and the other will hold the different fields of the record. Then we create our instance of the
Record object:
Dim objNodeRecord, objNodeField
Set objNodeRecord = Server.CreateObject("ADODB.Record")
Then we open the URL of our node that will be our record:
objNodeRecord.Open "","URL=http://my_server_name/BegASP/"
To display the contents of our record, we create a two-column table. The first column will contain
the names of the fields of the record. The second column will contain the values of those fields for
our record:
Response.Write "<H2>Properties of the folder:</H2>"
Response.Write "<TABLE BORDER='1'>"
For Each objNodeField in objNodeRecord.Fields
Response.Write "<TR>" & _
"<TD>" & objNodeField.Name & "</TD>" & _
"<TD>" & objNodeField.Value & "</TD>" & _
"</TR>"
Next
Response.Write "</TABLE>"
We iterate through fields of the record, using a For Each…Next loop, displaying the Name and
Value properties of each Field object in the Record object's Fields collection. As you can
see, the fields of this record represent properties of the folder such as its absolute name, and
when the folder was created and last written to.
The functions of these three methods are all fairly obvious – and as they suggest, they manage
the resources in a more 'physical' way. That makes sense when you think about it: dealing with
database data is easier because all the data is stored within one database; but when you're
dealing with something like an email folder system, the location of your record is likely to be
related to the overall organization of the data store.
What happens when you try to make a copy of a file, only to find that there's already a file or
folder with the same name as your specified destination? In that case, you can use the fifth
parameter to the Open method to specify what behavior you prefer. This parameter will take one
of the CopyRecordOptionsEnum constants, some of which are shown here:
adCopyOverWrite allows the existing file to be overwritten with the new file
adCopyNonRecursive allows you to copy only a folder, and none of the folder's
subfolders
You specify this option with the Open method, like this:
objRecord.Open "File.txt", "URL=http://chrisu/BegASP/", , ,
adCopyOverWrite
Note that this is specified as the Open method's fifth parameter – here, we've left the third and
fourth parameters empty. The missing third and fourth parameters would usually be used to
specify UserId and password for remote access to the file on your server. So the full set of
parameters is:
Like CopyRecord, there's a limitation on this method – you cannot use it to move a record from a
folder into one of it's own subfolders. And again, the MoveRecord method allows you to specify a
UserID and password to the data source if appropriate:
objRecord.Open = "File.txt", "URL=http://chrisu/BegASP/", _
"UserId=Chrisu", "Password=OpenSesame"
The fifth parameter allows you to select from a number of options. Valid values are:
adMoveOverWrite allows you to overwrite an existing file of the same name
adMoveDontUpdateLinks allows you to move the file without updating any hyperlinks
to the file
For example:
objRecord.Open = "File.txt", "URL=http://chrisu/BegASP/", _
"UserId=Chrisu", "Password=hello", adMoveOverWrite
Try It Out – Using your web page to manipulate files on the web
server
Now you have an idea of how to move the records around on the server, let's run through an
example that allows you to copy, move or delete a record on your web server. To give you an
idea of how it works, we'll create a simple text file called MyFile.txt, and a new web folder
called TestFolder, and we'll write an example that allows us to move the file between the
TestFolder and BegASP web folders, or delete it altogether. It takes a bit of preparation
because we need TestFolder to be a proper virtual directory, but once we've done that the
code is quite simple.
1. First we'll create the file – this will be the node that our Record object represents. Start
Notepad, type in some random text, and save it in your \inetpub\wwwroot\
BegASPFiles folder with the name MyText.txt.
2. Now we'll create a new folder. On your web server machine, start Windows Explorer; in
the \inetpub\wwwroot folder create a new subfolder, called TestFolder. Now, the \
inetpub\wwwroot folder should contain two child subfolders called TestFolder and
BegASPFiles.
3. Now fire up the Microsoft Management Console (MMC) – select Start | Run and type
MMC, and press OK; then select Console | Open and navigate to the snap-in file iis.msc
(which you'll probably find in your C:\WINNT\system32\inetsrv folder – if not, do a
search to find it on your machine). The IIS snap-in in the MMC should reveal the TestFolder
file as being a regular file.
Right-click on TestFolder and select Properties; then on the Directory tab, click the Create
button to turn TestFolder into an ASP application:
4. That's the preparations; now here's the code. Create the following new file, called
RecordMove.asp, using your ASP editor:
5. <%Option Explicit %>
6. <!-- METADATA TYPE="typelib"
7. FILE="c:\program files\common files\system\ado\msado15.dll"
-->
8. <HTML>
9. <HEAD>
10. <TITLE>Retrieving Semi-Structured Data</TITLE>
11. </HEAD>
12. <BODY>
13. <%
14. If Request.Form("Task")="" Then %>
15. <FORM NAME=MovieInfo ACTION="RecordMove.asp" METHOD="POST">
16. What do you want to do? <BR><BR>
17. <INPUT TYPE="RADIO" NAME="Task" VALUE="Copy" CHECKED>
18. Copy MyText.txt to a new file called MyText2.txt</INPUT><BR>
19. <INPUT TYPE="RADIO" NAME="Task" VALUE="Move">
20. Move MyText.txt to a new file called MyText2.txt</INPUT><BR>
21. <INPUT TYPE="RADIO" NAME="Task" VALUE="Delete">
22. Delete MyText.txt from the BegASP file</INPUT><BR>
23. <INPUT TYPE=SUBMIT VALUE="Go">
24. </FORM><%
25. Else
26. Dim objRecord
27. Set objRecord = Server.CreateObject("ADODB.Record")
28. objRecord.Open "MyText.txt", "URL=http://my_server_name
/BegASP"
29. If Request.Form("Task") = "Copy" Then
30. objRecord.CopyRecord "", _
31.
"http://my_server_name/TestFolder/MyText2.txt",,,adCopyOverWrite
32. Response.Write "MyText.txt file copied to a new file called
MyText2.txt"
33. ElseIf Request.Form("Task") = "Move" Then
34. objRecord.MoveRecord "", _
35.
"http://my_server_name/TestFolder/MyText2.txt",,,adMoveOverWrite
36. Response.Write "MyText.txt file moved to a new file called
MyText2.txt"
37. ElseIf Request.Form("Task") = "Delete" Then
38. objRecord.DeleteRecord
39. Response.Write "MyText.txt has been deleted"
40. End If
41. objRecord.Close
42. Set objRecord = Nothing
43. End If
44. %>
45. </BODY>
46. </HTML>
5. Save Recordmove.asp into your \inetpub\wwwroot\BegASPFiles folder.
6. Open Recordmove.asp in your browser of choice. Because the Request.Form
collection is empty, you'll see the following form:
7. Select the first radio button, which will have the file copied to a new filename in the
TestFolder directory. Then hit Go, an the page will reload – this time it will detect the
value of Request.Form ("Task"), copy the file and then display a report. But don't just
take the word of the ASP page. Instead, go to Windows Explorer and the check this for
sure:
8. Use Windows Explorer to delete this occurrence of MyText2.txt, go back to Step 6 and
try the other two options.
How It Works
The page is split into two. The first part is an HTML form that is just designed to find out which
task you want to perform – copy, move or delete. There's nothing new here – the real action is in
the second part of the form. We start by creating an instance of the Record object:
Dim objRecord
Set objRecord = Server.CreateObject("ADODB.Record")
We open our instance of the Record object, so that the record represents the MyText.txt file
that lives in the http://my_server_name/BegASP virtual directory:
objRecord.Open "MyText.txt", "URL=http://my_server_name/BegASP"
Then we check the Task value that was passed as part of the HTTP request. If it's equal to Copy
then we execute the CopyRecord method to copy our file from its current location to the new
TestFolder directory, with the filename MyText2.txt. Then we display an appropriate
message:
If Request.Form("Task") = "Copy" Then
objRecord.CopyRecord "", _
"http://my_server_name/TestFolder/MyText2.txt",,,adCopyOverWrite
Response.Write "MyText.txt file copied to a new file called
MyText2.txt"
Otherwise, if it's equal to Move, then we execute the MoveRecord method to move our file to its
new location; then we display an appropriate message:
ElseIf Request.Form("Task") = "Move" Then
objRecord.MoveRecord "", _
"http://my_server_name/TestFolder/MyText2.txt",,,adMoveOverWrite
Response.Write "MyText.txt file moved to a new file called
MyText2.txt"
Otherwise, if it's equal to Delete then we delete the file altogether, and display a message:
ElseIf Request.Form("Task") = "Delete" Then
objRecord.DeleteRecord
Response.Write "MyText.txt has been deleted"
End If
In order to create an instance of the ADO Stream object, we use the Server object's
CreateObject method. The ProgID for the ADO Stream object is ADODB.Stream:
Set objStream = Server.CreateObject("ADODB.Stream")
This example opens the Stream object on the stream of text contained in the file ReadMe.txt,
which is contained in a virtual directory of my web server machine. It specifies values for three of
the parameters:
Source: this the URL of the file whose content we want to access
ModeofAccess: the second is the mode of access (whether we're allowing read or write
access). The example above specifies the value adModeRead, which allows read-only
access to the contents of the file. A value of adModeReadWrite would allow read and write
access to the source file
OpenOptions: the third parameter specifies from where we're getting the data. The value
adOpenStreamFromURL specifies that we're opening the stream using a URL as the
source, while the value adOpenStreamFromRecord should be used when opening a
stream using an existing Record object
Note We're not going to cover the full list of available parameter values here,
because it's beyond the scope of this book. You can find more details in
our title Professional Active Server Pages 3.0 (Wrox, ISBN 1-861002-61-
0).
The Open method has two more optional parameters, which we can use to specify a UserID and
password when accessing the web server remotely; so the full syntax for the Open method is:
Before we try an example, we'll quickly cover some of the Stream object's other methods and
properties.
If you try this you'll find that the output to the page takes the form of a sequence of question
marks, and not the text that you might have expected. This is because the Stream object needs
to know which character set it is supposed to be using. In order to give that information, we also
need to use the Charset property.
Of course, we need to think about at what point in the stream we want the new string to be
added. For that, we have the Position property.
This would set the pointer for writing to the beginning of the file.
Alternatively, we can force the stream to end at a particular position by using the setEOS
method, which updates the value of the EOS property by setting it to be the same as the current
Position. For example:
objStream.Position = 0
objStream.SetEOS
This would set the end of stream property to 0. Beware – when you call SetEOS, you'll lose any
data that lies between the position specified in the Position property and the old EOS. In
particular, the above two lines will succeed in deleting the contents of the file.
We haven't covered all of the Stream object's methods and properties here, but we've covered
enough to put it into practice. Let's look at an example that allows the user to change the contents
of a text file. Here's what we'll do: we'll ask the user to provide the URL of a text file that needs
backing up, and a location for the new backup file to be placed. We'll use a Record object to
create the copy of the text file. Then we'll open a Stream object on the contents of the backed-up
file, and we'll change the contents of the stream, adding a datestamp.
Note In order to keep the example simple, we haven't written any error
handling – so be careful to enter a valid URL in each text box.
54. When you click the Create Backup button, the CreateBackup.asp page will create the
backup and add the datestamp to the backed up copy; and you'll see a message in the
browser, informing you that the backup has been created.
But the real proof of the pudding is in the eating, so use your Windows Explorer to open
the backup file and view its contents
How It Works
The first page, PromptForURL.htm, just contains an HTML form that prompts the user for the
URLs of the original file and the desired location for the backup copy:
<FORM NAME=form1 METHOD=POST ACTION=CreateBackup.asp>
Type the full URL of the file to be backed up here:<BR>
<INPUT TYPE=TEXT SIZE=50 NAME=URLofFile><BR><BR>
Type the full URL of the desired location for the backup file
here:<BR>
<INPUT TYPE=TEXT SIZE=50 NAME=URLofBackup><BR><BR>
<INPUT TYPE=SUBMIT VALUE="Create Backup">
</FORM>
These URLs are passed to the page CreateBackup.asp, where they are accessible via the
Request.Forms collection (with the names URLofFile and URLofBackup). The first thing we
do there is to save those values to local variables:
strURLofFile = Request.Form("URLofFile")
strURLofBackup = Request.Form("URLofBackup")
The rest of the page is concerned with two tasks: first, to create the backup copy (which we'll do
using the Record object's CopyRecord method); second, to append the datestamp to the
contents of the backup copy (which requires the Stream object's ReadText and WriteText
methods).
So let's deal with the first task. We create a new ADO Record object, and open it using the first
URL:
Set objRecord = Server.CreateObject("ADODB.Record")
objRecord.Open "", "URL=" & strURLofFile
Now we create the copy of the file. This is just like the code we saw in the RecordMove.asp
example, earlier in the chapter. The first parameter of the CopyRecord method is blank, ensuring
that we take the default (i.e. the record on which the Record object is opened); the second
parameter is the URL of the destination of the backup copy. The option adCopyOverWrite is
used to indicate that writing over an existing version of the destination file is allowed:
objRecord.CopyRecord "", strURLofBackup, , , adCopyOverWrite
This is enough to create a copy of the original file. Now the Record object has done its job, we
can Close it and release its resources:
objRecord.Close
Set objRecord = Nothing
And that's the first task done; now to the second. We'll start by defining a character string
containing the text of the datestamp:
strDatestamp = "'This backup file was created on " & Date & " "
The datestamp string contains the current date, which is generated when needed using the
VBScript Date function.
Now we'll open the backup file for modification. To do this, we open an ADO Stream object on
the backup file, so that the Stream object represents the character stream of that file:
Set objStream = Server.CreateObject("ADODB.Stream")
objStream.Open "URL=" & strURLofBackup, adModeReadWrite,
adOpenStreamFromURL
We've specified three parameters here. The first parameter is a URL specifying the source of the
stream. The second parameter gives us read and write permissions of the stream, and the third
indicates that we're opening a stream from a URL.
Note Note: in a prerelease version of msado15.dll for ADO2.5, the constant
adOpenStreamFromURL was undefined. If you have difficulty with this, try
adding the following line to your ASP before this point:
Const adOpenStreamFromURL = 8
This is enough to define the missing constant.
We need to specify the character set we're using, to prevent ADO from assuming that we are
using UNICODE and generating an error. To specify the character set as ASCII we set the
Charset property:
objStream.Charset = "ascii" ' select character set
Now we read the existing contents of the file into a variable called strText:
strText = objStream.ReadText ' read stream into local
variable
Now that we have the original contents of the file stored in a string variable, we can write new
contents to the file. First, we set the current position to the beginning of the stream, and the EOS
(end-of-stream) property to the same position (thus cleaning out the entire stream):
objStream.Position = 0 ' set current position
objStream.SetEOS ' set EOS to be
position 0
Then we write our new text into the file. The new content of the stream will consist of the
datestamp first, followed by two carriage returns and then the whole of the original contents of the
text file:
objStream.WriteText (strDatestamp + vbcrlf + vbcrlf + strText)
Next we write to the browser to inform the user that the backup process has been completed.
Response.Write "Backup created."
In this section we've tried to provide only a very brief introduction to ADO's two new objects – the
Record and Stream objects. An in-depth discussion is beyond the scope of this book, but
hopefully the techniques demonstrated here will go some way to illustrating the kind of
functionality that is available from these objects, particularly in the remote administration of web
sites, and will tempt you to investigate them further. For more information, try Professional Active
Server Pages 3.0 (Wrox, ISBN 1-861002-61-0) and Professional ADO 2.5 (Wrox, ISBN 1-
861002-75-0).
Summary
This chapter ends our whirlwind three-chapter tour of ADO. Having already established a
foundation in using the Connection and Recordset objects, and in requesting data from
databases, this chapter broadened our ADO horizons by looking at the remaining major objects in
ADO:
The Command object, which allows us to run SQL statements against a database
The Record object, which allows us to represent non-database structures, as having
records and fields
The Stream object, which is used to represent binary data or plain text, held in a record
In addition to talking about these three objects, we introduced the notion of writing a SQL
command that tells ADO exactly what we want from the data store. We identified two important
types of SQL command:
A SQL SELECT command can be used to request data from the data store. It gives us
flexibility over what data we request because it means that each data query doesn't
necessarily have to ask for an entire table – instead, we can be choosy over which fields and
records we want to see.
The SQL UPDATE, INSERT and DELETE commands are collectively known as action
commands. They are different because (unlike SELECT) they are all used in the process of
making changes to the database – changing records, adding new records and deleting
records.
We looked at how we can use ASP logic to generate our SQL commands dynamically – allowing
the user to choose and dictate, at runtime, the records and fields to be selected, amended,
inserted or deleted. Using a SQL command is as simple as specifying the command string as a
parameter of the Recordset object's Open method, or the Connection object's Execute
method; or by assigning the command string to the Command object's CommandText property
and then running the Command.Execute method.
We saw that there are also two very direct ways to execute commands on the database:
Methods of the Recordset object, such as AddNew, Delete, Update and
UpdateBatch
Calling an Access query or a stored procedure – a precompiled command that is written
and contained within the database (in this case, we can extend the capability by the use of
parameters in our precompiled queries and stored procedures, which requires the use of the
Parameters collection of the Command object)
Lastly, we touched briefly on the subject of manipulating data contained in sources other than
databases. To do this we introduced the two new objects of ADO 2.5 – the Record and Stream
objects. They allow you to manipulate the records and the contents of records. We ran through a
few quick examples of how they might be put to use on some simple web administration tasks.
We'll be using ADO for our data access in the remaining chapters of this book. In the next
chapter, we'll be showcasing ASP and ADO functionality in one big example – the Wrox
Classifieds ASP application. Over the course of the chapter, we will see what it does, how to
build it, and how it all works.