ASP Code Formatter

CodeMorph

CodeToHtml

Coding Standards

ASP Coding Standards


You can read and download this ASP coding standards, coding style guide, code convention, code guideline, manual or reference from here for free at your own risk. All trademarks, registered trademarks, product names and company names or logos mentioned herein are the property of their respective owners.

ASP Coding Standards

A.T.G. Consulting, Inc.

Many programmers quail at the sight of source code for "active server pages". The source code is a clumsy hybrid of at least four dissimilar 每 even incompatible 每 languages. And because ASP tends to be treated as a somewhat "informal" language, people sometimes play fast and loose with standards and conventions. In general we believe that you will have much better results in ASP if you follow these simple guidelines:

  1. Many people tend to think of ASP as just a way of inserting custom elements into a pre-existing HTML page; instead you should try to think of your ASP code as a computer program that you are designing to output HTML code. It might sound like a minor quibble or touchy-feely mind game, but we truly believe that this mental attitude is at the heart of your success or failure as an ASP coder.
  2. Always keep track of where you are. ASP lets you mix and match server-side code and client-side code in a fearful mess. You should always remember where you are. Techniques that sometimes help include writing server-side and client-side script in different languages wherever practical.
  3. Treat your server-side code like a computer program. Always declare your variables, always indent your code, always make it as readable and as clear as possible. Split wide lines of code, and continue them on another line so that they can be read without scrolling. Use white-space, indentation, and headers judiciously to make your programs as clear as possible. Remember that someday, if everything goes perfectly, someone will have to figure out what you meant... It might even be you.
  4. Avoid wizards, design-time-controls, code-writing tools, drag-n-drop design tools, and anything else that creates code elements you can't understand, can't debug, and can't control. You may think these tools will save you time -- but you will waste more time in the long run tracing and fixing unexpected anomalies and bugs in someone else's tools. Avoid these things wherever possible.
  5. Know your target browser audience, and test your pages religiously -- in as many different browsers and versions as possible. Browser inconsistencies always creep in slowly, but eventually become ingrained. If you catch them early you will waste less time in the long run. And you should not be lulled into complacency in single-browser environments: point-rev changes between versions of IE have often broken our pages.

If you do these things your life won't be perfect, but you won't waste as much time as you would otherwise.

VBScript Coding

In general we tend to follow these recommendations during development:

  • VBScript code is indented in keeping with standard indenting practices. Although it might be nice, it is not necessary or important to indent any HTML or client-side JavaScript code included within the VBScript to match the VBScript's indentation.
  • All active server pages are commented profusely with VBScript comments 每 enclosed inside "<% %>" tags so that they are only visible to server-side developers. In general we try to keep downloaded HTML code (including JavaScript) free from user-readable comments.
  • Module comments should include a section at the beginning (top) of the module that identifies the name and purpose of the module.
  • In-stream comments throughout the code describe the functions being performed, explain any unusual techniques, and tell why any surprising techniques were performed the way they were. At the same time it is not important to comment every line, or every common technique each time it is used. If something is obvious there's no reason to comment it.
  • Any internal "Sub" or "Function" routines in the code should contain comments at the top that identify the name and purpose of the routine as well as a description of any calling parameters and return values
  • In general during the initial implementation no "change dates" and "changed by" information are attached to code or code comments, although that is a very useful technique for future maintenance programmers.
  • Server-side VBScript code is always declared as "OPTION EXPLICIT" at the top of the primary .asp file. Note: It is not appropriate to put "OPTION EXPLICIT" at the top of .inc ("include") files since they always follow the primary .asp files in order.
  • Since "OPTION EXPLICIT" is in effect, all VBScript variables must be declared using "Dim" statements. This is important even though all VBScript variables are data type "variant" so they are never declared with a type descriptor. Variable names are typically prefixed with letters that indicate the primary "type" of data that they are designed to hold 每 rather than their own "data type" (which is always variant). For example,
    Dim nCount       ' integer
    Dim sTemp        ' string
    Dim oList        ' list object
    
  • There are a few exceptions to this rule. The first is for data elements that are received in "QueryString" or "Form" input. In general we prefer to prefix those variables with the letters "rq" to indicate that they refer to information received from the input stream. For example:
    Dim rqStartDate      ' start date
    Dim rqContractorID   ' contractor ID
    Dim rqSupplierKey    ' supplier key
    
  • A second exception to the naming rule is for simple loop counters. For loop counters we allow the use of the single-letter variable names in the range of "i" through "n". Agreed, it's not according to "formal" standards, but everyone knows what you mean. For example:
    Dim i, j
    For i = LBound(arrX) To UBound(arrX)
        For j = UBound(arrY) To LBound(arrY) Step -1
            If arrY(j) < arrX(i) Then
                ProcessFunction(i, j)
            End If
        Next
    Next
    
  • The last exception to this rule is for common ADO and FSO object types. Rather than the generic "o" or "obj" prefixes, we use common two- or three-letter prefixes that identify the specific object type (such as rs, com, and cmd). In fact sometimes we also allow the unqualified use of the prefix itself. For example:
    Dim rsWhatever       ' recordset
    
    Set rsWhatever = Server.CreateObject("ADODB.Recordset")
    SomeSubroutine rsWhatever
    
    Sub SomeSubroutine(rs)
        If rs.State = 1 Then
            Response.Write "Recordset is open."
        Else
            Response.Write "Recordset is not open."
        End If
    End Sub
    
  • Adherence to hard and fast rules about the specific prefixes to use (for instance, "s" vs. "str" for string) is less important that the ability to easily identify the use and expected data type of a particular variable. Nevertheless, we tend to use the entries in the "short style" column as follows:
