ASP Guidelines

From Mynoteswiki.com

J.D. Meier

December 27, 1999

Contents

Summary

The following article originally appeared in the MSDN Online Voices "Servin' It Up" column.

Introduction

The success of your Active Server Pages (ASP) applications often hinges on both architecture and design choices. These choices can be difficult, given ASP technology's broad spectrum and the inherent complexity of today's applications. In this article, I'll provide you with specific guidelines to help you succeed with ASP-based applications. I've organized the guidelines into a set of principles. You can apply the principles below to help guide your decisions when evaluating solutions and technology. Over time, and with the benefit of hindsight, the following principles have emerged from successful development patterns.

Principle 1: Use Standards

Establishing naming conventions and standardizing your directory structures can go a long way to improve your ability to read and maintain for your ASP applications. While there aren't official standards for ASP applications, many developers have established some common practices. I'll share some of the more common practices here. Because ASP technology relies on scripting engines to do its work, and because of the loosely typed nature of script, naming conventions have been somewhat fuzzy. In strongly typed languages, variables are declared as their actual type. When using ASP technology, it's common practice to declare your variables in ASP code the way they should be treated, rather than their actual data type. For example, when working with Visual Basic® Scripting Edition (VBScript), you would declare your flag for success as bSuccess (b for Boolean) rather than vSuccess (v for Variant), even though all VBScript variables are Variants. The following tables show some of the more common naming conventions. Variable Prefixes:

  Prefix 	Variable Use	Sample Variable
  b  or bln 	Boolean 	bSuccess 
  c  or cur 	Currency 	cAmount 
  d  or dbl 	Double 	dblQuantity 
  dt or dat 	Date and Time 	dtDate 
  f  or flt 	Float 	fRatio 
  l  or lng 	Long 	lMilliseconds 
  i  or int 	Integer 	iCounter   
  s  or str 	String 	sName 
  a  or arr 	Array 	aUsers() 
  o  or obj 	COM Object 	oPipeline 

Variable Prefixes for Database Objects:

  Prefix 	Variable Use 	Sample Variable 
  cnn 	Connection 	cnnPubs 
  rst 	Recordset 	rstAuthors 
  cmd 	Command 	cmdEmployee 
  fld 	Field 	fldLastName 

Scope and Usage Prefixes:

  Prefix	Description
  g_ 	Created in the Global.asa. 
  m_ 	Local to the ASP page or in an Include file. 
  (no prefix) 	Non-static variable, prefix local to procedure 

The Knowledge Base (KB) article "Q110264 INFO: Microsoft Consulting Services Naming Conventions for Visual Basic" provides some more insight into naming conventions.

As far as directory structures go, provide a consistent home for your various applications parts. Your actual application directory structure is up to you, but it is common practice to provide separate directories for images, documents, include files, and components. Here is a sample directory structure for a simple ASP application. Sample Directory Structure:

\SimpleAspApp
  \Docs
  \Images
  \Includes

A good directory structure allows you to apply NTFS permissions selectively. You can also take advantage of relative paths from within your ASP application. For example, if from my default.asp page were located in the SimpleAspApp directory, I could refer to my include, top.asp, in the Includes directory using the code below:

  ./includes/top.asp

Note that my include extension is .asp and not .inc. This is for security purposes, but using the .asp extension, rather than .inc, also gives you color coding in Visual InterDev®. For some additional tips and tricks about structuring your ASP application, please see the article "ASP Conventions."

Principle 2: Design to Run Under a Service

ASP runs under a service. When you design ASP applications, you suddenly face security context and threading issues that you didn't run into in desktop applications. In a desktop environment, you are typically working with a single thread of execution running as the Interactive user, and you have access to the current desktop. Under Internet Information Services (IIS), multiple client threads impersonating various user contexts call your application, and your application is limited to the System desktop.

