Chapter 12

Enhancing Interactivity with Cookies, Headers, and the Server Object


The Good Stuff

In Chapter 11, "Building a Foundation of Interactivity with Request and Response Objects," you learned how form data moves between client and server. This is the Web's most basic kind of interactivity. In that chapter, you had one guestbook.asp file, and you called it once to update your Guestbook's data file. This chapter covers error trapping, and provides a fuller treatment of client feedback.

See "CGI Input" for more information about form data, in Chapter 11.

In this chapter, then, you enhance your basic Guestbook application with those two additional features. To accomplish this goal you need only remember what you already know about the Request Object and you will learn how to get a form to call itself. In Chapter 11, you had separate guestbook.html and guestbook.asp files. In this chapter, however, you only have one guestbook.asp file. As you will see, this "reflexive" feature of the Active Server Pages (ASP) may be its most useful feature (with the exception of ADO, which is treated in depth in Part IV, "Database Management with Active Server Pages").

Using its own output-Program Recursion

Recursion is a simple programming technique. You need it when a program must reuse its own output. The biggest problem with the technique is that it consumes memory at an alarming rate, and when recursion goes awry it will cause something called a "stack overflow." As a result, it is not commonly used.

By the way, you can visualize stack overflow if you've ever worked in a restaurant: Dinner plates are stored in spring-loaded cylinders. As you add plates, the stack gets higher, and as you take plates away, each plate in the stack moves up. If you stack plates too high, they can overflow onto the ground and make a loud mess. Now memory is silent as light, but it, too, can get in a muddle if you try to push too many calls to the same program into a limited amount of memory. Each call to a recursive program is a plate.

When an .asp file calls itself, however, the program contained in that file is not using recursion; it only looks as if it is. It's safe, and it will be one of the most immediately important features of ASP development. The key to the technique is the astute use of the Request.Form method.

There are several design objectives the ASP developer needs to keep in mind:

ON THE WEB

http://www.mcp.com/que/asp—The advanced Guestbook program, guestbookpro.asp, is included at the Web site. It uses the same Guestbook database as its less sophisticated cousin.

In this section you look at how .asp files work when they call themselves. In the next section, you include the code that you need to minimize overhead.

The ASP Program Model

The reason Active Server Pages have so little trouble calling themselves is ironic: ASP programs run on a stateless server. That's right-the one thing that makes database programming challenging makes reflexive programming easy. When you think about it, it's easy to understand: When an .asp (or HTML) file requests the attention of the server, it is served and then forgotten. Subsequent HTTP requests for the same file are totally unaffected by anything requested before. Form variables get passed exactly the same way with .asp files as with HTML files. In a sense, every instance of an .asp file is different from every other; the only thing they share is a name. Except for the referrer data (which contains the name of the file making the request) that's passed in the HTTP request, the request is an HTTP request, not the request of your specific Active Server Pages.

The point of all this is that your focus is on the HTTP request, not the file making the request. Think of each call made by an .asp file as one being made to a different file. In each case, the called .asp file has access to the Request.Form collection sent in the HTTP request transaction.

Setting Default Values

When an .asp file loads, the form fields it contains can have one of three values: a default (for example, you could hard-wire a value in its VALUE property), the value in some named parameter of the Request.Form collection, and, finally, a null value.

For .asp files that call themselves, the simplest technique is to put the Request.Form command in the VALUE property of the form field. For example, your Advanced Guestbook can use the code block in Listing 12.1 as a prototype for all the other fields:

Listing 12.1 GUESTBOOKPRO.ASP-Use Default Unless a Previous Value Was Entered

<%

varRealName = Request.Form("realname")

If IsEmpty(varRealName) Then

 varRealName = "Katy Corning"

End If

%>

The entry in the <FORM> block of the ASP program would look like this:

<INPUT TYPE="TEXT" NAME="realname" SIZE=70 VALUE="<%=varRealName%>">

From these examples you can see that the VALUE property is set with a variable, and the variable is initialized with NULL when the form is first called (or the Refresh button is pushed). If the variable is empty, initialize it with the field's default value.

However, on subsequent calls, the guestbookpro.asp file sees some value (perhaps the original default value, perhaps some value entered by the user) in the Request.Form collection's "realname" parameter. In this case, the variable used to set the VALUE property of the field gets the value in the Form collection. It's actually deceptively simple, once you get the hang of it.

Take a closer look at form fields that don't have VALUE properties.

On the Advanced Guestbook, you have a checkbox that the users select if they want to be notified of announcements on the Working with Active Server Pages Web site. To set the checkbox's default value, you have to see if it was "on" in the previous instance of the Guestbook form.

