This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


MIND


This article assumes you're familiar with Active Server Pages and Visual Basic 5.0
Download the code (170KB)

Designing ASP Files within Visual Basic
Dave Cline

Ever pull your hair out trying to write an Active Server Page? Dave Cline shows how easy it can be when you use the Visual Basic IDE.
Looking at an Active Server Pages (ASP) file for the first time, I wondered how I could write complex data applications using such a hodgepodge of languages. VBScript, HTML, SQL, in-process objects, and JScript™ all in a single file? No thanks, I figured. These languages are hard enough to write and maintain on their lonesome, much less together.
      Well, it turns out you can write ASPs in a maintainable, friendly manner in the Visual Basic Integrated Development Environment (IDE).
      I love the Visual Basic IDE. Visual Basic 4.0 or 5.0, winter or spring, feast or famine, I can get seriously productive within it. But ASP in the Visual Basic IDE? Well, ASP pages are essentially standalone VBScript applications with a few other languages thrown in for good looks and intelligent content. What about HTML and JScript in the Visual Basic IDE? Yes and no. You need to provide for them, but just where and how to store them are some of the tricks you'll learn in this article.
      To whet your appetite, let's begin by examining a snippet of code. Later on, I'll continue with a discussion of this environment.

Trick 1: Encapsulate all HTML code inside tight, well-named Visual Basic subroutines.
      You were wondering how to slip HTML and JScript into the IDE? Well, this is it. The code in Figure 1 contains a single table and the necessary encapsulated tags to present it. For example, the subroutine TBStart contains code to print the following HTML to the Response object:


 <table border=0 width=400 cellpadding=3 cellspacing=0>.
       In Figure 1, TableWidth, TableColor, and gintIndent are all defined outside the figure as globals. The first two are set prior to calling the TBStart routine. The third, gintIndent, stores a public integer that controls the indenting of HTML code when it's printed to the browser. Text is a subroutine that writes passed values to the Response object using the Response.Write method. The HTML that gets pumped out of this system looks terrific. Figure 2 gives you a taste of how encapsulation works inside the IDE by comparing Visual Basic statements with their HTML output.
      Encapsulation sounds good, but gads, what a task. Actually, I've saved you a fair amount of effort: just move your cursor to the top of this page and download the code. Any tags missed or any new ones you'd like to add (whether they're DHTML, CSS, or XML) can be wrapped and packaged right along with the others.

Setup
      Now that I've piqued your curiosity, let's perform the first task, project setup. First, create a new directory on your drive and use Internet Information Server (IIS) to assign it a virtual directory alias. Make sure to enable the directory's executable rights. You now have a URL of http://127.0.0.1/VirtualDirectory/. I used the TCP loopback address (127.0.0.1) which works on all TCP-enabled machines.
      Describing the entire construction of this environment from the ground up would take much more room than I have here. So to speed up my information transfer rate, I'm going to assume that some time soon you will download the code. The download contains not only the ASP and INC code, but also the executable (and its code) for postprocessing.

Trick 2: Name your classes .ASV and your modules .INV.
      Notice the .asv and .inv file extensions in Figure 3. These files are actually Visual Basic classes and modules. I've named them this way to facilitate the post- processing needed to convert these files into full-blooded ASP pages and server-side includes. For more information, see the sidebar.

Figure 3: Naming Conventions
Figure 3: Naming Conventions


      You can name them this way when you first save them in Visual Basic, or rename them later, changing their names in the Visual Basic project file. You can also just drag them from a file directory of a previous project into the current project window if you're careful with versioning.
      Also notice my X files (the X forces them to the bottom of a directory window when listed by name). These are true classes, and are named as such. Note that their names bear a remarkable resemblance to some ASP objects you may have dealt with. This naming convention has its reasons. I don't want to postprocess the X files because they're meant for support only. I rename the classes and modules to .asv and .inv so they're not confused with real classes and modules, and to allow my postprocessor to pick them out of the crowd of files in the directory in which they live.
      There are four .asv files in my project: three data-oriented and one index/menu. In addition, there are six classes, five of which are direct equivalents of objects I'll discuss in just a moment. The sixth class, X_Support.cls, combined with my only X-type .bas file provides the instantiation of the five other classes required by my ASP VBScript. The last three files contain subroutines and functions, public constants and variables, common code, and application-specific code to be shared by all the pages within this ASP application.

Trick 3: Create shadow representations of the asp.dll objects.
      
      This is a great time to look at some more code. In Figure 4 I've got some fairly simple ASP code that drives ADO. You might think that I pulled this straight from an ASP page, but this code lives in A01.asv, written completely inside the Visual Basic IDE. With a simple Ctrl+F5, the whole project even compiles. Think about it: compiling ASPs!
      If you're familiar with ASP, you'll recognize some old favorites in this code: Server, Request, and Session (Application and Response are represented elsewhere). This snippet came from the Process subroutine, which loads a record's particulars from ADO into Session variables (defined here by constants). The code opens an ADODB connection, then uses it to open a firehose cursor recordset. If the recordset is returned empty, the code saves error messages in a Session-level variable and logs the error to an Application-level log file. If the recordset has one or more records, the code loads a few Session-level variables with values from the database, cleans up after itself, and then uses the application-specific AppRedirect to send the user to the next page. Except for the constants and that last AppRedirect, this code should hold no surprises. I encapsulated the Response.Redirect method so I could add additional control logic if the time came to force a user to a particular page.
      The reason I need to clone the ASP objects at all is that they're not available during the writing or compiling of the ASP pages. Even though the reference has been made to the asp.dll, the objects do not become available until they are instantiated within IIS by the ISAPI asp.dll. Shadowing the objects allows me to trick the pages into thinking they have access to the Application, Session, Server, Response, and Request objects when they actually do not.
      How I get away with forcing Visual Basic to swallow these ASP constructs is what Trick 3 is all about. I do it by spoofing them as classes that contain all the same methods as the original asp.dll objects. For instance, the X_Server class contains four methods—CreateObject, MapPath, URLEncode, and HTMLEncode—and one property, ScriptTimeout (see Figure 5). You might be misled into thinking the methods must do exactly what the Server object does. This is not the case; they needn't do anything, since they're just placeholders for the real thing.
      I'm not going to make an executable out of this project, but I do want the Visual Basic compiler to check all of my variables and constants. For Visual Basic to perform complete compilation, these classes must be instantiated. Otherwise, the code

 Session(Error_Message) = ""
will not have a Session with which to work. This is where X_Support.bas and X_Support.cls come in. X_Support.bas stores object variables that X_Support.cls instantiates with all of my shadow objects when I compile the ASP application. See Figure 6 for clues on how this is done.
      Other necessary constructs are also tucked away within these shadow objects, such as the implementation of Application, Session, and Request dictionaries as collections. These collections allow me to mimic dictionary functionality by letting me set and retrieve key/value pairs within the class instances. They also provide the ability to preload Session and Request variables for interactive debugging.

Trick 4: Wrap all VBScript routines in Visual Basic lookalikes.
      Remember the AppRedirect routine in Figure 4? I wrapped Response.Redirect in a Visual Basic method of my own devise. This sleight of hand becomes quite convenient as I approach subroutines and functions that are found only in VBScript.
      Visual Basic provides the Format function. VBScript doesn't, but instead provides the various FormatDateTime, FormatNumber, FormatPercent, and FormatCurrency functions. For Visual Basic code to compile, I must circumvent this incompatibility by creating my own versions of the VBScript routines and storing them in the X_Support.bas module. But what about the other way, when VBScript code has no equivalent in Visual Basic? Some Visual Basic code has no VBScript equivalent. To handle this, I must enlist the use of a postprocessor text parser designed to rid my ASP pages of those unsightly Visual Basic strings and routines.

Trick 5: Store common and application routines and constants in server-side includes.
      I've encapsulated all the HTML tags I need in their own Visual Basic wrappers. I've then assembled these wrapped tags into one include file, Utility.inv. Utility.inv contains code for tables, forms, formatting, common routines like UtilTrapSingleQuotes (which doubles up single quotes for posting back to SQL Server), UtilAssert (which asserts datatypes for posting back to a database), common constants such as HTML_SPACE = "&nbsp" and Const WHACK = "\", and even JScript. I've created numerous canned JScript routines—including routines such as JSValidateDate, JSValidateEntry, JSValidateInteger, and so on—that can be added to a page by just including their Visual Basic-wrapped names in the Heading section of your ASPs.
      After postprocessing, Utility.inv weighs in at around 33KB. I agree that 33KB for an include might, on first blush, appear large. However, Utility.inv contains all the common code I need to support any ASP application I've built. For an application that needs the utmost in responsiveness, much of it can be trimmed. You might even enlist the use of a Visual Basic code cleaner to rid the code of unreferenced routines and constants. But I don't bother. Read the Performance sidebar and you'll see why.
      There are two additional includes to discuss, App.inv and Admin.inv. You may need to add others, but these three suit the size and complexity of my current ASP applications. App.inv contains application-specific routines: AppPageOpen, AppPageClose, and AppDisplayErrorMessage. These change depending on the app and its specific formatting and functional needs.
      Admin.inv extends the include concept to the extreme. A list of 30 to 60 constants makes its home within Admin.inv. Many of these constants, like A_PRODUCTION_ON, are seemingly simple Boolean switches. A_PRODUCTION_ON has absolute power over the app's debugging features. Set it to 1 and the HTML delivered will be void of all page processing details. Set it to 0 and any error generated will splash across the screen. Three other boolean constraints—A_LOGGING_ON, A_APPLICATION_LOGGING_ON, and A_ SESSION_LOGGING_ON—allow the client to switch custom file logging on or off.

Trick 6: ASP internal file structure.
      If you've downloaded the code and examined the project's ASVs, you'll soon find that they contain only one set of VBScript container tags. The top of each ASV file begins with references to the three includes mentioned previously. Each include is preceded by a custom comment tag 'asp (see Figure 7). The Postprocessing sidebar has details on this tag, which instructs the postprocessor to insert the rest of the comment line into the returned page.
      The next few lines in each ASV are usually page scope constants and variables. I strive to create constants for database and field names, thereby reducing the use of hardcoded values buried in the body of the ASP code.
      The last line in the declaration section for each ASV reads:


 'asp Route
These lines instruct the ASP page (after it passes through the postprocessor, removing all the 'asp and 'vb comments) to include the three server-side include files, open up a scripting tag, initialize the constants and variables that I discussed previously, and perform the subroutine titled Route. At the absolute end of the ASV file you will find the closing scripting tag:

 'asp %>
Two tags enclose the entire ASP page. Between those two tags you'll find nothing but Visual Basic, comprised of code separated into at least three routines. Sub Route is shown in Figure 8, Sub Paint in Figure 9, and Sub Process in Figure 10.
      The idea here is that a page should take care of itself. If I'm adding a record to a database, the HTML entry form should be found in the Paint routine. The entry form processing code should be found in the Process routine of the same ASP page. If processing fails, I merely need to call the Paint routine, which repaints the entry form and any error message I choose to display.
      Route is the first routine called after the page is parsed and executed by the ASP engine. It controls the program's path between the Paint and Process routines (and others if you need them), depending on the state of a Request variable sent back by the Paint routine upon form submission. A typical page transaction might look like Figure 11.
Figure 11: A typical page transaction
  1. Request from browser.
  2. Route subroutine detects first time to page; sends request to Paint stub.
  3. Paint retrieves any data and builds return HTML page.
  4. Paint stub ends and server replies with HTML response.
  5. Browser submits HTTP POST request.
  6. Route sub detects specific POST variable and passes to Process stub.
  7. Process stub adds, updates, or deletes from database, then either Paints on error or...
  8. Redirects on success.
  9. B01.asp contains exact logic and replies with B01-painted HTML page.
Figure1: A typical page transaction

      Two other routines often used in ASPs I've built are List and Validate. List prints out a table-formatted list of database row contents. Validate contains canned JScript that enables POST form variables to be confirmed for entry and data type prior to submission to the server.

Parting Tips
      Referencing msado10.dll and asp.dll in your Visual Basic app will allow you the pleasure of syntax checking your ADODB and your ASP object code. If you use Option Explicit in the IDE, you don't need to make it show up in your ASPs. Option Explicit forces the developer to dim all variables and remain consistent with constant scope. Also, please notice the Hungarian naming convention used throughout my code. I estimate that the increased readability will make your code at least 20 percent easier to maintain.
      I've introduced concepts here I hope will be of value to other programmers working with ASP. As a consultant, I am always searching for better ways to program more efficiently for myself and my clients. If you find superior methods for getting the job done, please cast your findings out to all of us who need the help. We'd much appreciate it.
      I'd like to thank my associate and employer, Andrew Goodman, for aiding in the design and development of this radical yet highly productive ASP coding world.

From the April 1998 issue of Microsoft Interactive Developer.