What does this mean to you? Learn about the IIS security model. Also, be aware that just because something works from your Visual Basic IDE does not mean that it will run safely under ASP technology. The Visual Basic IDE does not accurately simulate the runtime environment. Common design mistakes include using .OCX controls that require a user interface, using components that are not thread-safe, and using components that require a specific user context under ASP technology. One of the simplest problems to avoid is trying to access the HKEY_CURRENT_USER (HKCU) registry key from your application (for example, don’t call Visual Basic's GetSetting and SaveSetting functions, which rely on HKCU). Also, do not raise message boxes or other dialogues that require user interaction.

The following articles are great primers on security and authentication issues under ASP technology:

  • "Authentication and Security for Internet Developers"
  • "Q172925 INFO: Security Issues with Objects in ASP and ISAPI Extensions"

Principle 3: Encapsulate Your Business Logic

ASP technology provides presentation services by generating HTML output. Simply put, it generates a user interface. You need to partition your business logic from your ASP presentation script. If you aren't using COM components to separate your business logic from your ASP code, then at least partition your business logic into functions and includes for maintainability, readability, and reusability. You will also appreciate the modularity when you need to troubleshoot and isolate problems.

You can avoid spaghetti code and add structure to your ASP application simply by calling functions and methods from within your script. Here is a quick sample to illustrate separating your logic into method calls from your ASP code:

<% Main()
   MyBizMethod()
   ...
   Sub Main()
         GetData()
         DisplayData()
   End Sub

%> You can apply this principle when working with technologies that include ASP functionality. For example, here's how you might apply this concept when working with Visual Basic WebClasses:

  • Since the WebClass itself is producing HTML through a reference to ASP code, you won’t put your business logic directly within the WebClass. Because this is your presentation layer, you will not run the WebClass directly under MTS/COM+.
  • From your WebClass, you will call separate business components, which can be run under MTS/COM+.
  • You might decide to create your own COM component with a reference to ASP, rather than relying on the WebClass framework and additional overhead of the WebClass runtime -- or you might simply stick with ASP script to automate your business components directly.

Principle 4: Acquire Resources Late and Release Them Early

A common problem is transitioning from the desktop to the server. Many developers coming from a desktop background have not had to worry about server issues and resource sharing. In traditional desktop applications, connecting to the server was a time-consuming process. To improve the user experience, a common approach was to acquire resources early and release them late. For example, many applications would hold an open connection to the database for the application lifetime. This approach worked fine in traditional desktop applications, because the user base was fairly well known and controlled, and the back end was tightly coupled to the front end. However, for today’s Web applications, this approach is not feasible, because of limited server resources facing increasing user bases. To scale your application across users, you need to obtain resources late, and free them as early as possible.

Pooling has helped make this approach more effective. Through pooling, multiple users can share resources with minimal wait time and minimal impact to the server. For example, when working with databases, ODBC connection pooling and OLEDB resource pooling enable connections to be plucked from a pool to minimize database connection overhead. For more information on pooling with ADO, please see "Pooling in Microsoft Data Access Components."

Principle 5: Maintain Complex State Using a Database

While the HTTP Protocol is stateless, ASP developers commonly use ASP functionality for its built-in state-preservation mechanisms. For example, by using ASP technology's built-in Application object, a developer can store resources that can be shared across all users of the application. By using ASP's built-in Session object, a developer can store resources for a single user.

While storing information in ASP technology's Session object may sound like an easy way to persist state, it is expensive -- and can be one of the most limiting factors when it comes to scalability. An application's scalability is essentially its ability to maintain performance as the number of users increases. Consider that for every user, a Session object consumes resources on the server until the session either times out or is abandoned. Sessions also tie you to a server, so they limit your ability to take advantage of a Web farm. Avoid using the ASP Session object for state management when possible. If you completely avoid using sessions, you can disable Session state for your Web application (see the IIS documentation). Otherwise, you can disable Session state on a per-page basis, by using the following <%@ENABLESESSIONSTATE=False %> For simple data, you can persist state between ASP requests using cookies, the QueryString, or hidden form fields. However, for more complex information, it is generally preferable to use a database. A common technique is to generate some unique identifier that is sent to the client on each request and stored as a hidden form field. On subsequent requests, the unique identifier is used to look up state information within the database associated with that user. This approach offers high scalability and cleaner code. For more information on using cookies, the QueryString, and hidden from fields, please see "Q175167 HOWTO: Persisting Values Without Sessions."

Principle 6: Create Objects with Server.CreateObject

When creating objects from ASP technology, you have a choice between the <OBJECT> tag, Server.CreateObject, and CreateObject. Each technique has a slightly different behavior. While there may be slight performance advantages in using the <OBJECT> tag or CreateObject versus Server.CreateObject in IIS 4.0, it is generally preferable to use Server.CreateObject, so that the ASP application is aware of your object. (Note that In IIS 5.0, there is no longer a performance advantage over Server.CreateObject.) The <OBJECT> tag has the advantage of conserving resources by not creating the component until its first method is invoked. Server.CreateObject uses ASP technology's built-in Server object to create the component. Under the covers, it is simply performing a CoCreateInstance -- but ASP is aware of the object. Additionally, ASP technology's legacy OnStartPage and OnEndPage are called. (Note that ObjectContext is preferred starting with IIS 4.0). If you simply use CreateObject, you are bypassing ASP technology and going straight through the Scripting engine. One possible exception where you would call CreateObject instead of Server.CreateObject is when you are calling through a Firewall. For more information, see"Q193230 - PRB: Server.CreateObject Fails when Object is Behind Firewall."

Principle 7: Provide Rich Troubleshooting Information.

Throw yourself a bone, here. Make sure you include error handling in all of your ASP applications. In addition, make sure that you provide useful diagnostic information. I've never seen anyone complain that error information was too descriptive. Be sure to include the following information in your error log:

  • User context (if you're using components, you can make a call to GetUserName)
  • Thread ID (from a component, you can call GetCurrentThreadId) <
  • Time
  • Complete error information (including number, source, and description)
  • Parameter values

Because you will be running under ASP, you may want to write the information either to a file or to the NT Event log. You might also create an application event log to record critical application events that may be useful for diagnosing application failures.

The following articles provide more information on error handling techniques:

  • "Bulletproofing Your ASP Components", by Charles Alexander
  • "Fitch & Mather Stocks: Web Application Design"
  • "Handling and Avoiding Web Page Errors, Part 1: The Basics"
  • "Handling and Avoiding Web Page Errors, Part 2: Run-Time Errors"
  • "Handling and Avoiding Web Page Errors, Part 3: An Ounce of Prevention"

Principle 8: Test Performance, Scalability, and Reliability.

A browser is not an accurate test, except for perhaps usability. Set specific performance goals for your application, and stress test using a load tool, such as the Web Application Stress Tool. While you will need to determine what is acceptable for your environment, here are some general guidelines to help get you started:

  • Test performance by measuring ASP's requests per second, and establish a minimum threshold. In general, simple ASP pages that do not perform database access should return at least 30 pages per second. Pages that call components or access database should return at least 25 pages per second.
  • Test scalability by throwing users at it until requests per second is below a predetermined threshold.
  • Test reliability by removing machines from a Web farm and checking for errors and failures.

Match your test environment to your production environment right down to the firewall. This sounds expensive, but I have heard stories of developers losing their jobs because they did not account for firewall considerations. For more information on using the Web Application Stress Tool to test ASP applications, please see "I Can't Stress It Enough -- Load Test Your ASP Application."

Principle 9: Increase Isolation

Using isolation to protect your application processes can go a long way toward improving server stability. When it comes to Internet applications, isolation can mean the difference between a server down and a crashed application. Protecting your main IIS process (InetInfo.exe) usually ranks high on the list of priorities. When you are working with components, this can be especially true.

A common technique to protect your main IIS process is to run Web applications in their own memory space. You can set this option on a per Web basis in the Internet Services Manager. While there is a slight performance hit because of some marshaling overhead across process, the benefits of application protection usually outweigh the cost. Under IIS 4.0, you can run your application either in-process or out-of-process (OOP). OOP applications will run under a new instance of Mtx.exe. In IIS 5.0, you have additional isolation options. You can set isolation levels to either Low (which means in-process to Inetinfo.exe), Medium (which means a shared instance of DllHost.exe), or High (which means a non-shared instance of Dllhost.exe). In addition to isolating your Web applications in their own memory space, you may want to isolate your untrusted components. Untrusted components are basically components that have not withstood the test of time in production. You can run these components in a Server package, so that they run under a new instance of Dllhost.exe.

In general, a happy medium between performance and protection is to run your Web application using High isolation while running components in library-packages. This minimizes marshaling overhead while providing maximum protection between processes.

Please see the article "Server Reliability Through Process Isolation" for more information.

Principle 10: Don't Abuse Your Thread Pool

Under IIS 4.0, ASP has a default pool of 10 threads per processor managed by MTS. In IIS 5.0, the default is 20. This means each thread is a precious resource potentially handling many client requests. As such, you need to avoid calling methods that will block, such as long database calls. If you have work to execute that will prevent your ASP application from quickly returning a response to the client, consider using queuing. For example, on NT 4.0, you could use MSMQ. On Windows 2000, you can use Queued Components.

Do not store Single-threaded Apartment (STA) components in Session. A common pitfall is to stuff Visual Basic objects in Session scope. This has the effect of locking a user down to a single thread, and defeats the purpose of a thread pool. Potential users are blocked behind other users as they wait for the thread that created their component to become available. Instead, design stateless components that can be created and destroyed from page to page. Quick tip: Make sure that ASP Script Debugging is disabled on the server (using the Internet Services Manager). When ASP Script Debugging is enabled, ASP execution is locked down to a single thread. Please see the following articles for more information:

  • "Q243544 INFO: Component Threading Model Summary Under Active Server Page"
  • "Q191979 PRB: VB Component Not Marked Apartment Produces ASP 0115 Error"
  • "Q243548 Design Guidelines for VB Components Under ASP"
  • "Q243543 Single-Threaded Apartment Objects in Session or Application"
  • "Tuning Internet Information Server Performance"

Summary

Building ASP applications requires a fairly broad scope of knowledge. Part of the challenge of ASP applications is that there are no blanket rules (this is also part of the fun). Add to the challenge the fact that many developers are approaching Internet development from a desktop background. By applying the principles above to your own ASP development, you will hopefully avoid costly mistakes and deliver great ASP applications.

J.D. Meier was born and reared on the U.S. East Coast. Since heeding Horace Greeley's advice, he has worked as a Developer Support engineer specializing in server-side components and Windows DNA applications involving MTS and ASP technology.