Chapter 9

Calling Procedures: Functions and Subroutines


Over the past few years, countless articles and books have been written and discussions held centering on code reuse. The latest development in code reuse,- although it has been around now for quite some time- is object-oriented development. The basic concept involves encapsulating key pieces of functionality in different objects. Then, when you are ready to create your application, you have an inventory of prebuilt functional objects to choose from. The many benefits of code reuse can be reaped in almost any development environment (although many developers would like you to believe that it is only attainable via SmallTalk or C++). In this chapter, we will discuss crafting procedures to maximize code reuse in your Active Server Pages development.

Examining Procedures

As you begin to develop your Active Server applications, you will find that they consist of a number of interrelated pages; each page is a logical division of work, expressed by the functions you embed within that page relative to those pages that it is linked to. Within any specific page, you can also divide the functionality into discrete pieces by using procedures. A procedure is a logically related group of statements that can be executed by a single statement. Creating procedures enables you to encapsulate specific actions and/or calculations within a callable routine. As an example, in the following code snippet, the CreateNewUserId function is called. The function creates a new user in the database and returns the new Id as a string sub-type variant.

strNewId = CreateNewUserId()

For the moment, we will defer talking about the cases in which the Id was not created successfully. Instead, focus on the fact that with one line of code, you have executed a procedure that performs a logical unit of work (creating the Id). The implementation of the CreateNewUserId function could conceivably be fifty or sixty lines long. There are a number of reasons why it makes sense to logically divide up your page into statements of related functionality through the use of procedures. In the previous example, imagine that you need to create a number of new user Ids within a script. If you did not encapsulate the functionality within a procedure, you would have to copy/paste the fifty lines of code in each place within the script that you needed to create a new Id. In addition to the extra code that must be put in and parsed, if you were to change a line of code within the CreateNewUserId routine, you would have to change that line in each place that you copied the code to.

This need to find each statement to be changed also introduces another point of possible failure and inconsistency into your code. This is one of the main benefits of procedures. When you make a change within a procedure, all of the scripts that reference it will automatically benefit from the revised code. There is no chance of forgetting a place (each of those copy/paste steps) where the code needs to be changed. You will run into enough of those problematic situations in the first few months without any extra help!

In the preceding example, we mention that you might be calling the CreateNewUserId function a number of times within the same page. A more likely situation is one in which you want to use the routine in a number of different pages. Again, you don't want to resort to the copy/paste routine. You will want to utilize a functionality called Server-Side Includes, which allow you to share procedures, and any file for that matter, between multiple scripts. More on Server-Side Includes will be found later in this chapter in the section "Using Server-Side Includes."

You can use any combination of variables, arrays, and control statements found in Chapter 7, "Understanding Variable Typing, Naming, and Scoping" and Chapter 8, "Working with Program Flow and Control Structures," within the body of the procedures that you create. You can also integrate calls to the objects that are delivered with the Active Server Pages product, as well as any third-party objects that you acquire. This provides you with a powerful set of building blocks to begin crafting your procedure library. And in effect, what you will want to do is just that: Begin to create a library of procedures that you can use over and over again in your ASP development. This is a major source of code reuse that will make future applications much easier to develop.

There are two types of procedures within the VBScript environment, subroutines and functions. They both provide a method to logically group a related set of statements. There are, however, some subtle differences between the two, noted in the next two sections.

Subroutines

A subroutine is a block of code that you can invoke from anywhere within your script. The template for a subroutine procedure looks like this:

Sub procedure-name(argument list)

   statements to execute

[Exit Sub]

   statements to execute

End Sub

The argument list contains the values that are passed into the subroutine, which can be referenced in the body of the procedure. In the Visual Basic environment, argument values are passed either By Reference (ByRef)or By Value (ByVal). Passing a variable ByRef enables the subroutine to alter the value of the passed in argument. Passing a variable ByVal passes only the value of the variable into the subroutine. The variable can not be changed within the Sub procedure. The current version of VBScript only supports passing arguments ByVal. Be sure to check the documentation as each new release is made available. I'll bet that you will be able to add the ByRef option to your VBScript repertoire shortly.

