The most powerful aspect of server-side scripting languages such as ASP is their almost magical ability to turn the Web â€“ a medium originally crafted to serve one static document after another – into a platform that delivers rich, interactive experiences. These experiences come in the form of online applications that respond differently to different users in different situations.
To make this transformation possible, ASP must not only be able to produce dynamically-generated Web pages in response to requests. It must also have the ability to track information that’s specific to the users of a set of related ASP scripts (e.g. an online discussion forum), and permit those scripts to share information with each other.
In this, the fourth article in SitePoint’s series on ASP, I’ll be describing the features of ASP that make the above possible: Applications and Sessions. To start, I’ll take you through the steps required to create a Web Application in Microsoft Internet Information Server (IIS), and I’ll explain how applications and sessions fill the need for persistent data. I’ll then introduce you to the
Session objects in ASP that are the center of the magic, and also explain the role of the
global.asa file. Finally, I’ll take you through the code of a complete example that deals with tracking the number of users on your site at any given time.
This article assumes that you’re familiar with the concepts covered in the previous articles in this series, beginning with Getting Started with ASP. If you haven’t read them, and you’re approaching ASP for the first time, I would suggest at least skimming through those previous articles before you jump into this one.
Here we go!
Web Applications in IIS
A Web application is a lot like any other software application that you’d run on your computer, except that the interface is provided by a set of Web pages, and the code of the application runs mainly on the Web server from which you fetch those pages. Examples of common Web applications today include discussion forums, book stores, and custom news feeds.
From the point of view of an ASP developer, however, a Web application is just a collection of ASP files that work together to provide this kind of service. This group of ASP files is generally stored in a single directory, and possibly a number of subdirectories. The first step in taking advantage of the features that ASP provides to support the development of Web applications is to identify that ‘application root directory’ to IIS.
I’ll assume for the moment that you have administrator-level access to the IIS server that you’re using for ASP development. In this case, your first task is to define a Web application on your server. Create a directory anywhere on the server (I chose
c:MyApplication) that you’ll use for the examples in this article. Open Control Panel, Administrative Tools, and Internet Information Services, then navigate to the Default Web Site. Right-click on it and select New, Virtual Directory. When prompted by the wizard, type an alias for the directory as it will appear in the URL (e.g. a virtual directory with the alias
myapp will be accessible as
http://localhost/myapp/ from your computer and as
http://your.host.name/myapp/ from other computers on your network), and then browse to the directory you just created. For the access permissions, leave the default options (Read and Run Scripts) selected. When you’ve completed the wizard, the new virtual directory should appear in the tree view as shown here:
You’ve now defined a Web application on your server. All the ASP files contained in that directory (or its subdirectories) will be considered a part of the
myapp application (or whatever you chose to call it). Before we explore further the features of ASP that take advantage of this, let’s stop and examine what we need a Web application for.
The Need for Persistent Data
As we’ve seen in previous articles in this series, you can store values in ASP variables and display them as part of a dynamic Web page. One limitation of variables, however, is that they only exist for as long as the particular ASP script that created them runs. Once an ASP script has ended, and the last of the Web page has been sent to the requesting browser, all the variables that were created during the course of the script’s execution are destroyed to free up memory, to enable later page requests to be processed.
There are many situations where it would be handy to have a variable that stayed in memory at the end of one ASP script so that the value stored within it could be picked up and used again by another script later on. In the last article, we saw how we could pass information from one page to another, either through a form submission or with a URL query string; however, these methods are not always adequate.
For instance, take the example we looked at in that article. Here we prompted the user for his or her name with an HTML form that appeared on the first page requested when they entered the site. We then passed that name on to each additional page with a query string that contained the name the user entered. Imagine the implications of putting this example to practical use on a site the size of SitePoint.com. Every single link in every single page would have to be individually coded to pass the username variable on to the next page. Plain HTML pages could not be used even when it wasn’t necessary to display any dynamic data on a given page, because an HTML page would not be able to pass on the username to subsequent pages.
As you can see, when your goal is to create a variable that you can use throughout your site, passing it from one page to the next is rarely a practical solution. Fortunately, ASP Web Applications were designed with just this sort of thing in mind. You’ll also recall from our last article that ASP has a number of built-in objects –
Response being prime examples of these. Two other objects that all ASP scripts have access to are
Session, and it is these special objects that let us store data that will be accessible by all ASP scripts in the current Web application.
The Application Object
Whereas every page request gets its own
Response objects, all requests for ASP pages in a Web application share the same Application object. This object is created the first time an ASP page is requested from the application after the Web server starts up, and is destroyed when the Web server shuts down, or when the Web application is unloaded manually in the IIS management console. Because this object persists from one page request to another, it can be used to store data that you want to share with all other pages in your application.
To store a value in the application object, simply use the following syntax:
Application("varName") = value
You can then access that stored value as you would a regular variable, except as
Application("varName"). A simple use of the Application object would be for a hit counter. You could record the number of hits your Website has received as
Application("hits"), and increment that value with each page request. Here’s the basic code involved:
1 <% Option Explicit %> 2 <html> 3 <head> 4 <title> Simple counter </title> 5 </head> 6 <body> 7 <p>Page requests: <% 8 Application("hits") = Application("hits") + 1 9 Response.Write(Application("hits")) 10 %></p> 11 </body> 12 </html>
Line 8 adds 1 to the value of
Application("hits"), and line 9 then prints out the updated value. Save the above code in an ASP file (e.g.
counter.asp) in your application directory, and then load it repeatedly into your browser (e.g.
http://localhost/myapp/counter.asp). Watch as the count increases by 1 with each page request. By including this code fragment on each page of your site, you can count the total number of page requests, as all of your pages will share the same
While the above method of value storage and retrieval represents the way you’ll use the
Application object 90% of the time, this object does have a few more methods you should be aware of.
First of all,
Application("hits") is actually just a shortcut. The hits variable is actually a member of a collection called
Contents, which is a property of
Applications. Thus, the longhand version is
Application.Contents("hits"). There’s no reason for you to use this longer version when accessing stored values, but you must use it when you remove a stored value from the application. To do this, call the
Remove method of the
Contents collection, as follows:
This code would delete the
"hits" stored value from the application. To remove all stored values from the application, you can use the
To understand the purpose of the remaining two methods of the
Unlock, we must first take a close look at Line 8 from the hit counter example above:
8 Application("hits") = Application("hits") + 1
As you can see, this line actually accesses the stored application value twice. The first time (on the right of the equals sign), it retrieves the stored value. Then, after adding 1 to the retrieved value, it accesses the stored value a second time (on the left of the equals sign) to record the updated value over the top of the existing value.
What’s important to understand is that the three steps described above do not necessarily occur as an uninterrupted sequence of events. If two Web browsers were to access your site at the same time, the events for each browser could intermingle, producing a sequence like this:
- browser 1 reads "hit"
- browser 2 reads "hit"
- browser 1 stores "hit + 1"
- browser 2 stores "hit + 1"
If you look at this sequence closely, you’ll realize that the two hits that should have been recorded only resulted in an increase of 1 to the stored "hit" value! To avoid this happening, your code should "lock" the Application object before undertaking a series of events like this that should occur in an uninterrupted sequence. Thus, Line 8 above should be replaced with these three lines:
8 Application.Lock 9 Application("hits") = Application("hits") + 1 10 Application.Unlock
The call to
Application.Lock on Line 8 waits for any other processes to finish accessing the
Application object, before locking access to it. This forces other clients to wait until the call to
Application.Unlock on Line 10 before they can read or write the stored values again. This effectively prevents the occurrence of mix-ups like the one above, as ‘browser 2′ would have to wait for ‘browser 1′ to read and then store its updated value before it could read the value and update it itself.
The Session Object
Session object is very similar to the
Application object, as it allows you to store values that are shared between all the pages of your site. The main difference between the two is that, where a single
Application object is shared by all pages and all clients that access your site, each client (browser) is assigned its own
Session object must be created for each user session that occurs on your Website. Here’s an updated version of the hit counter example we saw in the previous section:
1 <% Option Explicit %> 2 <html> 3 <head> 4 <title> Simple counter </title> 5 </head> 6 <body> 7 <p>Your page requests: <% 8 Session("hits") = Session("hits") + 1 9 Response.Write(Session("hits")) 10 %></p> 11 <p>Total page requests: <% 12 Application.Lock 13 Application("hits") = Application("hits") + 1 14 Application.Unlock 15 Response.Write(Application("hits")) 16 %></p> 17 </body> 18 </html>
Save this page and open two copies of your Web browser. It’s important that you actually launch your browser application twice – opening it once and then opening a new window will make the two windows part of the same session, whereas actually opening two separate copies of your browser will cause them each to be assigned separate Session objects on your server. Load the above page into both browsers, and refresh it a few times in each. Observe that the counter stored in the
Session object only counts the hits from each browser, while the counter stored in the
Application object stores the total page request count.
A typical use of the
Session object is to store a user’s shopping cart on an online shopping site. Obviously it doesn’t make sense for all your users to share a single shopping cart, so the fact that each user gets a
Session object of his or her own fits the bill precisely.
As for the
Session("hits") is actually just shorthand for
Session.Contents.Remove("hits") can be used to delete a single stored value, while
Session.Contents.RemoveAll will delete all values stored in the current session.
Every user that connects to your site is assigned his or her own
Session object for the duration of his or her visit, so there must be some mechanism in place to prevent old
Session objects from adding up and consuming all the memory on your server. By default, a
Session object will expire and be deleted from your server if the client that corresponds to that session doesn’t request a page from your server for 10 minutes. In most cases, this means that the user has closed the browser or has moved on to another Website. If your application has a typical ‘time between page requests’ of more than 10 minutes, this default timeout value will prevent your application from working properly. To specify your own session timeout interval, set the Timeout property of the Session object:
Session.Timeout = 30 ' The current session will end after ' 30 minutes of inactivity
You can also end the current session immediately, for instance, if you wanted to ‘log out’ the current user from your Web application:
The global.asa File
global.asais a special file that you can create in the root directory of your Web application. It specifies the actions to be taken in response to four events:
- Application end
- Session start
- Session end
Application_OnStartcan be used to set up initial values for application variables, or perhaps to restore some saved information from the last time the Web server was run, by loading it from a file or database.
Application_OnEnd, meanwhile, is generally used to clean up any open resources, and save information that will be needed the next time the Web application is started.
Session_OnEndperform similar functions for user sessions. Let's create a simple
global.asafile to see these events in action. Open Notepad (or your text editor of choice) and create a file called
global.asain the directory that houses your Web application. Type the following as the contents of this file:
1 <script language="VBScript" runat="Server"> 2 Sub Application_OnStart 3 ' Commands to be run at startup 4 End Sub 5 6 Sub Application_OnEnd 7 ' Commands to be run at shutdown 8 End Sub 9 10 Sub Session_OnStart 11 ' Commands to be run at session start 12 End Sub 13 14 Sub Session_OnEnd 15 ' Commands to be run at session end 16 End Sub 17 </script>
This is the basic structure of a
global.asafile. Let's say, just for fun, that we wanted to start the count of our global
(Application)hit counter at 1000. The code for the
/Application_OnStartsection (lines 2-4) might look like this:
2 Sub Application_OnStart 3 Application("hits") = 1000 ' Pad the numbers :) 4 End Sub
In a more practical example, you might use
Application_OnEndto save the current hit count into a text file when the server is shut down, and
Application_OnStartto load that value when the server starts up again.
Putting it All Together To demonstrate the use of
global.asatogether in a single system, let's create a more practical and interesting version of the example we saw last time. In this example, users were prompted for their name, and were then welcomed by a personalized message at the top of each page they visited. This time, we'll use the
Sessionobject to track each user's name, instead of having to code every link on our site to pass the variable onto the next page. We'll also use the
Applicationobject to track the number of users on our site (and how many of those have given us their name). To start, create a new
global.asafile with the following contents:
1 <script language="VBScript" runat="Server"> 2 Sub Application_OnStart 3 Application("users") = 0 4 Application("namedUsers") = 0 5 End Sub
When our application starts up, we set the two variables (the first of which tracks the number of users, while the second variable tracks users who have logged in by providing their names [named users]), to zero.
6 Sub Session_OnStart 7 Session.Timeout = 5 ' minutes 8 Session("name") = "" 9 Application.Lock 10 Application("users") = Application("users") + 1 11 Application.Unlock 12 End Sub
When a new user connects to our site (starting a new session), we set a
Session.Timeoutvalue of 5 (minutes) - the session will end after 5 minutes of inactivity (instead of the usual 10). We then create a variable to store the user's name (which he or she may eventually provide), and set it to "" (the empty string), which indicates that the user is as yet unnamed. We then
Applicationobject while we increase the number of users by one.
13 Sub Session_OnEnd 14 Application.Lock 15 Application("users") = Application("users") - 1 16 If Len(Session("name")) > 0 Then 17 Application("namedUsers") = Application("namedUsers") - 1 18 End If 19 Application.Unlock 20 End Sub 21 </script>
When a user leaves the site, and their session times out after the requisite 5 minutes' inactivity, we again
Applicationin preparation for the changes we're about to make, and decrease the user count by 1. Next, we check the length of the string that's stored in
Session("name")with the help of the built-in
Len()function. If the string is longer than zero characters (i.e. ""), we know that the user has logged in by providing a name, so we also decrease the
namedUserscount by 1. Now we need to create a sample page on our site. Name the file
default.aspand type the following code:
1 <% Option Explicit %> 2 <!-- #include FILE="login.asp" --> 3 <html> 4 <head> 5 <title>My Sample Application</title> 6 </head> 7 <body bgcolor="#FFFFFF" text="#000000"> 8 <p><!-- #include FILE="welcome.asp" --></p> 9 </body> 10 </html>
The magic is in the
<!-- #include ... -->tags. As there'll be a significant chunk of code involved in handling both the login process, and in displaying the tally of users, and since this is something we'll presumably want to handle on every page of our site, we're going to place the code for each of these functions in separate files (
welcome.asprespectively). This means we'll only have to type this code once in order to have all pages on our site share the same code. This
<!-- #include ... -->tag basically inserts the contents of the specified file at the location of the tag, before the ASP code in the file is processed. So our next task, obviously, is to create the
welcome.aspfiles. We'll start with
1 <% 2 ' Displays welcome message or login prompt 3 If Len(Session("name")) > 0 Then 4 Response.Write("Logged in as: " + Session("name")) 5 Else 6 Response.Write("Not logged in. <a href=""") 7 Response.Write(Request.ServerVariables("SCRIPT_NAME")) 8 Response.Write("?login=1"">Log in</a>") 9 End If
We start by checking the length of the
Session("name")variable to see if the user has logged in. If they have, we display the message /"Logged in as: name". Otherwise, we display "Not logged in", followed by a link for the user to log in. The code for this link may seem a little complicated at first, but bear with me as I explain it. First we print out the start of the link tag
(<a href="). Because we're already using quotes to tell
Response.Writewhere the string that's to be sent to the browser begins and ends, we use double quotes ("") for the quotes that appear in this bit of code. The third quote is the one that marks the end of the string, as usual. Next, we output the
"SCRIPT_NAME"server variable. As we have seen before, this will make the link point back to the very same page that is currently being processed. Finally, we tack
?login=1onto the end of the URL, to signal the user's intent to log in (
login.aspwill watch for this, as we'll see in a moment). Next, we print out the tally of users (and how many are logged in) by simply outputting the appropriate
10 ' Display tally of users 11 Response.Write("<br>" & Application("users") & " users online") 12 Response.Write(" (" & Application("namedUsers") & " logged in)") 13 %>
That's all there is to
welcome.asp; now here's
1 <% 2 ' User wishes to log in 3 If Request.QueryString("login").Count > 0 Then 4 %> 5 <html> 6 <head> 7 <title> Log In </title> 8 </head> 9 <body> 10 <form action="<%=Request.ServerVariables("SCRIPT_NAME")%>" 11 method="POST"> 12 <p>Enter your name: 13 <input type="text" name="name"> 14 <input type="submit" value="OK"></p> 15 </form> 16 </body> 17 </html> 18 <% 19 Response.End 20 End If
First, we catch page requests that result from the user clicking the "Log In" link generated by
welcome.aspabove. As we saw in the previous article, we spot this by checking if
Request.QueryString("login").Countis greater than zero. When such a request is found, we display a simple page that prompts the user for his or her name. The form submits to
Request.ServerVariables("SCRIPT_NAME"), so our next task in this script is to process submissions of this form, which will be marked by
Request.Form("name").Countgreater than zero:
21 ' User is logging in 22 If Request.Form("name").Count > 0 Then 23 Session("name") = Request.Form("name") 24 Application.Lock 25 Application("namedUsers") = Application("namedUsers") + 1 26 Application.Unlock 27 Response.Redirect(Request.ServerVariables("SCRIPT_NAME")) 28 Response.End 29 End If 42 %>
This code begins by taking the name submitted in the form (
Request.Form("name")) and storing it in
Session("name"). Then we
Applicationand increase the number of named users by 1. Finally, we redirect the user to the page that was originally requested. With all these files in place, load
http://localhost/myapp/default.asp) in a few instances of your browser, and you should see the system in action!
Summary In this article we explored the primary mechanisms provided by ASP to support persistent data. With the
Applicationobject, we can store data values that are shared by all pages and all users on our site, such as the simple hit counter that we looked at. The Session object, meanwhile, gives the power to associate persistent data with each active user on our site. In our final example, we used this to store the name of each user and display it at the top of each page. In a public Web forum, this stored value could save the user from having to type his or her name to post a message. Used in combination with a database that stored a list of authorized users and their passwords, the system we developed in this article could be expanded to act as an access control system. In the next article in this series, we'll look at how ASP interfaces with a relational database such as Microsoft Access. This very powerful combination of ASP with a backend database is what drives many of the hottest sites on the Internet today.