Microsoft FrontPage 2002 Technical Articles
Best Practices for ASP Development in FrontPage 2002
 

Kevin Spencer
AutoMark

October 2002

Applies to:
    Microsoft® FrontPage® 2002

Summary: Make your applications easier to work with and more extensible with code organization and code optimization. You can increase your productivity, as well as the performance and usage of server resources. And you can write more "bullet proof" code. (13 printed pages)

Contents

Introduction
Code Organization
Code Optimization
Conclusion
About the Author

Introduction

Active Server Pages (ASP) was developed by Microsoft as an alternative to Common Gateway Interface (CGI) and other server-side technologies for producing dynamic content in Web pages. What makes ASP superior to CGI is the fact that server-side scripting can be combined with client-side Hypertext Markup Language (HTML) in the same page/script, thereby making the process of developing and maintaining ASP pages much easier than using CGI. In addition, ASP has the capability of working with server-side Component Object Model (COM) objects, extending the ability of ASP to perform server-side activities that are limited with scripting alone. As a result, ASP is powerful in terms of both functionality and productivity.

ASP is so flexible that it opened up myriad possibilities in terms of how an ASP page could be structured. Unfortunately, many ASP developers haven't given much thought to the maintainability and extensibility of their code. As a result, there is a lot of badly written ASP code out there. I know because I've had to work with a lot of it!

One outcome from badly structured ASP code has been the widely held but mistaken idea that the Microsoft® FrontPage® Web site creation and management tool is not a good environment for ASP development, even though FrontPage was developed for that purpose (among many others). For years, I have felt like a "voice crying out in the wilderness," proclaiming to the world that FrontPage is not only an excellent environment for ASP development but also superior to other ASP development tools in many ways because the limitations it presents challenges the developer to write well-structured ASP code.

In this article, I want to discuss practices that enable you to develop ASP pages that you can easily maintain, easily work with in FrontPage, and run with optimal performance.

Note   For security information about working with Web sites and Web servers, see FrontPage Security Best Practices.

Code Organization

One of the biggest problems in terms of code maintenance is code organization. Where do you write what code in your page? ASP has very few rules concerning this, but establishing a few of your own will make your life much easier, especially if you have to revisit a script that you last worked on six months ago.

Separate Code from Content

Separating your code from your content is one of the most important principles that you can embrace in terms of code maintainability. Because an ASP page typically consists of both executable server-side scripting and static HTML content, it is best to think of the executable code as a separate tier from your HTML. In general, this is accomplished by putting as much executable code above the <html> tag (which indicates where the content begins) as possible.

Of course, the page wouldn't be dynamic if there weren't some executable code inside the HTML portion of the page. But the amount of executable code can be minimized with effective use of global variables, Subs, Functions, and other mechanisms for creating content where you want it.

The failure of developers to separate their code from content is one of the major reasons why FrontPage may seem to many developers to be ill suited for ASP development. FrontPage uses the HTML Document Object Model (DOM) to check for correct HTML in order to render it in Normal view. For example, when a <form> tag doesn't have a matching </form> tag, FrontPage will by default add the </form> tag automatically, making its best guess as to where the tag should be. FrontPage will also do this with "orphaned" <table> tags and several other types of HTML tags with similar characteristics.

FrontPage can't interpret the server-side script, written in Microsoft Visual Basic® Scripting Edition (VBScript), in order to determine whether the HTML that is sent to the browser will be correct. There are many factors that could affect what HTML is ultimately sent to the browser. After all, it is dynamically generated HTML. An ASP script can receive variable values from outside the page, the Query String in the URL, form data posted to the page, application variables, session variables, and so forth—all of which may affect the HTML that is streamed to the browser. Therefore, FrontPage must deal only with the hard-coded HTML in the page and will attempt to "fix" the HTML in order to render it in Normal view.

Of course, you can turn off the Normal view feature. Unfortunately, this means that you no longer will be able to use the productivity tools available in Normal view to write your HTML. In other words, you will have to hand-write your HTML. This means a slowdown in terms of productivity.

Let's take a look at an example of ASP rendered in Normal view. For this example, I configured FrontPage to format my HTML automatically. I created a form and added some dynamic ASP code to conditionally set the Action property of the form according to a value in the Query String of the URL used to get to the form.

<%Dim strAction
strAction = Request.QueryString("strAction")
if strAction = "custom_content" then%>
<form method="POST" action="custom_content.asp">
<%else%>
<form method="POST" action="something_else.asp">
<%end if%>
  <p><input type="submit" value="Submit" name="B1"><input 
      type="reset" value="Reset" name="B2"></p>
</form>

When I switched back to Normal view, FrontPage reformatted the HTML.