data
type
preferred
short style
traditional
long style
String s -- sWhatever str -- strWhatever
Fixed Point
Number
n -- nWhatever
i -- iWhatever
lng -- lngWhatever
int -- intWhatever
Floating Point
Number
d -- dWhatever
s -- sWhatever
dbl -- dblWhatever
sng -- sngWhatever
Boolean b -- bWhatever bln -- blnWhatever
Arrays arr -- arrWhatever( ) ary -- aryWhatever( )
Variants v -- vWhatever var -- varWhatever
Objects o -- oWhatever obj -- objWhatever
Received Input rq -- rqWhatever typically not
distinguished
Common Objects
(ADO & FSO)
con -- conWhatever
rs -- rsWhatever
cmd -- cmdWhatever
fso -- fso
ts -- tsWhatever
typically not
distinguished
Loop Index single-letter
i through n
typically not
allowed

Module Structure

Active server pages ".asp" files will contain declarations of currently-scoped variables in open code, as well as in-stream VBScript code, HTML, and JavaScript.

In general if an active server page performs repetitive or package-able tasks unique to its own function, those repetitive or package-able tasks should be moved to "Sub" or "Function" routines. Those "Sub" and "Function" routines should be contained in a separate source code file, named with the same prefix as the active server page ".asp" file, with file extension ".inc" or suffix "_include.asp". That "#include" file should be incorporated into the primary .asp file using a "#include" directive such as this include for subroutines used by the "supplieredit.asp" page:

<!--#INCLUDE FILE="supplieredit.inc"-->

or, alternatively

<!--#INCLUDE FILE="supplieredit_include.asp"-->

Security Issue! While "include" files are pre-processed by the IIS Active Server Page interpreter when downloaded as part of ".asp" files, it is possible under certain conditions for a user to directly download the text of ".inc" files and view the entire file in plain text. For that reason it is extremely important that no sensitive information 每 such as passwords 每 is included in "include" files with a ".inc" suffix, or in comments inside these "include" files. This is not a problem for files with ".asp" or ".asa" suffixes. For that reason it is often preferred to use the suffix ".asp" for include files.

Repetitive or package-able tasks common to multiple functions, or used by several pages, should be moved to common "include" files.

Configuration parameters such as connect strings, passwords, and other elements that can vary from implementation to implementation should always be defined as constants that are contained and isolated inside their own configuration file -- which should be inside its own directory wherever possible. This allows system operators and IT operations personnel to independently manipulate these parameters without modifying any executable code.

Clarity and maintainability of an application is enhanced by standard and straightforward source code. Wherever practical complex multi-language techniques should be avoided in favor of packaged routines with an approachable calling structure. For example, a standard call to begin a table might use following HTML syntax:

<TABLE BGCOLOR="<%=COLTABLEBORDER%>" BORDER=0
CELLSPACING=0 CELLPADDING=0 BORDERWIDTH=0>
<TR><TD><TABLE BORDER=0
CELLSPACING="<%=COLTABLEBORDERWIDTH%>"
CELLPADDING=1 BORDERWIDTH=0>

Rather than repeating that complex syntax every time you want to begin a table it would be better to package the syntax as a subroutine that can be more easily called from VBScript:

G_TableBegin

The simpler calling structure lets you to include the complex syntax more easily, allows you to easily modify the structure of all tables by simply changing the included common routine, and makes the code self-documenting to a limited extent.

Module Elements

Except where explicitly indicated otherwise, each active server page module should contain the following major sections:

<%@ LANGUAGE="VBSCRIPT" %>
<% OPTION EXPLICIT %>
<%
'-------------------------------
' module: [whatever].asp
' block-style comments should explain
' what each page does and
' how it does what it does
'-------------------------------
%>
<!--#INCLUDE FILE="Adovbs.inc"-->
<!--#INCLUDE FILE="commonfuncs.inc"-->
<!--#INCLUDE FILE="[whatever].inc"-->
<%
Response.Buffer = True
Response.ExpiresAbsolute = 0
G_CheckSecurity
'-------------------------------
' declare local variables
'-------------------------------

[declare variables here]

[process input here]

%>
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" content="text/html"
    charset="iso-8859-1">
<TITLE>Page Title</TITLE>
</HEAD>
<%
<SCRIPT LANGUAGE="JavaScript">

[insert local JavaScripts here]

<%
'-------------------------------
' each client-side JavaScript should be
' commented with server-side VBScript comments
' that do not get transmitted to clients
'-------------------------------
%>
</SCRIPT>
<%
'-------------------------------
' proceed with page body
'-------------------------------
G_BodyStartMenu "document.f1.FriendlyName"
%>
<FORM NAME=f1 METHOD=POST>
<%
'-------------------------------
' form fields
'-------------------------------
%>

[insert form fields here]

<%
'-------------------------------
' custom display logic
'-------------------------------
%>

[insert display logic here]

'-------------------------------
' end of module
'-------------------------------
%>
</BODY>
</HTML>
<%
    Response.End

[insert instream server-side scripts here]

%>

The sample also demonstrates the use of several of the other elements that were described earlier in this document, such as the use of include files and the liberal use of comments throughout the code.

There are a few other techniques that assist in the modular handling of pages:

  • Retrieve user input into local variables and manipulate it within those variables. In addition to improving performance, this will allow you to assign default values to undefined or unselected elements, and will allow you to use the same input section to handle input forms for new records, input forms for update requests, and input forms redisplayed due to validation errors.
    <input type="text" name="foo"
    value="<%=rqFoo%>">
    
  • Perform input validation on both the client and the server. Client-side validation assists the user, improving performance by eliminating the need for extra round trips back and forth to the server just to handle simple errors. This does not eliminate the need for server-side validation, however. Server-side validation is necessary to protect the server -- both from clients who have disabled client-side scripting, and from malicious users who will try to hijack your scripts to do things you don't intend.

Session State

The pseudo-conversational mode of web-based interaction makes it difficult to maintain state information or to pass that information between pages. One method to maintain state is with the use of "Session" variables. These variables persist for the lifetime of the user*s application session. Sessions persist from the time the user requests their first page in the application*s directory until either

  • 20 minutes elapse with no transactions requesting a page within the application, or
  • The session is explicitly terminated with a "Session.Abandon" command.

You should consider carefully before implementing sessions in your pages. There are several reasons that you might not want to use them:

  1. Sessions only work if the visitor has enabled cookies in their browser, and
  2. Sessions do not let your applications scale to multi-server "farm" implementations without the use of other mechanisms for sharing state between the servers.

In general even if you do allow the use of Session variables it is a good practice to avoid storing any OLE objects within sessions.

Session start and end are marked with events in the application's "global.asa" file. Bear in mind that this does not always work to your advantage. For example, in shared environments a single "global.asa" may need to be used by multiple web applications. In that type of environment you may want to find techniques other than global.asa to persist data.

Session variables are variant data elements named with a string. For example, to keep a counter within the session, you might create a session variable in global.asa:

Session("counter") = 0

You could then increment the counter at the beginning of each page:

Session("counter") = Session("counter") + 1