<%

If Request.Form("chkNotifyMe")="on" Then

 varCHECKED = "CHECKED"

End If

%>

The comparison is case sensitive. One thing you can do to control case sensitivity is force the left side of the expression to a case of choice, usually uppercase, using the UCase() function, and then compare the result to the test value (in uppercase if that's your case of choice).

From your example, it would look like this:

If UCase(Request.Form("chkNotifyMe"))="ON" Then


Actually, this is a good habit to use, regardless. That way you never have to wonder if something is case sensitive or not, or whether case sensitivity in an application changes as programs upgrade.

Caching Tasks

If your .asp file that calls itself uses ActiveX Data Objects (ADO), then you also have to attend to caching tasks. In this case, the stateless nature of HTTP is not a blessing as it was in "The ASP Program Model" section, but it's not quite a curse either. You just have to be careful that, if your .asp file has gone to the trouble of making a connection to an ADO data provider, you don't make it do that again each time the .asp file is called. This concern extends to any recordsets that might also be opened during previous calls to your .asp file.

As noted in Chapter 10, "Managing States and Events with Application and Session Objects," you can create Session Objects that are identified with individual parties using your Web application. Each user has a unique system-generated ID attached to every place she goes in your application. In the same way, ADO Connection and RecordSet Objects can be identified uniquely.

In your Advanced Guestbook you test for the existence of these objects in the manner of Listing 12.2:

Listing 12.2 QUESTBOOKPRO.ASP-Testing for Objects

<%If IsObject(Session("SessionConnection")) Then

   ' Reuse the ADO Objects

   Set objConn = Session("SessionConnection")

   Set objRst  = Session("SessionRecordSet")%>

   <FONT COLOR="Green">

   Using the Cached Session Connection and RecordSet

   </FONT><P>

<% Else

   ' This is the first time this user has called the file.

   ' Create the ADO Objects necessary to update the Guestbook.

   Set objConn = Server.CreateObject("ADODB.Connection")

   objConn.Open("Guestbook")

   ' Initialize the Session property with the Connection Object

   Session("SessionConnection") = objConn 

   Set objRst = Server.CreateObject("ADODB.Recordset")

   ' Initialize the Session property with the RecordSet Object

   Session("SessionRecordSet") = objRst %>

   <FONT COLOR="Yellow">

   Opening the Connection and RecordSet 

   </FONT><P>

<% End If %>

Take a good look at the Advanced Guestbook application at the Web site. I think you'll be amazed at how much work is being done with one program.

You have now completed your survey of basic Web interactivity using Active Server Pages instead of CGI. Now it's time to look at the next level of interactivity that this new technology enables. In the rest of this chapter, you will see that you can, not only enable a richer interactive environment for the user and the Web site, but also enhance the interactivity between the client program and the server. You will reach this higher level of interactivity between client and server because you will do so on their turf: HTTP transactions using Request and Response headers.

The Rest of the Request Object

You have covered the simplest and most often used methods and collections of the Request and Response Objects. You now complete your survey of the fundamentals of interactivity by exploring the nooks and crannies of cookies, server variables, buffers, headers, and other arcane features.

How to Get a Header in Life

On the Web, there are always three entities communicating with each other: the Web server, Web client, and the human. Clearly, the language, images, and perhaps sound displayed on the screen or sent through the computer's sound card are how both the server and client software communicate with the human.

But how do the server and client communicate with each other?

With headers. Headers come in all shapes and sizes. Well, actually they come in four varieties: General, Request, Response, and Entity headers. These collections of data tell the server vital information such as the media type of the returned entity. Remember, HTML can be almost any kind of information, from text, images, and sound to binary files and BLOBS. For all of the transporting infrastructure to work properly, knowledge of what is being transported is vital.

Most of the material in this section refers in one way or another to message headers.

The definitive word on HTTP/1.0 is found at http://www.ics.uci.edu/pub/ietf/http/rfc1945.html#Product. Section 4.2 describes all the details of message headers. A careful reading of this document is almost prerequisite to the serious use of most of the following features of the Response Object.

The Cookies Collection

Cookies are an odd name given to an important task in programming stateless environments like the Web. If you're interested in all the technical details of this technique, direct your browser to an Internet-Draft entitled "HTTP State Management Mechanism," which is the official word on cookies. It can be read at http://portal.research.bell-labs.com/~dmk/cookie-2.31-2.33.txt.

Cookies: A Basic Recipe

You first looked at cookies in Chapter 10, "Managing States and Events with Application and Session Objects." There, you learned about its use by the Active Server as a way to identify individual users and to do what's known as "manage state." In that context the Active Server did all the work. In this chapter, you look at reading and writing cookies yourself. I'll wager that most readers have not used cookies yet. Until I found the Cookies Collection, I hadn't either; I had no idea how. You will see that reading and writing them really couldn't be easier. In fact, of all the header-oriented methods in ASP development, working with cookies is the most direct and will probably be the most popular.

As you might guess, in the context of the Request Object, cookies are read. In the section entitled "The Rest of the Response Object" you will learn how to write them.

To properly understand the function of cookies takes some concentration. It's not difficult to understand, just backward. Here's what I mean: Cookies are unusual in HTML because they are data written to the local client storage media. Except, perhaps, ActiveX controls and Java applets, nothing else in HTML writes to disk. But the real function of Cookies is to request data. That is, cookies are used by clients to make specific requests of a particular server (defined as the domain and/or the path to an HTML document or an ASP application).

Cookies get confusing because we tend to think of them in the context of writing, not reading. Writing by the server is a necessary evil because Web clients usually never write data themselves; they must rely on the server to do that.

So, cookies are used to request things. Here's an excellent example of what I mean: When Microsoft first hosted the version of their Web site that could be configured to individual client preferences, they used cookies. Once the preferences were selected and stored on client computers, subsequent requests for the MSN home page looked just like previous ones. The cookies enabled the client to make a special request of the MSN Web server to deliver a custom document. See? Cookies are important because of what they do after they are created.

So, what's in a cookie? A name? Yes. But what if two different servers need the same name? As other sites enabled the client to customize the interface to the server, this became more likely. The solution is stored in cookie attributes. The two most important attributes are domain and path. One or both of these data points serve to uniquely match the cookie with its server. When the server wrote the cookie it could have included a path back to itself. Say your ASP application is found at your_company.com/data/web. Suppose further that you have another ASP application at your_company.com/data/web/tutorial. If both applications have a cookie named "last_visited" and if the client sends both "last_visited" cookies to the server, Request.Cookies("last_visited") will return the value of the cookie stored at your_company.com/data/web/tutorial.

Fancy Cookies

Cookies can also have "dictionaries." It's true. Dictionary objects can be anything that stores a term and a value, like its namesake stores a term and a definition. When it comes to cookies, think of the dictionary as a dictionary of synonyms. Look at an example.

Say you need to store a cookie called "client". This cookie can have two key names: "last_name" and "first_name". You access each with the following syntax:

Request.Cookies("client")("first_name")

Request.Cookies("client")("last_name")

A Request.Cookies call without specifying a key will return a URLEncoded string of the name/value pairs for all entries in the cookies dictionary.

strQuery = Request.Cookies 

would assign the variable strQuery the string value of LAST%5FNAME=Mansfield%2DCorning&FIRST%5FNAME=Katy

ON THE WEB

http://www.mcp.com/que/asp—There's a demo program at the Web site, cookies_walk.asp, that shows writing and reading cookies with data you enter.

Working With Request.Cookies

Unlike its twin, Response.Cookies, Request.Cookies can appear anywhere in the HTML BODY section. As you will see in the next section, Response.Cookies is an HTTP header transaction. This means it must be completed before the entity body in the HTTP transaction is begun. Remember, headers are the means by which client and server communicate; the entity body is the part of HTTP that the client and server use to communicate with human users.

As previously noted, the syntax is simple:

Request.Cookies("cookie-name")|("key-name")

If you request an undefined cookie or key, the Request Object returns a null value.

Because cookies is a collection, you can move through the collection with Listing 12.3. Note that this works without further modification with any Cookies Collection.

Listing 12.3 COOKIES_WALK.ASP-Walking Through a Cookies Collection

<H1>It's a Cookies Walk</H1>

<HR>

<UL>

<% 

For Each cookie In Request.Cookies %>

 <LI>Cookie: <B><% = cookie %></B> = <% = Request.Cookies(cookie) %>

 <UL>

 <% For Each key in Request.Cookies(cookie) %>

        <LI>Key: <B><% = key %></B> is <% = Request.Cookies(cookie)(key) %>

 <% Next%>

 </UL>

<%Next%>

</UL>

The ServerVariables Collection

This collection tells you everything the server knows about the client and itself. There are three groups of data in this collection

As with all collections, you can use the For Each...Next control structure to enumerate the collection.

ON THE WEB

http://www.mcp.com/que/asp—A two-file demo is included at the Web site, demo_servervar.htm and demo_servervar.asp, to show how selecting the two Request Methods, GET and POST, affects the ServerVariables Collection.

The Cookies demo mentioned previously also calls the demo_servervar.asp file and shows the effect on the HTTP_COOKIE variable in the ServerVariables Collection.

The HTTP Request group of variables describes important parts of the HTTP request transaction with the server. Things like REQUEST_METHOD, CONTENT_LENGTH, and CONTENT_TYPE are included here. Other important data points are the REMOTE_ADDR and the REMOTE_HOST variables. These two data points give you some idea of the IP address of the requesting client, and this is often used in programs that send e-mail via Web forms. The addresses returned (when they are, and that doesn't happen with all Web clients) are not always reliable or useful. On-line services have the same host, but often use a unique identifier in the address for individual users. However, this same user will have a different IP address the next time he logs on to the service. Other times no address resolution is provided by the client software, usually because the user is coming from behind a firewall (a computer that protects a trusted network from unwelcome intrusions by outside computers).

The Server group of data points report facts about the server, such as SCRIPT_NAME and SERVER_NAME; these facts might be important in an intranet setting where there are as many HTTP servers as there are clients (for example, every Windows 95 machine is running the "Personal Web Server" application). Another important fact is SERVER_SOFTWARE; this would be important if all servers on an intranet were not running the same version of IIS (for example, IIS 2.0 does not have all the power and speed of IIS 3.0, because the ActiveX Server Engine is an add-on to the former and an integral part of the NT operating system in the latter).

The final group of server variables have the form HTTP_<header_name>. Some of the most useful variables are contained here. You'll find the Cookies Collection here, as well as the HTTP_REFERRER variable. This variable tells you the URL used to engage the current HTTP request transaction (unless the URL was entered manually), so you can see from whence your visitors have come. Another interesting fact is the HTTP_USER_AGENT; this variable tells you what kind of client software is being used at the other end (which can be helpful when you want to present HTML code that is optimized for any given client).

The Rest of the Response Object

Now return to your old friend, the Response Object. You have already covered the most frequently used method for this object, the Response.Write method. The rest of this object contains properties and methods almost exclusively for the management of HTTP headers.

Begin with your favorite topic, cookies.

Response.Cookies Collection

Think of writing cookies in the same way you would write a Registry entry in Windows 95 or an .ini file entry in Windows 3.1. Actually, in ASP development, writing cookies is much easier than it ever was in Windows. There are no special API functions to write (and maintain when you upgrade your Windows). Just read and write, and let the server do the work. This is the way programming was always meant to be, isn't it?

The NAME field is the only required field for cookies, and its value is URL encoded. When you get to the Server Object in the next chapter, you learn how to use this method to do the dirty work of encoding cookies values.

Always put your Response.Cookies calls before the <HEAD> section of your .asp files. These are header transactions (as are most of the rest of the Response Object's methods) and will fail if the server has already written the header of the HTTP response transaction to the client.

If you spy on the cookie collection using the Request.ServerVariables("HTTP_COOKIES") variable, you see that this collection can get full quickly. One of the reasons for this is that each cookie is "attached" to SessionIDs (which have their own cookie in this collection, "ASPSESSIONID"). Fortunately, the server takes care of the details. If you set a "LAST_VISITED" cookie to the current time, all you have to do is Request.Cookies("LAST_VISITED") and you have its value, regardless of what else lurks down there in the cookie jar.

As you learned in the discussion of the Request.Cookies method, cookies can have "dictionaries" (sometimes called "data sets"). These are like fields in a database record, and they're called "keys" (what keys have to do with cookies is anybody's guess). The syntax is simple:

Response.Cookies("NAME")("FIRST")="Michael"

Response.Cookies("NAME")("LAST")="Corning"

When a cookie has keys it also reports TRUE when the "HasKeys" attribute is tested. The other attributes are Domain, Path, Expires, and Secure. Domain and Path provide information that uniquely identify cookies with the same name when they were created by interacting with different Web servers. For example, many servers will write a cookie called "last_visit" and the Domain and Path attributes permit each server to identify its own cookie and not confuse it with the cookie left by another server. The Expires attribute tells the server when it can safely ignore the cookies' value, and the Secure attribute tells the server that the cookie can be used in a secure transaction.

Cookie attributes can only be set, not read directly. This is because they are useful only to the server.

Response Properties

The properties of the Response Object control how the HTML output stream is to be processed. Properties such as ContentType, Expires, and Status all have counterparts in HEAD tags that you may have used before, but we think you will find using Response properties to be simpler.

The Buffer Property

This property is important if you use the other properties and methods of the Response Object extensively. It is important because it permits you to relax the constraint that you must finish manipulating the HTTP headers before you start sending the body of the HTML transaction to the client.

Remember, the HTTP headers are used between the client and the server and are invisible to the output of the HTTP transaction; in other words, none of the header data appears on the client program's screen. Usually, the client and server are intolerant of any interference on the part of the human readable component of the transaction unless the Buffer Property is set true.

When true, none of the output data is sent to the client until all the ASP scripts are complete, or until the Response.Flush or Response.End methods are called.

ON THE WEB

http://www.mcp.com/que/asp—The cookies.asp file in the /lab/ directory uses Response.Buffers=True to permit the Request.QueryString("yesno") variable to be displayed before the BODY section begins further down in the script. The cookies.asp file in the /lab/test/ directory uses conventional programming style to effect the same result.

The ContentType Property.

This property is used to instruct the client how to display the HTTP Response Body sent by the server. For .asp files, setting this to "text/plain" will render the actual HTML source code instead of interpreting it. If you look on the bottom of the /lab/cookies.asp screen you will see an option, "View Source Code." If you select this, you send a query string to the .asp file that instructs it to set the Response.ContentType property to "text/plain." You can see the same result if you select the other option, "ServerVariable Collection" and choose View Source from your client program.

Notice that there are no <HTML><HEAD><BODY> tags used in demo_server.asp. If those tags are present, they override the ContentType property.

The Expires Property.

This property tells the browser how many minutes it has to live from the last time it updated. The default value is 0, meaning that every time the page is visited, it is refreshed. The twin cookies.asp files are an interesting test of this property. Take a closer look.

The /lab/test/cookies.asp file calls the lab/cookies.asp file. Note carefully: The cookies.asp file in the child directory calls its counterpart in the parent /lab/ directory. The first time this parent cookie is called, its LAST_VISITED cookie is set to the current time. From that page, you select the "Set test cookie" option and the child cookie page fires updating its LAST_VISITED cookie to the current time. Note this time before you return to the parent cookie. Now, when you immediately select the "Set lab cookie" link, you will note that the LAST VISITED cookie's value did not change. Why?

Because the page was cached. If you look at the top of the parent cookie page you will see that it tells you that nothing new will appear on that page for at least one minute. For that minute, the Web client retrieves the cached copy instead of refreshing the link.

OK, so switch back and forth, now, between these two pages. Note the time on both pages. As long as there is a difference of less than sixty seconds between the two pages, the parent cookie will not be refreshed. But as soon as the child cookie page has a time one minute or more later than the parent cookie page, it will refresh with a new time.

Clearly, you need to download the demo files from the Web site and conduct this experiment for yourself.

The ExpiresAbsolute Property.

The only difference between the ExpiresAbsolute and the Expires properties is that the former includes a data and time. Use the pound sign (#) to delimit this value. For example:

<% Response.ExpiresAbsolute=#December 21, 1997 1:00:00 AM# %>

The time is converted to Greenwich Mean Time before the Expires HTTP header is sent. GMT is the time that all servers use to stay synchronized.

If you leave the time blank, midnight on the date given is assumed. If the date is blank, the time on the date the script is run is assumed.

The Status Property.

The Status property forces the Server to send whatever response value you give it. The most visible example would be to put the following line of code at the top of your .asp file:

<% Response.Status="401 Unauthorized"

The result of a call to an .asp file with this line in it would be a username/password dialog to request authorized access to the page.

Response Methods

The methods of the Response Object covered as follows help you do odd jobs around the Web site. You probably won't have much occasion to use these methods, but you need to be aware of what they can do, should the need arise.

The AddHeader Method.

This method is not for the fainthearted. If you looked at the demo_servervar.asp program, you noticed that the end of the collection contained a whole bunch of ServerVariables that started with the "HTTP_" prefix. These headers are generally accepted data points that some or all client programs can consistently interpret.

Some of the most commonly used headers are HTTP_REFERRER, HTTP_USER_AGENT, and, of course, HTTP_COOKIE. When a client program sees these headers from the server in response to a request it made, it knows what the server is trying to "say" to it. This is what I meant earlier about the "other" interactivity going on over the Web; i.e., the interactivity not between user and machine, but between Web client and Web server.

One of the header fields that is exchanged in this category is the WWW-Authenticate header. This header "exists" in the HTTP specification but can be "customized" to meet the specific requirements of authentication required by any given server. It is possible that some challenge-response processes would require more than one WWW-Authentication header. The AddHeader method was created to cover this contingency.

The AppendToLog Method.

This method adds a string of no more than 80 characters to the IIS log file. You must have IIS configured to log server transactions. You can confirm (or enable) logging by running the Internet Information Services Manager and selecting the Logging tab from the services window you select (for example, WWW service). This dialog box will also tell you where the log file is located.

Because the IIS log file is comma delimited, you cannot append a string with embedded comments. You may find it necessary to URLEncode the string first, but you will have to decode the string later if you need to refer to it.

One example of using this method is appending the HTTP_USER_AGENT to the transaction. This way you can find out which browsers are going to certain pages on your Web site.

The BinaryWrite Method.

Binary files are not text files. They are usually required by custom applications running on the client's computer. This method enables the ActiveX Server Engine to send this kind of file to the client. This method can be used in a way similar to the way Server Components are used (see the next section, "Server Components," for a detailed discussion). That is, you can create a component that creates objects, and then you can instantiate objects in your .asp file as follows:

Set objImage = CreateObject(ImageComposer)

imgPortland = objImage.PhotoShoot

Response.BinaryWrite imgPortland

The Clear Method.

The Clear method is used in conjunction with the Buffer method mentionedpreviously. This method, along with the End and Flush methods coming up next, is useful when there is a lot of ASP processing necessary before a final result is ready to send to the client. This will make for some very creative and resourceful ASP code.

For its part, the Clear method erases the content of the response body. Remember, that's the part of the HTTP transaction that the client displays on the screen; it is not the same thing as the information contained in the response header. If the ASP code finds an error in processing or data, or if it needs to start processing over for any other reason, the Clear method gives it a clean "sheet of music."

The syntax is simple:

Response.Clear

The End Method.

The End method stops ASP processing in its tracks. It functions like the Stop command in Visual Basic. When the ActiveX Server Engine sees this method, it flushes the buffer (which assumes, of course, that the Response.Buffer = True method was executed in the first line of the .asp file). Any contents in the buffer (including the response body, if any) are sent to the client. To send nothing, use the Response.Clear method first. As with its cousins, the syntax for this method is direct:

Response.End

The Flush Method.

This method will send the buffer to the client without stopping the processing. It, too, only operates without error if the Response.Buffer method has been set to true.

Response.Flush

The Redirect Method.

This method is most commonly used when you want to send clients to another URL instead of sending them an HTML stream. This is commonly needed when a Web site is substantially altered or moved. When someone returns to your site from one of their Shortcuts, Bookmarks, or Favorites entries, the Redirect method can be waiting for them in the now defunct HTML page. When a client requests this page before it can stream the entity body of the document back, the server sees that the URL contains a Redirect header and consequently changes the location of the requested URL. This is done without the user knowing it, though they will see that the URL in the Location box is different than what they entered.

This is also a useful method when you want certain clients to see HTML optimized for them. You can do the same thing using client-side scripting, but this alternative suffers the single weakness of all other client-side scripting strategies: not all clients script.

To avoid this kind of confusion, it may be better to have an explicit message in the body of the outdated HTML page that tells the client that the URL is different and to update their Shortcut, Bookmark, or Favorites entry.

The Server Object

The Server Object can be a real workhorse and time saver. Its primary mission is like all programming utilities that have come before it: do the same old thing, and do it well and quickly. In one (limited) respect, utilities have been the precursor to objects. They have always been reusable code that did a clearly defined "administrative" task. Batch updates, directory maintenance, you name it; if it qualified as drudgery work, a programmer sat down and composed a utility program to do it. On the desktop, macros have generally served this function with distinction.

In the new world, we have come full circle: The utility is now an object (not a fully polymorphic, inheritance-capable object) with its own property and methods. It may not have an impeccable object-oriented pedigree, but it will quickly earn the affection of all Webmasters who can now delegate such routine tasks as rotating ads to it.

To start off correctly, remember that the intrinsic methods of the Server Object have two general roles:

See "Instantiating an Object" to learn the details of CreateObject, in Chapter 14.

Server Property: ScriptTimeout

This is the only property for the Server Object. It is designed to give the ASP developer some control over the contingency of script processing time. Note I said "script" processing. If a server script calls a server component (such as those discussed in Chapter 14) then the ScriptTimeout property will not control that component (the developer of the component is responsible for that). So, if there is a chance that something might take an inordinate amount of time (this is the Internet, after all, and we all know it is not the fastest thing alive) to complete, you can take steps to avoid being deadlocked as a result.

This property is read/writeable. To read the value of this property, use the following expression:

<% =Server.ScriptTimeout %>

If you need to set the property, for example, to three minutes, do it like this:

<% Server.ScriptTimeout = 180 %>

The default value of this property is 90 seconds.

Managing HTML

HTML was meant to be read by a machine, not by people. In the spirit of maximum productivity that drives nearly all Microsoft product development and usability testing, the Active Server provides some great little text manipulation utilities.

CreateObject

There are five Server Components that ship with the Active Server: AdRotator, Browser Capabilities, Content Linking, TextStream, and the Database Access Component. (This last component exposes interfaces to three more objects, the Connection, Command, and RecordSet objects.) The Server.CreateObject method is how we create instances of all these components. To be more precise: each of these components produces an object. Remember, an object contains both data (properties) and programs (methods). Once created, these component objects act on things it was designed to do. For example, the TextStream component produces an object that acts on text files. Most of what you do with text files-opening and closing, reading, and writing-qualifies as utilities, something done the same way over and over again.

Because you tackle these utilities in alphabetic order, the first one you examine is the HTMLEncode method.

HTMLEncode

Server.HTMLEncode is closely related to Response.Write and, in special cases, to the implicit write method (for example, text appearing outside ASP code tags). All of these methods write to the outgoing HTML data stream. To sort all this out, first see what the Server.HTMLEncode method needs for input and what it does with that input after processing.

Simply put, Server.HTMLEncode wants to see exactly what you do. If you want to see the actual HTML source code on your client's window, you give that string of characters to Server.HTMLEncode. It's pretty smart. It knows which characters in the input string will cause confusion to the rendering engine in the client, so it replaces these problematical characters with something the client will properly render.

The most obvious culprit is the angle brackets; they're the characters that tell the client rendering engine that the following text is HTML source and that it should interpret the text as commands.

ON THE WEB

http://www.mcp.com/que/asp—Take a look at the HTML_tips_traps.asp file at the Web site for examples and comparisons of these issues and methods.

ASP development adds another problem, for the <> characters can confuse the server too, and it will be confused before the client will be. In fact, it's possible that the ActiveX Server Engine will make your original problem even more confusing for the client. Confused? Try this again, only differently:

On both the client-side and the server-side, the < and > tells the software that everything in between those characters is source code, not text. But when you actually want to print the angle bracket (say you want to display an actual HTML tag on the screen), how do you tell the software to print it instead of interpret it? That depends on which software you're worried about. Any text surrounded by angle brackets is interpreted. On the server any text surrounded by <% and %> is interpreted.

When trying to output HTMLEncoded text, mistakes can be hard to interpret because of the mess it can make of the resulting HTML data stream. Other times context plays a role so that the same non-alphanumeric characters can have different effects.

In the latter category, my favorite is sending %> to the HTML data stream. Sometimes, the Active Server chokes when it hits what it thinks is the end of ASP code without first having encountered <%, the beginning tag for ASP code. Yet if you refresh the errant .asp file, it seems no longer to be confused.

The safest approach is to experiment with the various techniques, find the one that gives the most consistent results, and stick with it. For example, you have already seen that sending %> can be trouble. Mitigating the potential threat by using Server.HTMLEncode is a good idea, but remember to include the escape character. The syntax is

<%=Server.HTMLEncode("%\>")%>

Server.HTMLEncode needs the = before it in order to send the encoded stream to the client. This is because the output of Server.HTMLEncode can be used in ways other than writing to the client (for example, its output can be the input to Response.Write).

Response.Write does not need the equal sign (output to the client is implicit; it's the only thing it knows how to do), but the equal sign won't get in its way either. You may find it easier to always remember to put the = before methods whose output goes to the client (including Response.Write) than it is to remember which one doesn't need it.

On the other hand, if others are going to see your source code, you might want to write it properly. Otherwise, it's like leaving too much space between your tie and your belt; to those who care about such minutiae, the appearance can be embarrassing.

Generally speaking, using the Server.HTMLEncode method is safer than the Response.Write method. The reason: Server.HTMLEncode always replaces the angle brackets with their "entity names." Entity names are the special character strings that begin with the & and end with the semi-colon. Examples include &gt;, which always yields the ">" character; &lt;, which yields the "<"; &nbsp;, which is a non-breaking space. Response.Write sends the literal character to the output stream, so it might backfire on you (but at least it will only be the client that gets confused).

ON THE WEB

http://www.mcp.com/que/asp—There is a text file at the Web site that lists the most common entity names. But you shouldn't need to bother; Server.HTMLEncode makes the list almost obsolete (except for the one entity name Server.HTMLEncode can't capture, the perennial &nbsp;).

When troubleshooting this kind of code, you can sometimes squeeze a little more troubleshooting data out of your Web client by looking at the HTML source code. the ActiveX Server Engine writes to the data stream up to the point of error, and sometimes it prints more data than is reported by the error alert.

MapPath

The MapPath method is necessary because of one thing: the definition of an ASP application; namely, all files stored in and under a virtual directory.

If you come from a Novell network environment, you are already familiar with logical drives (indeed, in the old Dark ages Of Software (DOS) days we had operating system commands like SUBST that also mapped directories to drive letters).

IIS also uses this kind of technique to shorten the identity of a collection of files. This works great under HTTP with its object-centric orientation. The virtual directory is part of the URL for the Web site. But what happens when you need to do something to a file in that virtual directory, and you are not going to access it through a Web server?

For example, Chapter 13 discusses the Content Linking Component. This component needs access to a text file and will access it through the server's file system.

To access the file properly, the Content Linking Component must know where the file is. If the file is stored somewhere in the virtual directory, you can find it by using the MapPath method relative to the virtual directory. If the file is somewhere else, you need to take your bearings from the ServerVariables Collection's PATH_INFO variable. Here's how you do each thing:

Say that your application is called /web (that's its virtual directory) and that it is physically located in the c:/data/web directory. Recall that when you set up the virtual directory in the Internet Information Services Manager application, you specified this physical directory then. The PATH_INFO variable in the ServerVariables Collection keeps a record of this physical path. Therefore, when you invoke the Server.MapPath method using the Request.ServerVariables("PATH_INFO") syntax, you return the following string:

c:\data\web

If, in fact, the file you need to access is inside the virtual directory, you can specify its location relative to that directory. Here, the presence or absence of an initial slash character (forward or backward are recognized) controls whether or not the mapped path starts at the root of the virtual directory or relative to the current one.

If the parameter passed to the MapPath method begins with a slash, then MapPath begins at the top of the virtual directory. If no slash precedes the path passed to MapPath, then the result of the method is the fully qualified path to the file from the current directory.

See "Content is King" for more information about Contnet Linking Components, in Chapter 13.

ON THE WEB

http://www.mcp.com/que/asp—This is one of those topics that is so hard to describe, yet easy to demonstrate. We have included another or our famous demo applets at the Web site to show you, not only what we've been talking about, but also how you might use it in your own applications.

URLEncode

You have already broached the topic of URLEncoding; you encountered it in the context of the QueryString passed in a GET request to the server. In this chapter, you have made your own query strings when you wanted to call an .asp file in a particular way (see the demo_cookies and demo_servervars .asp files for examples). You also noted that cookies are URLEncoded (though you won't need to encode cookie values since the Response.Cookies method does that for you).

The most common use for this method may just be in passing variable strings between ASP pages. To render a string according to the rules of URL encoding, simply send the string to the Response.URLEncode method like this:

<% http://lab/test.asp?Server.URLEncode("Katy Mansfield-Corning") %>

and you will send the correct string to the test.asp file that looks like this:

Katy+Mansfield%2DCorning

Managing Data With CreateObject (ADODB…)

We note the use of the Server.CreateObject method at the end of this chapter for the sake of consistency. Note the Server Component identifier used in the section heading. The "ADO" part of it stands for ActiveX Data Objects. The developers had to add the "DB" when they released Beta 2 because the underlying Server Component had been substantially improved. If you look in the Registry, you will see an entry in there identified by "ADODB." The ellipsis is there because there are three objects intrinsic to the ADODB component.

We will say a little more about this method in the next chapter when we introduce the Database Access Component and the ActiveX Data Objects, but this technology is so important that it deserves its own Part in the book, Part IV, "Database Management with Active Server Pages."

Before you leave this topic note two things:

Outside of ASP, the CreateObject is handled as it usually is in that environment. Again, with VB, the syntax is simply CreateObject. As we said, there's plenty of time and space set aside for ADO in coming chapters.

From Here...

With this chapter, we leave the Server Objects for the moment. Later in the book, we will need everything we've learned so far. In the following chapter we conclude Part III with a discussion of a very important part of ASP development, Server Components.

Server Components permit something crucial to all modern programming development environments-extensibility. What the "Integrated Development Environment (IDE)" did for developers using Visual C++, Visual J++, Visual Basic 5.0, and the Internet Studio, server components will do for users of applications written by Active Server Pages developers.

Perhaps put a better way, the advent of Server Components makes it possible to extend the functionality of the Active Server without interrupting what it already does so well. If the IDE was a boon to professional programmers, then Server Components will have the same impact on power users and Webmasters who do not hold themselves to a professional programming standard. It's an IDE for the rest of us.


© 1997, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.