<%Dim strAction
strAction = Request.QueryString("strAction")
if strAction = "custom_content" then%>
<form method="POST" action="custom_content.asp">
  <%else%>
</form>
<form method="POST" action="something_else.asp">
  <%end if%>
  <p><input type="submit" value="Submit" name="B1">
      <input type="reset" value="Reset" name="B2"></p>
</form>

Now this was a bad way to write ASP code to begin with, because the server-side executable code is all mixed up with the HTML. But what's worse, FrontPage tried to "correct" what it sees as an error in the HTML, by adding a second </form> tag, thus breaking the code by creating a second form.

FrontPage has come a long way since the early days, and I was able to change the configuration to preserve my existing HTML (uncorrected) and still see the Normal view of this page with the "incorrect" form tags. However, FrontPage assumed that these were two forms and displayed them as two forms, which would make working on this much more difficult in Normal view.

Let's have a look at how this code could be better structured. We want to keep the server-side code separated from the client-side HTML, so I would put the functionality to determine the form's Action property in a function, which I would place above the HTML in the page:

<%
Function GetAction()
   If Request.QueryString("strAction") = "custom_content" then
      GetAction = "custom_content.asp"
   Else
      GetAction = "something_else.asp"
   End If   
End Function
%>
<html>

...

<form method="POST" action="<%=GetAction()%>">
  <p><input type="submit" value="Submit" name="B1">
      <input type="reset" value="Reset" name="B2"></p>
</form>

...

</html>

Notice that the only server-side code in the HTML portion of the page is the single <%=GetAction()%> in the form tag.

To sum up this principle with regard to FrontPage: Make sure that the HTML in your ASP pages is correct without the server-side code, and avoid mixing server-side scripting and hard-coded HTML content in your ASP pages.

Another good reason to separate code from content is that the code will run faster. Every time the compiler has to switch from executing server-side code to streaming hard-coded HTML to the browser, there is processing cost involved. The less you mix them, the faster your code will run and the less it will use of server resources.

Organize Your Executable Code

Well-organized executable code is definitely a plus when it comes to maintaining existing code. Regardless of how you decide to organize your code, it is a good idea to have a standard and stick to it. As an old C programmer, I tend to stick with the same basic flow:

  • ASP preprocessor statements
  • ASP constant definitions
  • ASP variable declarations
  • ASP Subs and Function definitions
  • ASP execution block
  • HTML document

Consistent organization of your executable code will enable you to find any code element easily because you'll have a clue as to what section the code is in. You can use server-side comments to delimit the sections as well, giving you a visual clue as to where the sections begin, if you like. This will increase productivity by reducing time spent in debugging and code maintenance/upgrade.

Declare Variables (Using Option Explicit)

In ASP, it is not required that you declare (Dim) your variables. However, there are several good reasons to do so:

  • Declaring variables increases performance. Variables that are dimmed "on the fly" incur extra processing.
  • Declaring variables prevents accidental misuse of them. By declaring all your variables, you can avoid the conflict of having two variables with different purposes having the same name. (If you declare two variables with the same name, you will not be able to compile the script.) The fact that all variables in ASP are variants (nontyped) contributes to this problem, because two different blocks of code could be working with data of two different data types and still be able to use the same global variable.

Avoid Excess Page Functionality

Because an ASP page can dynamically generate any HTML in the page, it is often tempting (especially to newer developers) to make an ASP page perform a large variety of functions in a single Web page. For example, a Web site might have a page that displays sensitive information from a database, using ASP. Of course, this page would require some kind of password protection. This can be done with ASP, using a form that posts data to another script that checks the logon information against a database and then either redirects to the database page or back to the logon page.

You could design all this functionality into a single Web page, which would display the logon form at first, post the data back to itself, and then either display the sensitive database data or the logon form again, depending on the result.

There are several reasons why this would not be a good idea:

  • Extensibility. In the future, additional pages with sensitive data might be added to the site. At that point, the combination logon/handler/database results page would have to be split up.
  • Maintainability. Debugging code or adding new/upgraded code to such a page is more difficult because it is harder to find the different functionality in such a large script.

Rather than combine all these functions into a single Web page, it would be better if each function had its own page:

  • Logon page
  • Form handler for logon page
  • Database results page

This organization is both more maintainable and more extensible. The form handler page could be modified or extended to redirect to a page other than the database results page easily, if desired. Additional functionality could be added easily. The logon page could be a straight HTML page instead of ASP, saving processor time. The database results page could be modified more easily in the future. Additional sensitive pages could be added to the site later with little effort.

It is not a crime to use more scripts. It is better to break up your pages/scripts into logical component units.

Code Optimization