The Exit Sub statement found within the subroutine template is an optional statement that works just like the Exit Do or Exit For statements within a loop. When an Exit Sub statement is encountered, the subroutine is exited immediately. The Exit Sub statement is usually found with an If statement, as in: if a condition exists (an error, or other), leave the subroutine immediately.

Because VBScript only allows passing arguments by value, use script level scope variables if you need a variable to be visible in all procedures within a page.

There are two ways to call or invoke a subroutine. The first is to call the routine by typing the subroutine name followed by a list of arguments separated by commas. The second way is to use the Call statement in front of the subroutine name. If the Call statement is used, the parameters passed to the subroutine must be enclosed within parentheses.

The following examples show how a simple subroutine that writes a greeting to the HTML stream can be invoked using either of the two subroutine calling syntax:

   Greeting server.request(UserName)

or

   Call Greeting(server.request(UserName))

Sub Greeting(strUserName)

   response.write(strUserName)

   response.write(", welcome to our page!")

End Sub

Functions

A function is very similiar to a subroutine with two important differences. First, when calling a function, you must use parentheses around the argument list. If there are no arguments for the function, you must still include parentheses, although they will contain nothing: FunctionName(). The second difference is that a function will return a value to the calling procedure or statement. The template for the function procedure follows:

Function name [(arglist)]

   statements to execute

   [name = expression]

   [Exit Function]

statements to execute

End Function

You will set the return value of the function by assigning a value to the function name within the body of the procedure. If you do not explicitly assign a return value to the function name, it will return zero. Until the ByRef argument passing option is added to VBScript, you will likely find yourself creating more functions than subroutines because you will not be able to change the value of the arguments passed to a subroutine

The following example illustrates a common function, returning a description of an error, based on an error number passed in.

Function ErrorMessage(intErrorCode)

   Select Case intErrorCode

      Case 1024:

         ErrorMessage = "Insert record failed due to key violation"

      Case 1100:

         ErrorMessage = "Update failed.  Key not found"

      Case 1220:

         ErrorMessage = "Invalid Key for Update"

      Case Else:

         ErrorMessage = "Uncategorized Error: " & intErrorcode

   End Select

End Function 

In reality, this function might include a hundred different error messages. The key thing to remember is that the return value for the function is set by assigning a value to the function name. Also, notice that this type of function is an ideal one to add to your function library. Error numbers and their associated error messages can be standardized across your site allowing one function to be used for all cases in which you need to return an error message to the client.

Error Handling Within Procedures

I have often heard it said that an ounce of prevention is worth a pound of cure. I was never sure what the cure was, but I'd sure prefer to expend the ounce up front, than to worry about the pound later. When a script that is processing encounters an error (an un-trapped error), the client browser receives an error message from ASP that can include the .asp file name, the line number of the error, a short description when appropriate, and other error information. Now, this is valuable information for you to use when debugging the application, but it is NOT the kind of impression that you want to leave visitors to you Web site with.

There are two main things that you can do to reduce the possibility of one of your pages blowing up in production. The first is to test, test and test again. There is no substitute for testing your application, ensuring that the links are available and that calculations performed are accurate. We will talk more about testing in the section "Creating Reusable Procedures" later in this chapter.

The next activity that you need to perform is to embed error-handling logic within your procedures. Within the Visual Basic environment, there is a fairly robust mechanism for handling errors, even raising your own errors within the framework. The ability to raise custom errors and to trap them intelligently is a key component of integrating custom objects into your script development. In VBScript, your options are a bit more limited. Even though you have limited tools available to trap errors, with a little work, you can catch the majority of them.

The first (and only) construct that enables you to instruct the script processor to take an action when an error occurs is the On Error Resume Next statement. The syntax of the statement is:

On Error Resume Next

When ASP encounters an error executing a statement, the On Error Resume Next statement causes the script to jump to the statement immediately following the statement in error. You can think of it as Resuming after an error at the Next statement. Using the On Error statement in tandem with the Err object enables you to respond to the majority of errors you will encounter in your Active Server Pages development. The On Error statement has scope, as well. You can set the scope at the script or procedure level, just as you set variable scope. Generally, you will want to implement error handling within your procedures (where most of your script code will live anyway).

Working with the Err Object