There are two important issues associated with session variables:

  • You should be able to gracefully recover if they are misplaced 每 for example if the user leaves their terminal unattended for more than twenty minutes, then comes back and clicks an "Apply" button. All of the session variables will have been deleted when the session timed out, so you will lose many aspects of application state. Our recommended technique is to put a "save state" routine into the Session_OnEnd routine that writes out all persistent session variables to the customer*s USER_ACCOUNT record in the database. Then when the user is prompted to log back in, the persistent session variables can be easily restored. (Note that there are some interesting aspects to handling events inside global.asa. For one thing, the "Session_OnEnd event does not have access to the client computer, so no user information or "cookies" can be written at that time.)

  • You should keep track of the names of all session variables in a single place so that you don*t run into name conflicts. Our recommended technique is to list all known session variables and their uses in comments located in the "global.asa" file.

Error Handling

It is important to handle errors in code judiciously. When unhandled errors occur, an error message is written to the output stream. When these errors occur in places that write data to the screen, they appear in the middle of output like this:

Microsoft VBScript runtime error '800a000d'
Type mismatch: 'cLng'
/projname/requisitionlist.asp, line 110

This can cause confusion to users, terminate execution of further portions of the page, and result in incomplete or unrecoverable error conditions. These errors can also occur in places that do not write data to the screen (for example, if a VBScript error occurs while writing a section of JavaScript code); in that case there may be no external indication of a problem at all. The user may think the application has "hung", or they may not even be aware of a problem.

Unlike standard VBA, VBScript does not allow "On Error GoTo [label]" commands, so you cannot implement a common error detection and correction routine. Use of a blanket "On Error Resume Next", however, could mask real error conditions in your application and lead to equally undesired results. For these reasons one of the best techniques for error handling is to isolate critical error-prone functions inside callable subroutines that do not implement internal error handling, then call these routines from a top-level routine with a testable "On Error Resume Next" in place. Routines to read and display a complex database record, example, could be called from the top-level .asp code:

<%
On Error Resume Next
ReadRecord rsRecord, nKey
If Err.Number <> 0 Then
    G_ShowError G_CollectErrors
Else
    DisplayRecord rsRecord
    If Err.Number <> 0 Then
        G_ShowError G_CollectErrors
    End If
End If
On Error Goto 0
%>

As soon as an error occurs inside any of the callable routines, control "bubbles up" to the first level on which a "Resume Next" is active. This simplifies testing for errors and responding to them appropriately.

Transactions

Closely associated with error handling is the handling of synchronized database updates. If an error occurs in one part of a multi-part logical chain of updates it is important that these updates be implemented as a unit. For example, the creation of a contractor record might consist of updates to a "CONTRACTORS" table, a "PEOPLE" table, and multiple "MAPPING" and "VALUE" tables. If for some reason part of the update fails all portions of the update should be backed out. This is implemented by means of database transactions.

Transaction processing logic makes use of the error-handling techniques described above. A transaction is initiated at an upper level of the hierarchy, then 每 with a "Resume Next" statement active 每 a subroutine is invoked to perform the chained updates. If any error occurs at a lower level, control passes back to the top level, where the error condition is caught and the transaction is rolled back. If no error has occurred by the time control returns to the top level, the transaction can be committed. Here is a fragment of how this might typically be implemented inside a subroutine named "WriteRecord":

' ----------------------------------
' create required recordset objects
' to be used by subroutines
' ----------------------------------
Set rsS = Server.CreateObject("ADODB.Recordset")
Set rsM = Server.CreateObject("ADODB.Recordset")
Set rsB = Server.CreateObject("ADODB.Recordset")
Set rsP = Server.CreateObject("ADODB.Recordset")
Set rsR = Server.CreateObject("ADODB.Recordset")
On Error Resume Next
' ----------------------------------
' initiate transaction
' ----------------------------------
nLevel = 0
nLevel = Session("db").BeginTrans
If nLevel <> 1 Then
' ----------------------------------
' was a failed transaction in process?
' ----------------------------------
Session("db").RollbackTrans
nLevel = Session("db").BeginTrans
End If
If nLevel = 1 Then
' ----------------------------------
' call subroutine to process update
' ----------------------------------
On Error Resume Next
WriteSub rsS, rsM, rsB, rsP, rsR, id
If Err.Number <> 0 Then
' ----------------------------------
' an error occurred in subroutine
' ----------------------------------
WriteRecord = "Unable to write record. " & _
    G_CollectErrors
' ----------------------------------
' cancel all updates
' ----------------------------------
rsS.CancelUpdate
rsM.CancelUpdate
rsB.CancelUpdate
rsP.CancelUpdate
rsR.CancelUpdate
' ----------------------------------
' close all tables
' ----------------------------------
rsS.Close
rsM.Close
rsB.Close
rsP.Close
rsR.Close
' ----------------------------------
' rollback the transaction
' ----------------------------------
Session("db").RollbackTrans
Else
' ----------------------------------
' success! okay to
' commit the transaction
' ----------------------------------
Session("db").CommitTrans
End If
Else
' ----------------------------------
' we get here if there was already a
' failed transaction pending and we
' could not back it out
' ----------------------------------
WriteRecord = "Synchronization error. " & G_CollectErrors
End If
' ----------------------------------
' clear object references
' ----------------------------------
Set rsS = Nothing
Set rsM = Nothing
Set rsB = Nothing
Set rsP = Nothing
Set rsR =   Nothing 

The essential cleanup functions need to be isolated and called at this level, and a descriptive string is passed back upstream to the routine*s initial caller. Note that it is important that lower-level routines which encounter processing problems that do not themselves generate system-level errors must "Raise" their own error conditions in order to take advantage of this error-handling logic.

  Don't waste time on formatting ASP code by hand any more!  Use SourceFormatX ASP Code Formatter to format ASP code for you!