There are many ways in which any programming code can be optimized. However, I have noticed that many ASP developers do not seem to be aware of these optimization principles. Good code optimization can make your code run faster and use fewer server resources, and can even have the effect of making code easier to debug and maintain. So let's have a quick look at some of these techniques here.

Avoid Redundant Code

Redundant code refers to code blocks that are identical or nearly identical in different parts of an application. Redundant code adds processing load on the server side because an ASP page must be compiled each time that it is requested from the server. And the more code there is to compile, the more processing time is used. Also, redundant code is hard to debug and maintain because such blocks of code generally must be edited separately each time a change is made (even though they all perform the same basic process).

Subs and Functions

If you can identify identical or similar code blocks in your application, consider converting these code blocks to Subs or Functions. Subs and Functions can take parameters so it doesn't matter if the code blocks do similar things; you can create parameters that enable the functionality of Subs and Functions to be extended. In addition, by combining similar code blocks into Subs and Functions, debugging and extending the code can be done more easily. Rather than having to find all the similar code blocks and edit each separately, only one Sub or Function needs to be edited.

COM Objects

ASP has support for integration with COM objects installed on the server. These are dynamic-link libraries that contain compiled Object code. There are many of them already on the server. You can also write your own if you can program with Visual Basic development system version 6.0. COM objects typically provide functionality for commonly used processes such as integrating with a database (ActiveX® Data Objects, or ADO) or using the local Simple Mail Transfer Protocol (SMTP) server to send mail (Collaboration Data Objects for Microsoft Windows NT® Server, or CDONTS). They also are capable of doing much more than VBScript can do, which adds extensibility to your application. COM objects can provide precompiled functionality that saves time and improves performance.

Scripted Classes

Scripted classes are a little-known entity in the Microsoft scripting environment, at least to most ASP developers. Perhaps this is because ASP is basically procedural, while developing a scripted class involves object-oriented design. Object-oriented programming can be difficult to grasp at first.

A scripted class is generally used by means of a server-side include. The included page contains the class definition and is included in any ASP page that uses the class. The following is an example of a script with two classes. I've removed some functions to save space:

<%
Class errorcode

Public number 
Public description

Private Sub Class_Initialize()
    number = 0
    description = ""
End Sub

End Class

Class JobsByEmail

Public body
Public subject
Public allerrors
Public testing
Public companyname
Public webemail
Public fromname
Public webname
Public webnumber
Public logevents
Public usekeywords
Public usejbewebs
Public joburl
Public webdomain
Public toemail
Public cstring
Public emailmethod

Private resumes

' ** ADO variables
Private rs
Private cn
Private q

Public Property Get errorcount()
    errorcount = allerrors.Count
End Property

Public Property Get resumecount()
    resumecount = resumes
End Property

[Some code removed here for brevity]

Private Function sendaspmail(fromname, fromaddress, toname, 
         toaddress, subject, body)
   Dim errorcode
   Set m = CreateObject("SMTPsvg.Mailer")
   m.QMessage = True
   m.FromName = fromname
   m.FromAddress = fromaddress
   m.RemoteHost = Application("aryweb")(29,0)
   m.Subject = subject
   m.BodyText = body
   m.AddRecipient toname, toaddress
   errorcode = m.SendMail
   if NOT errorcode then
      createerror jbeaspmailerror, "Error: " & m.Response
      sendaspmail = "Error: " & m.Response
   else
      sendaspmail = "Email Sent"
   end if
   Set m = Nothing
End Function

Public Function sendemail(fromname, fromaddress, toname, 
         toaddress, subject, body)
   if emailmethod = 2 then
      sendemail = sendaspmail(fromname, fromaddress, toname, 
            toaddress, subject, body)
   else
      Set objMail = CreateObject("CDONTS.Newmail")
      objMail.Send fromaddress, toaddress, subject, body
      Set objMail = Nothing
      sendemail = "Email Sent"
   end if
End Function

[Some code removed here for brevity]

Private Sub Class_Initialize()
    Set allerrors = CreateObject("Scripting.Dictionary")
    cstring = ""
    companyname = ""
    fromname = ""
    webemail = ""
    webname = ""
    body = ""
    subject = "Daily Job Search for " & Date
    testing = False
    resumes = 0
    logevents = False
    usekeywords = True
    usejbewebs = False
    webnumber = 0
    joburl = ""
    webdomain = ""
    toemail = ""
    emailmethod = 2
End Sub

Private Sub Class_Terminate()
    Set allerrors = Nothing
    CloseConn
End Sub
    
End Class
%>

To use the above code in an ASP page, you would simply use a server-side include and instantiate the JobsByEmail class using the following code:

<!--#INCLUDE VIRTUAL="/_private/jbe.asp" -->
<%
Dim JBE
JBE = New JobsByEmail()
JBE.fromname = "Kevin Spencer"
' etc.
%>