Information about errors that are encountered as your scripts execute is stored within the Err object, which is an intrinsic part of the VBScript language. It has properties that are set when errors occur, and methods that can be performed. The properties of the Err object are outlined in Table 9.1.

Table 9.1 Err Object Properties

Property

Description

Example

Number

The error number for the most recent error

11

Description

A string containing a description of the error

Division by 0

Source

A string containing the source of the error

Project.Class

When an error is raised within a Visual Basic class module, the source property is in the format Project.Class, where project is the Visual Basic project file and class is the class name of the method that was executing when the error was raised.

See "Handling Errors Within Your Classes" for more information about Visual Basic classes and raising errors from within class modules, in Chapter 14.

There are two methods associated with the Err object. The first, Err.Clear, is called with no parameters and clears any information residing in the properties of the Err object. The second method, Err.Raise, raises a run-time error. The template for the Err.Raise method is:

Err.Raise(number, source, description, helpfile, helpcontext)

The only required parameter is the number, which is the number returned from the Err.Number property. The source and description are string parameters, the uses of which are described in Table 9.1. In Active Server Pages VBScript development, you will not use the helpfile or helpcontext variables.

If you call the Err.Raise method and do not include all of the parameters, any values that were in the Err object will remain. To ensure that you do not inadvertently leave erroneous information in the object, call Err.Clear before you call Err.Raise.

Error Handling in Action

One of the best places to use error handling within your programming development is at the procedure level. Because procedures are logical units of work, it makes sense to have error handling that is specific to each procedure. In the following example, we have embedded error handling within our procedure to ensure that an error is appropriately trapped.

Do you remember imaginary numbers? Now there was a class of numbers that I could relate to. Their only use in high school was in taking the square root of a negative number. In the VBScript world, there is no support for imaginary numbers (only imaginary features), and if you tried to calculate the square root of a negative number, you would generate a run-time error, which, in this case is exactly what we are going to do.

<% 

   dim intRC

   intRC = SquareRoot(-4)

   If (intRc < 0) Then

      response.write("Error calculating the square root")

   Else

      response.write(intRc)

   End If

%>

<SCRIPT LANGUAGE="VBScript" RUNAT=SERVER>

   Function SquareRoot(varNumber)

      On Error Resume Next

      SquareRoot = Sqr(varNumber)

      if Err.Number > 0 Then

         Err.Clear

         SquareRoot = -1

      End If   

   End Function

</SCRIPT>

To set up the scenario, the SquareRoot (Sqr) function is called, passing in a negative value as the argument. The first line within the function is the On Error statement. When an error is encountered, the On Error statement lets the execution pass to the next line. If you do not check the Err object for an error condition, and you have the On Error statement, your function can return invalid or incorrect information from your procedure if an error is encountered. The way to handle this is to add checks of the Err object in strategic places (those most likely to generate an error). You could, potentially, check the Err object after each statement is executed, but that would be inefficient. The key is to place the Err object check at critical points within the procedure. Because you will be creating procedures that perform one logical function, it should be easy to select the appropriate places to add Err object checks.

Back to our SquareRoot example code: If the number passed in to the function generates a run-time error within the Sqr function, the function sets the return value to -1. This value notifies the calling statement that an error has occurred within the procedure. Notice that we are actually introducing another level of error handling into the script. We are handling the run-time error within the procedure, and then responding to an error condition (return value of -1) within the script proper. In most cases, you will be checking the value of a return code from a function to verify that the function has completed successfully and then code for appropriate action based upon the outcome.

Creating Reusable Procedures