The JobsByEmail class then can be used by any page that needs the functionality contained in it. While it still has to be compiled with each page view, the code is in one place where it can be edited easily and is organized. Note the use of the New operator. A COM object is registered on the server and must therefore be called using Server.CreateObject(). Because a scripted class is not registered on the server, the New operator can be used to instantiate it.

Scripted classes also can be stored in Application or Session state, which enables persistence of the data stored in them. We will discuss maintaining state (persisting data across pages) later.

Server-Side Includes

A server-side include is a preprocessor directive that includes code from one page in another prior to compiling and executing the code. Server-side includes are particularly useful when combined with Subs and Functions, effectively making those Subs and Functions available in whatever page they may be needed. In a sense, these become "global" functions, at least global to all the pages the script containing the include statements.

The include page must be compiled for every Web page that includes it. However, it still makes for more maintainable code by eliminating redundant code across pages.

You can organize your Subs and Functions into separate include pages containing similar functionality. For example, you could create one page that opens and closes database connections. Another page could contain Functions for opening, reading, and writing text files. Another page could contain Functions for sending e-mail. If a Web page needed functionality from more than one of include files it could easily include them. And by keeping the functionality in separate files, only the code needed for any particular Web page would have to be compiled to run that page.

Use State Maintenance

Redundant code is a performance hog to be sure and a maintenance pain as well. Now we'll talk about redundant data. By this I mean that certain kinds of data are used in more than one page in your Web site and, in some cases, all the pages in your Web site. A typical example of this would be a Connection String to a database, which would be used by all your database-connective ASP pages. Redundant data can also extend to entire record sets of data collected from a database and be used to create dynamic drop-down list boxes in multiple pages, all containing the same data.

ASP has several built-in objects for maintaining state:

  • Application. Memory space for maintaining state of data that is globally available to all user sessions.
  • Session. Memory space for maintaining state of data that is globally available to all scripts running in the context of an individual user session.
  • Cookies. Text files stored on the client computer for text data unique to a user.

In addition, there are several methods available for passing data from one ASP or HTML page to another ASP page:

  • Query String. The part of the URL following the "?".
  • Form Elements. Passed with a form submission.

There are plusses and minuses—and appropriate places—to use each of these techniques. The Application object is persistent for the life of the Application, which means until the Web server is rebooted. It is global to all user sessions. The Application and Session objects are not thread safe, however, and they are bad places to store open database connections because thread pooling is disabled for such objects.

The Session object is useful for storing information unique to a user session. However, it has the drawback of timing out from inactivity. Therefore, steps must be taken with regard to the loss of session data. An example of a good use for Session state is a password-protected area of a Web site, which requires a logon password to enter. By putting the logon data into the Session object, you protect the area from being accessed by someone after a user leaves the computer without logging off. When the session ends, new logon entry is required.

Cookies are good for storing data unique to a user beyond the length of a user session—for days, weeks, months, or even years. The drawback is that cookies are text data only and, if the client browser has disabled cookies, then no data will be saved.

The Query String is a handy place to pass data from one ASP page to another. It must be string data (although it could be a number, date, array, and so forth, in the form of a string). A Query String is a good place to store data if, for example, you want the user to be able to bookmark the page. If the user bookmarks a page with a URL that includes a Query String, the page will execute the same way it did when the query was first submitted and return the same HTML.

Note   A Query String longer than 255 characters may not work in all browsers. Also, you should avoid putting sensitive data, such as a password, into a Query String to maintain confidentiality. A password, for example, may show up in a Query String, making it accessible to anyone who reads it.

Forms are an excellent way to pass data that is too long to fit in a Query String. Form data can be any length. Data is passed between pages in this way when you don't want the user to be able to bookmark the target page (this would require a form submission to execute properly), or when the length of the data is too long to put in a Query String.

Remember that these are all tools and the right one to use is the one that does all of what you want it to do and none of what you don't want it to do.

Conclusion

In this article, we've discussed two major areas in which ASP code can be optimized for better extensibility, performance, and easier maintenance:

Code Organization

  • Separate server-side code from HTML content
  • Organize executable code
  • Declare variables explicitly
  • Avoid excess page functionality

Code Optimization

  • Avoid redundant code
  • Use state maintenance (persistence of data across pages) effectively

By applying these principles, you can make your applications easier to work with and more extensible. You can increase your productivity, as well as the performance and usage of server resources. And you can write more "bullet proof" code.

About the Author

Kevin Spencer started his Internet Web application programming business, Site Design by TAKempis (http://www.takempis.com), in 1996. He is a senior application developer for AutoMark (http://www.automark.net), and develops Microsoft .NET classes for AutoMark's 4,000+ Web applications.

Page view tracker