Coding in VBScript is not a traditional object-oriented reuse exercise, but by creating useful procedures, combined with the Server-Side Include capability of ASP and Internet Information Server, you can ensure that you will be reusing your code just like the Object-Oriented developers (well, not just like, but close). For those of you who are Visual Basic developers, many of the functions (and those subroutines that don't change the passed in arguments) can be moved directly into your VBScript library.

To create a library of reusable procedures, you need some guidelines and a method to create them. The first bridge that must be crossed, of course, is to create the reusable procedures.

To be reusable, procedures must provide a level of functionality that is generic enough to be used in a variety of situations. These types of procedures include formatting code, header and footer code, advertisement code, database query code, and others that have wide applicability across applications.

The first step in creating world-class procedures is to define the requirements of the routine. This just requires some thinking to determine the exact functionality that the procedure will provide. For example, suppose you want to create a generic procedure to validate credit cards. There are a number of questions that come immediately to mind:

You need to address every question that you come up with regarding the procedures functionality before you write a single line of code.

Once you have settled on the functions that the procedure will provide, the next step is defining the arguments that the procedure will accept, and optionally, the return value that the procedure will provide. Again, there are a number of questions relating to the arguments or parameters that the procedure will accept and to the return values:

You have answered all of the argument and return value questions (again, no coding yet). Now you are ready to begin examining the interfaces (if any) that your procedure will interact with. Interfaces include such things as database access, mini or mainframe connectivity, transaction processor access, and so on.

With all of the answers to the questions posed above in-hand, you can begin thinking about any algorithms that will be implemented locally within your procedure. Do you need to perform any calculations outside of the interfaces? Are there any complex looping or array handling requirements? Again, these are questions that you need to answer before you begin to code the procedure.

Coding the Procedure

The next step is to create the procedure skeleton. This includes the Sub or Function block, as well as selecting the name for the procedure. Just as you learned about the naming conventions for variables, there are naming conventions for procedure names as well. The naming conventions are more common sense and less rote, however, than those for variables. The best guide is to think of a name that, even if you had never seen the code, would give you a clear idea of what function(s) the procedure performs. For example, use InsertNewDatabaseRecord instead of addrec, or AuthorizeCreditTransaction instead of authtran.

Now that you have gathered the necessary information about functionality and decided upon a name, you can begin to code the procedure. When you are developing a complicated procedure, it is often wise to code the entire procedure in pseudo code, and then implement it in VBScript. Using pseudo code will do two things for you. First, it enables you to write out the logic of the procedure in near-English and helps you to ensure that all of the functionality that was specified in your design is in the procedure. Second, the pseudo code becomes the documentation for each step within the procedure. You just add the comment delimiter in front of each line of pseudo code and viola, your code is fully commented!

Basically, pseudo code is the use of English phrases to illustrate the functions that your code will carry out. An example of this follows:

Function AuthorizeCreditTransaction(strAccountNumber, dblAmount)

   Verify that the account number is valid

   If the account number is invalid

      return invalid account

   Connect to the Credit Authorization Service

   Send the Authorization Transaction

   Receive the Authorization

   Return the Authorization to the calling procedure

End Function

Notice that we have not included a single line of VBScript in this psuedo code, but we have mapped out the entire procedure.

Now it's time to integrate the answers to each of the questions that you asked earlier. If there were range restrictions on the arguments passed in, add them now. For example, you might implement a mod 10 check digit routine on the account number before it is sent for authorization. A mod 10 check digit is an algorithm used to verify that an account number is valid within a vendors range. This provides a local level of account verification before the transaction is sent to the authorization process. You also might ensure that

the amount passed in is greater than zero. These would, of course, show up in your pseudo code as well. The mod 10 check digit also might be a function in and of itself that would be called from your AuthorizeCredit Transaction function.

Now, you can go back and add the scripting to complete each line of the pseudo-coded procedure. As you add the functional code, you will also introduce error handling code where appropriate, as discussed in the preceding section "Error Handling in Procedures." When you have completed the procedure, it is time to test it.

Checking the Routine

As Vidal Sasson once said, "The only place where success comes before work is in the dictionary." This is true in creating successful procedures as well. You have surely spent a considerable amount of time already crafting the procedure, but your job is not yet done. Even though you have carefully coded the procedure and introduced error-handling code in appropriate places, there is no substitute for rigorous testing to ensure that the procedure functions in all circumstances in your production environment. There are a number of fine texts available that can guide you through this rigorous testing process. Presented below are an outline of some of the specific test methods to get you started:

Unit Testing

Unit testing involves testing your procedure against the outcome criteria that you determined in the questioning phase in a stand-alone environment. This ensures that all of the interfaces and internal procedure logic are working correctly without worrying about external factors affecting the procedures performance. Things to test during this phase include passing in valid and invalid arguments, making services unavailable to test the robustness of your error handling code, and processing the function to completion to check the return value, if any.

Integration Testing

After the unit tests have been completed successfully, you move into integration testing. This involves including the procedure into an application and again testing the scenarios outlined during the questioning phase. This enables you to determine any potential problems in integrating the procedure with other code. This would be a place that you could find (if for some reason you forgot the Option Explicit declaration) a common variable in your procedure that was not dimensioned, and was subsequently overwritten by a script-level variable within the integrated script.

Deployment

It is now time to deploy your procedure into production. If you have followed the testing steps above, you will end up with a logically sound, error free and tested procedure that you can add to your growing procedure library. Now that you have all of these powerful generic procedures, you can maximize their use by including them into your pages. As it happens, we are now ready to discuss how to perform this feat, using Server-Side Includes.

Using Server-Side Includes

If you have ever done development in C or C++, you can think of Server-Side Includes as being somewhat analogous to the #include directive. In the C language, the #include directive enables you to insert the contents of one file into another file at compile time. Development in C and C++ would be impossible if you could not #include files in-line.

Server-Side Includes (SSI) perform a function similar to the #include directive. In its most basic form, SSI includes the contents of one file into an HTML stream before that file is sent to the client. The included file can be a text file, an .html file, an .asp file, a graphic file, or almost other file existing on the server.

Imagine this scenario: You have a Web site composed of hundreds of linked pages for your company. You want to provide a standard look and feel across all of your pages, so you can project a single image to any point on your site where a client may be browsing. On most Web pages there are standard headers and footers with links or graphics or some other unifying display that is consistent across the site. But, the information in these headers and footers change throughout the month (or even day). If you had to go to each page and change the information within the .html or .asp file, you would need to hire a team of Internet savvy Kelly Temps just to keep you up-to-date. Or, you could use Server-Side Includes and only have to change one or two Include files. Any changes made to the included files would then ripple through your entire site the next time a page was requested.

Scenario number two: You have yet to reach this part of the chapter. You were so excited when you learned about procedures, you stopped at that point and spent the next week or so developing procedures. Now it is a week later and you have a number of generic, well-thought-out, tested procedures that you are going to use in your Web pages. A few years ago, before the wizards that we find in most development environments these days were even a thought, the best way to start a new program was to find an old one that did something similar and then use the highly efficient CPC system (copy, paste, code) to complete your task. Although Server-Side Includes are not analogous to code wizards, they will let you avoid the CPC syndrome when you want to include pre-existing procedures into your Active Server Pages applications.

Server-Side Includes: How To

Before we begin, you must be aware that we are going to be discussing two types of Server-Side Includes. The first is provided by Internet Information Server (IIS). The Includes that are provided by ASP when processing .asp files are currently limited to including external files. As you continue through this section, the IIS vs ASP nature of the discussion will be clearly marked.

A couple of steps are involved in using SSI to enhance the functionality of your Web applications. The first step is to decide what file(s) you want to include. The second step is to add the Server-Side Include statement into the target .stm or .asp file. An .stm file is the same as an .html file, but causes the file to be pre-processed by the SSINC.DLL (Server-Side Include dynamic link library) which actually handles the Include functions for those non .asp files.

Server-Side Includes are through the use of pre-processing directives. These directives have the following syntax:

<!--#<PREPROCESSING_DIRECTIVE>-->

You will be examining a number of these directives, but first, you will move on to those that will be used most often in your development programming.

The default extension for the files that will be processed for Server-Side Includes by IIS is .stm. To change the default extension, edit the registry entry for the Server-Side Includes extension.

Including Files

The following discussion on including files is applicable to both IIS and ASP. There are two ways that you can specify a Server- Side Include file to include within a target file. The only difference in the two methods is where the Server-Side Include file is in relation to the target file. The first is the virtual file include method:

<!--#INCLUDE VIRTUAL = "inclfile.txt"-->

This will insert the file inclfile.txt into the HTML stream at the point that the Server-Side Include is found in the page. The file name is found in a directory, relative to the path for the base directory of the Internet Information Server (IIS). If you accepted the defaults when installing IIS, the base directory will be \WINNT\SYSTEM32\INETSRV, so the inclfile.txt will be located in that directory. You can also specify a path for a virtual include. The path will be in relation to the base directory of IIS as well. For example, to include the following file:

\WINNT\SYSTEM32\INETSRV\INCLUDES\HEADER.TXT

You would use the following include statement:

<!--#INCLUDE VIRTUAL="/INCLUDES/HEADER.TXT"-->

Notice that the directory delimiters (slashes) are in the opposite direction to what most of you are familiar with (unless you have worked in the UNIX or AS400 environments lately). They are used in that fashion because the Server-Side Include specification was developed in the UNIX environment. Even though the standard uses the forward slash, the backward slash will work equally well for Server-Side Includes.

The second method to include a file is with the FILE include directive, which looks like this:

<!--#INCLUDE FILE = "inclfile.txt"-->

Using the FILE include directive, the include file is located in relation to the location of the current target document. So, if you want to include a file in the same directory as the target file, leave off any directory path information. If you want to include a file that is under the target document-for example, a directory called scripts-then you would use the following statement:

<!--#INCLUDE FILE = "\scripts\inclfile.txt"-->

You might be wondering why there are two methods of doing what appears to be the same thing. Well, the real reason that you are given these options is to increase the flexibility of your Web development. There are generally two classes of files that you want to include. The first class are application-specific files, like the custom header and footer that were mentioned previously under "Using Server-Side Includes." These might be in a sub-directory stored beneath the location of the corporate Web pages default directory. In the next case, you will want to include, in many applications, all of those functional and thoroughly tested procedures that you have already developed. The two file include methods give you the flexibility to easily process Server-Side Includes in both situations.

An included file can have another Include directive within it. This is useful when you want to include a number of common procedures found in multiple files. In the first Include file, you will include directives including the common procedures. Then, in the target file, you would only need to include the first file containing the other Include directives, instead of an Include reference for each and every common procedure file. Just be careful not to include a reference to a file within that file. This will create an endless include loop, NOT a good thing!

File Information Includes

In addition to including files into your document, you can also include a number of items (file size, last mod date) about a particular file into your HTML stream. The file information includes are not currently supported within the ASP environment. To include the size of a file, you would use the following directive:

<!--#FSIZE [VIRTUAL][FILE]="inclfile.txt"-->

You can use either a virtual path or a relative path when using the FSIZE directive. The size of the file inclfile.txt, in kilobytes, will be included in the file. This is a particularly handy directive when specifying a file for download. You can include the filename and the file size, so your client will have an idea of how long a download might take. The number returned, using the FSIZE directive will be comma delimited in the thousands position. A 1 megabyte file will be returned as 1,024.

You can also obtain the date that a file was last modified to include in your HTML stream by using the FLASTMOD directive:

<!--#FLASTMOD [VIRTUAL][FILE]="inclfile.txt"-->

This directive also can be used with files that are referenced using a virtual or a relative path. By default, the date that is returned by using the FLASTMOD directive is in the format Weekday MonthName DayNumber YearNumber. The format of this date, as well as the default size returned by FSIZE (kilobytes) can be changed by using the configuration directive option of SSI.

Configuring Server-Side Includes

There are a number of options that you can specify to override the default behavior of the data returned from an SSI call. The format used when setting a configuration option is:

<!--#CONFIG OPTION ="optionstring"-->

The option string is relevant to the configuration option that you are setting. If you think about it, this is a powerful feature. You can change the format of the information returned from the include without having to provide any formatting for it in your scripts.

Setting the Format of the FLASTMOD Directive

As stated previously, by default, the FLASTMOD directive returns the last modified date of a file in the format Weekday MonthName DayNumber YearNumber (Tuesday, December 10 1996). By using the CONFIG directive with the TIMEFMT option, you have the opportunity to specify the information returned by the FLASTMOD directive.

<!--#CONFIG TIMEFMT ="date/time format string"-->

To specify a new date or time format, you create a format mask using the options specified in Table 9.2. The locale reference found in the table is referring to that on the server where the include is taking place.

Table 9.2 Parameters for Specifying Date and Time Formats for the FLASTMOD Directive; Examples based on a date/time of February 23, 1996 at 11:01:55 pm

Parameter

Description

Example

%m

Month as a decimal number

02

%b

Abbreviated month name

Feb

%B

Full month name

February

%d

Day of the month

23

%j

Day of the year

54

%y

Year without century

96

%Y

Year with century

1996

%w

Weekday as an integer

5 (0 is Sunday)

%a

Abbreviated weekday name

Fri

%A

Weekday name

Friday

%U

Week of the year, Sunday first day

8

%W

Week of the year, Monday first day

8

%I

Hour in 12 hr format

12

%H

Hour in 24 hr format

23

%M

Minute as an integer

01

%S

Second as an integer

55

%P

am/pm indicator for current locale

pm

%x

Date representation for current locale

2/23/96

%c

Date/time representation for current locale

2/23/96 11:05:55PM

%X

Time representation for the current locale

11:01:55pm

%z

Time zone abbreviation or blank

CST

%Z

Time zone, or blank if unknown

Central Standard Time

%%

Percent sign in mask

%M%%%S

If you have ever used the Format function within Visual Basic or VBA, you will be very comfortable using the #Config formatting masks.

Here are a couple of examples of formatting a date and time, using different masks. For the purpose of these examples, assume that the last modified date/time on the file that you are going to use the #FLASTMOD directive on was February 6, 1991 at 11:05:09 pm.

<!--#CONFIG TIMEFMT ="%I:%M %P"-->

formats the time as 11:05 pm. You can include seconds in the time as follows:

<!--#CONFIG TIMEFMT ="%I:%M:%S %P"-->

To format the date as shown in the preceding paragraph, you would use the following date mask:

<!--#CONFIG TIMEFMT ="%B %dth, %Y"-->

The configuration of the TIMEFMT remains in effect until the directive is called again within the page, or until a new page is loaded.

Setting Default File Size Increments for the FSIZE Directive

As mentioned in the section about the FSIZE directive, the default number returned is in kilobytes. If you want to specify the file size in bytes, generate the following directive:

<!--#CONFIG ABBREV ="bytes"-->

This ensures that the file size returned to the HTML stream by the FSIZE directive will be in bytes, not in the default kilobytes. The number returned in bytes will be comma delimited in the thousands position as well.

Setting SSI Error Messages

When a Server-Side Include fails for any reason, by default, a detailed message is returned that contains information explaining why the include failed. In many cases, you do not want this information returned to the client. To prevent this from happening, set the ERRMSG configuration option.

<!--#CONFIG ERRMSG ="Server Encountered an SSI Error."-->

Once this configuration option is set, the message set within the CONFIG directive will be the one returned to the client when an SSI error is encountered.

The Echo Directive

There are a number of "server" variables that are associated with any given request for the retrieval of a page. The Echo directive is not available in the ASP environment, but all of these server variables can be accessed within ASP by querying the Server object, discussed in Chapter 12, "Enhancing Interactivity with Cookies, Headers and the Server Object." Some of these variables are available to you for inclusion into the HTML stream that you return to your client, using SSI. The syntax of the Echo directive is:

<!--ECHO VAR ="VariableName"-->

Depending on your requirements, you can use one or more or even all of the available variables. The most useful choices are shown in Table 9.3. For a complete list of all the variables available using the Echo directive, see your SSI documentation.

Table 9.3 ECHO Directive Server-Side Include Variables

Variable Name

Description

LAST_MODIFIED

The date the document was last modified

PATH_INFO

Additional information about the document path,returned with a virtual path name

PATH_TRANSLATED

PATH_INFO with the virtual path mapped to the directory path

QUERY_STRING

The information passed to the script following the ? in the URL

DATE_GMT

The current system date in Greenwich Mean Time

DATE_LOCAL

The current system date in the local time zone

GATEWAY_INTERFACE

The current CGI revision level that is supported by the host server

HTTP_[header name]

All of the HTTP header information that will appear in a comma separated list

HTTP_ACCEPT

The MIME types that the browser can accept

HTTP_ACCEPT_LANGUAGE

The languages that the browser can accept

HTTP_USER_AGENT

The name of the browser software running on the client

HTTP_REFERER

The URL of the page that referred the client to the document on your site

HTTP_UA_PIXELS

The resolution of the client browser display

HTTP_UA_COLOR

The color palette of the browser display

HTTP_UA_OS

The operating system of the client browser

An example of including some of the server-side variables follows:

<HTML><HEAD><TITLE>#ECHO VAR Samples </TITLE></HEAD><BODY>

<P>Here are some examples of using the echo function<BR></P>

The Local Date    :<!--#ECHO VAR="DATE_LOCAL"--><BR>

The Remote Host   : <!--#ECHO VAR="REMOTE_HOST"--><BR>

All HTTP Header Information: <!--#ECHO VAR="ALL_HTTP"-->

</BODY></HTML>

The output from running this script will produce output like the following on the client:

Here are some examples of using the echo function

The Local Date  :Thursday December 26 1996

The Remote Host : 3.1.1.1

All HTTP Header Information: HTTP_ACCEPT:image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */* HTTP_ACCEPT_LANGUAGE:en HTTP_CONNECTION:Keep-Alive HTTP_HOST:selfanba HTTP_UA_PIXELS:1024x768 HTTP_UA_COLOR:color8 HTTP_UA_OS:Windows NT HTTP_UA_CPU:x86 HTTP_USER_AGENT:Mozilla/2.0 (compatible; MSIE 3.01; Windows NT)

Executing Commands Using Server-Side Includes

The last directive that is currently supported only under IIS is the EXEC directive. Using this directive, you can execute a CGI script, a shell command, or an ISAPI application (all ISAPI apps are packaged as DLL's). After the command, app, and so forth has executed, the output is inserted into the HTML stream. If there is any HTTP header information in the returned data stream, only URL redirection information is recognized, and the message is replaced by the text This document has moved to 'new addr'.

The format of the EXEC directive follows the preprocessor directive format and looks like this:

<!--#EXEC [CGI][CMD][ISA] ="Command/App/Script/ToExecute"-->

The CGI Option

Each of the options have slightly different meanings as they are implemented. The first, CGI, notifies the SSI processor that a CGI script (in its virtual path, if specified) is found in quotes after the equal sign. The CGI script can be formatted just as if you were calling it from your browser, with a ? and any parameters that need to be passed to the script delimited by +.

The capability to invoke CGI scripts in-line, using SSI is a powerful tool. Remember when you read about the benefits of including files within your pages? These same benefits accrue to using CGI scripting in-line. With a combination of these two methods, you can maximize code reuse while at the same time, minimize maintenance. A call to execute a CGI command in a cgi-bin subdirectory beneath the document directory looks like this:

<!--#EXEC CGI ="/cgibin/querytme.exe?1week+2days"-->

This executes the querytme.exe CGI script, passing in 1week and 2days as parameters. The output of the script is inserted into the HTML stream immediately after the EXEC directive.

A Commanding Option: CMD

When the CMD option is specified on the directive line, the CommandToExecute is the shell program to run. You can specify command line parameters, using the CMD option as well. You can specify a full path for the command, or you can let the server walk the path (those directories included in the PATH environment variable) to find the file. If the file is not found, an error message is returned as the text of the call. In this example, we are going to call the command cmdtest.exe and pass it a few parameters:

<!--#EXEC CMD ="/utils/cmdtest.exe?10024"-->

Including an ISAPI Application

When Microsoft released the ISAPI specification, it created an entirely new market for Internet development tools. With the ISAPI functions now residing within the Microsoft Foundation Classes, it is a quick and painless exercise to create Internet server applications using Visual C++ and IIS. This new API has also created another third-party boon for developers. What once would have been coded in CGI can now, in many cases, be purchased as an ISAPI application. These applications can be leveraged in the Active Server Pages environment as well. ISAPI applications are more efficient and perform better than CGI applications because they run in the same process space as IIS.

When you want to process an ISAPI application using SSI, you can also provide parameters, much like you did when using the CGI option. The syntax to call an ISAPI application named isapitst.dll with two parameters, an amount and a term, looks like this:

<!--#EXEC ISA ="/apps/isapitst?100.25+30"-->

From Here...

This chapter covers procedures: what they are, how to create them, how to ensure that they are error free, and finally, how to reuse them in your ASP development. Now that you have a firm grounding in procedure creation, it is time to move into some of the more advanced features of ASP application development: integrating your VBScript with the objects provided by ASP. Each chapter in Part III discusses a unique component that shows you how to integrate these incredible pieces of functionality to create dynamic ASP pages.


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