Chapter 6 - Developing Web Applications

The adoption of Internet standards coupled with the immense popularity of the Web have changed the architecture of distributed computing. The multilayered nature of the Web is an ideal environment for the development of component-based applications. They can be developed and customized quickly, with advanced system services such as database access and transaction processing. System resources can be managed and administered remotely. Moreover, new applications are available immediately, without requiring anything more than a browser on the user's system.

This chapter examines the opportunities that the Web provides for distributed application development, and demonstrates how to use Internet Information Services (IIS) 5.0 to develop the n-tier (also called multitier) Web applications of the future. In the process, the chapter will introduce client-based and server-based technologies that Microsoft has developed to implement this new breed of Web applications. The chapter assumes the reader is familiar with software development concepts.

On This Page

Building on Client/Server
Client-Side Technologies
The Middle Tier
Design Patterns for Web Applications
Debugging Applications and Components
Additional Resources

Building on Client/Server

Market analysts have noticed a trend toward developing multitier applications that are distributed over Internet-standard networks, and predict rapid growth in these distributed systems in the coming years. Some predict that, by 2005, the familiar architecture of client/server applications will be replaced by "super-suites" of interconnected components, operating in frameworks of widely-available distributed systems. In other words, applications will be assembled from reusable building blocks, by using a variety of cooperating subsystems.

Before delving into the implementation details of building Web applications, it might be helpful to take a brief look at the architecture of the Web from a historical perspective, beginning with the traditional client/server architecture.

Client/Server Revisited

Cooperating and communicating applications have typically been categorized as either client or server applications. While the client application requests services using Microsoft® Distributed Component Object Model (DCOM) or remote procedure calls (RPCs), the server application responds to client requests. Traditional client/server interactions, shown in Figure 6.1, are often data-centric and combine most (if not all) of the processing (or business) logic and user interface within the client application. The server's task is simply to process requests for data storage and retrieval.

Bb742411.iischp6_iis0601(en-us,TechNet.10).gif

Figure 6.1 Functional Diagram of a Client/Server (Two-Tier) Application

Client/server (two-tier) applications have usually performed many of the functions of stand-alone systems; that is, they present a user interface, gather and process user input, perform the requested processing, and report the status of the request. Because servers only provide access to the data, the client uses its local resources to process it. Out of necessity, the client application can tell where the data resides and how it is laid out in the database. Once the server transmits the data, the client is responsible for formatting and displaying it to the user.

The primary advantage of two-tier applications over monolithic, single-tier applications is that they give multiple users access to the same data simultaneously, thereby creating a kind of interprocess communication. Updates from one computer are instantly available to all computers that have access to the server.

However, the server must trust clients to modify data appropriately—unless data integrity rules are used, there is no protection against errors in client logic. Furthermore, client/server connections are hard to manage—the server is forced to open one connection per client. Finally, because much of the business logic is spread throughout a suite of client applications, changes in business processes usually lead to expensive and time-consuming alterations to source code.

Although two-tier design still continues to drive many small-scale business applications, an increasing need for faster and more reliable data access, coupled with decreasing development time lines, has persuaded system developers to seek out a new distributed application design.

Multi-Tier Design

The new system design logically divides computing tasks across the application. Viewed from a purely functional standpoint, most applications perform the following three main tasks: gathering user input, storing the input as data, and manipulating the data as dictated by established operational procedures. These tasks can be grouped into three or more tiers, which is why the new system design provides for three-tier, or multitier applications. The application tiers, shown in Figure 6.2, are:

  • Client Tier The user interface or presentation layer. Through this topmost layer, the user can input data, view the results of requests, and interact with the underlying system. On the Web, the browser performs these user interface functions. In non­Web­based applications, the client tier is a stand-alone, compiled front-end application.

  • Middle Tier Components that encapsulate an organization's business logic. These processing rules closely mimic everyday business tasks, and can be single-task-oriented, or part of a more elaborate series of tasks in a business workflow. In a Web application, the middle tier might consist of Microsoft® Component Object Model (COM) components registered as part of a transactional application or instantiated by a script in Active Server Pages (ASP).

  • Third Tier A database management system (DBMS) such as a Microsoft® SQL Server" database, an unstructured data store such as Microsoft® Exchange, or a transaction-processing mechanism such as Transaction Services or Message Queuing. A single application can enlist the services of one or more of these data providers.

    Bb742411.iischp6_iis0602(en-us,TechNet.10).gif

    Figure 6.2 Three-Tier Architecture on the Web

Application tiers don't always correspond to physical locations on the network. For example, the middle and third tiers may coexist on the same server running both IIS 5.0 and SQL Server, or they could be separate. The middle tier alone may tie together several computers, and sometimes the server becomes a client itself.

Separating the application into layers isolates each major area of functionality. The presentation is independent of the business logic, which is separate from the data. Designing applications in this way has its tradeoffs; it requires a little more analysis and design at the start, but greatly reduces maintenance costs and increases functional flexibility in the end.

The explosive growth of the Internet is a strong motivation for organizations to adopt n-tier architectures in their products. However, organizations still face challenges. How can they take advantage of new technologies while preserving existing investments in people, applications, and data? How can they build modern, scalable computing solutions that are dynamic and easy to change? How can they lower the overall cost of computing while making complex computing environments work? One solution is Microsoft® Windows® Distributed interNet Applications (DNA).

Windows DNA

Windows DNA architecture is Microsoft's framework for building a new generation of n­tier computing solutions. Windows DNA provides a framework for delivering solutions that meet the requirements of corporate computing, the Internet and intranets, and global electronic commerce, while reducing overall development costs.

The heart of Windows DNA is COM. Windows DNA architecture makes use of a common set of services, including Hypertext Markup Language (HTML) and Dynamic HTML (DHTML), Microsoft® ActiveX® controls, COM components, client-side and server-side scripting, transactions, security and directory services, database and data access, systems management and HTML, and component authoring environments. These services are exposed in a unified way through COM, which enables applications to interoperate and share components easily.

Bb742411.iischp6_iis0603(en-us,TechNet.10).gif

Figure 6.3 The Windows DNA Family of Technologies

Windows DNA builds on the client-side services of the Microsoft® Windows® operating system and Microsoft® Internet Explorer, on the distributed infrastructure of Microsoft® Windows® 2000 Server and the Microsoft® BackOffice family, and on the company's integrated tools, such as the Microsoft® Visual Studio® development system. Because Windows DNA architecture uses open protocols and published interfaces, organizations can integrate third-party products and solutions. In addition, because Windows DNA architecture embraces an open approach to Web computing, it builds on the many important efforts at developing standards approved by bodies such as the World Wide Web Consortium (W3C) and the Internet Engineering Task Force (IETF).

For more information about Windows DNA, see https://www.microsoft.com/dna/.

The Future of Applications on the Internet

Customers are beginning to demand global access to the information they need, both public and personal. Users increasingly want to use a single client application for their information access needs, and they rely on the versatility of the network and servers to provide content and services. Users will come to depend on these applications and want them to be universally available; they might even want to replace local applications on their desktop systems.

Consequently, there is likely to be an explosion of HTML-based server applications to feed the ubiquitous availability of the powerful Internet client. Applications will be factored into user interface—only client components (with little software required beyond the standard Internet browser), and a middle tier of server components that have no user interface and that provide services to the local desktop or across the Internet.

The following sections describe the roles that the client and middle-tier play in distributed Internet and intranet applications. The third tier is discussed in "Data Access and Transactions" in this book.

Client-Side Technologies

This section presents a survey of the technologies that make up the client tier of today's Web applications. Each technology is considered from the perspective of what it is, how it works as part of a Web application, and what the issues are regarding its use. This section does not discuss how to develop applications using client-side technologies.

Text and HTML

HTML is the basic formatting language of Web pages. Just like a printed page, text on a Web page can include a variety of font faces, colors, font weights and attributes, spacing, and columns. In addition, Web pages can include tables, frames, and HTML forms. Web applications make heavy use of tables and forms to display data, organize application elements, and collect user input.

HTML adheres to a set of standards that makes it usable over the entire Internet, as well as on intranets. For more information about HTML standards, either see the W3C home page at https://www.w3.org or consult your HTML reference materials. For information about the newest extensions to HTML, see "Dynamic HTML" later in this chapter.

Graphics and Multimedia

Graphics and multimedia, if used effectively, can greatly enhance the look and feel of an application. They can instruct, as well as draw the eye to important areas of the screen.

Multimedia is an especially powerful tool on an intranet. For example, by using streaming audio/video, like that available through Microsoft® NetShow", you can broadcast special events as they happen, or use prerecorded video to train employees in complex technical operations.

Because of the speed limitations of modems used for most Internet connections, heavy use of graphics and multimedia over the Internet should be restricted. Low-resolution graphics, if designed correctly, not only download more quickly but may actually look better than high-resolution graphics on most computer monitors.

Hyperlinks connect the parts of your application together, act as the application's "menu," and can perform both client-side and server-side actions. For example, clicking a hyperlink can cause a page to load in another frame, or can run a client-side script to change the layout of the page.

Hyperlinks are normally embedded directly on the page as text or graphics (such as an image map) where the user can view and click them. They can also be activated when a form is submitted, or client-side script can dynamically create and trigger them.

There are lots of ways to present links to the user. You can simplify the layout of a large number of hyperlinks by grouping similar choices together, by using a similar style of presentation, or by hiding and displaying links as appropriate. For instance, you can choose to display links dynamically, based on the privilege level of your users. Only visitors with high-level access would be able to view links that perform advanced or administrative actions.

Client-Side Script

Client-side scripts run within the user's browser, using the processing power of the user's (client) computer. They can be written in any language supported by the browser; the most common is JavaScript, which is supported by most browsers. Some browsers, such as Internet Explorer, also support Microsoft® Visual Basic® Scripting Edition (VBScript). Client-side scripting enhances Web pages with a variety of custom capabilities. For example, you can use scripts to perform field edits and calculations, manipulate the client window, or validate form input. Scripts normally appear directly on the page they affect, but they can be used to manipulate the content of pages in another frame or browser window as well. For more information about browser support for client-side technologies, see Table 6.1.

ActiveX Controls

ActiveX controls can be used either to customize the user interface, or as "plug-in" applications (such as the Macromedia Shockwave animation control and the RealNetworks streaming audio/video player). ActiveX controls can perform a variety of tasks, from navigation to real-time interaction with stock quotes. They can be written in any language that supports COM Automation, including Microsoft® Visual Basic®, C++, Java, or even COmmon Business Oriented Language (COBOL).

ActiveX controls can be embedded into the HTML page by using the HTML <OBJECT> tag. If the control does not exist on the user's system, it can be downloaded using the URL specified in the CODEBASE attribute (see the following example). The <OBJECT> tag also supports component versioning. Once the control is downloaded and installed, the browser continues to use the cached control until an updated version is available on the server. The following example demonstrates the CODEBASE attribute:

<OBJECT ID="BoomButton" WIDTH=225 HEIGHT=35
CLASSID="clsid:56F1BF40-B2D0-11d0-A6D6-00AA00A70FC2"
CODEBASE="https://domain.microsoft.com/AControl.cab#Version=1,0,0,1">
</OBJECT>

A malicious ActiveX control could perform potentially destructive actions on the user's computer, such as erasing data from the hard drive. To help users determine whether a control is safe to use, Microsoft has developed security guidelines for vendors to follow when releasing a control. A control should identify its creator with a "signature" issued by a well-known security authority, such as VeriSign. Microsoft® Authenticode", the company's code-signing technology, assures accountability and authenticity for software components distributed on the Internet. Only the original owner can modify a signed control, which prevents tampering by third parties. (For more information about Authenticode and code-signing, see https://www.microsoft.com/security/default.asp.)

As of this writing, only Microsoft® Internet Explorer 3.0 or later includes native support for ActiveX controls. Because of this, ActiveX controls are probably most useful for intranet sites or sites created especially for Internet Explorer users.

Case Study of a Web Application

Microsoft recently introduced a new means of filing employee expense reports. The old system required employees to prepare expense report forms, attach receipts, and submit them to their managers, who would review the forms and submit them to the accounting department. Mistakes were common, and forms often had to be resubmitted. Once the paperwork was finished, the reports were painstakingly entered into a database.

To eliminate some of the problems with the existing system, the accounting department introduced a Web application to control and streamline the employee reimbursement process. The new application allows the employee to report expenses using a Microsoft® Excel worksheet modeled after the paper version of the old form. The worksheet, after it has been downloaded to the user's browser, validates the data as it is submitted, catching most user errors up front. When the documents are ready, the electronic form can be routed to the employee's manager by e-mail. After the manager approves the form, a copy is returned to the accounts department and an approval notification is sent to the employee. The accounting department then performs all of its final work online, saving considerable time and effort.

The new expense-reporting system effectively:

Improved Control Control was a recurring theme throughout the design of the application. The worksheet and associated Web site were carefully designed to ensure that both employees and managers understood their responsibilities. Using an electronic form meant that reports could be tracked on their way through the system, making expense auditing on the back-end faster and more verifiable.

Reduced Labor Because there was less paperwork to be processed, the company was able to improve control while reducing the labor required to process and audit expense reports.

Decreased Resource Part of the goal was to reduce paper waste. The online system requires fewer forms, and hence fewer resources.

Decreased Payment Cycle Time The old process took 8 to 10 days from time of approval to employee reimbursement. Approval sometimes required weeks. The online solution enables a much faster turnaround time. In most cases, payment can be made within one or two days of approval.

Payoffs such as these are a common theme in most Web applications. A well­designed application can improve the way you work by being available wherever there's a browser.

Cascading Style Sheets

The Cascading Style Sheet (CSS) standard gives authors more control over fonts, sizes, two-dimensional overlapping, and exact glyph positioning (for rendering scripts and fonts of various languages, such as the diacritical marks used in Vietnamese or the calligraphic script of Urdu). CSS also separates formatting information from the Web page content, making it much easier to design and revise pages.

Style sheets control the appearance of HTML tags; they do not replace them. Style sheets give you the ability to attach style information to one or more HTML documents and to any of the tags within them, which greatly expands your control over the appearance and structure of each page. Formatting information can be applied to custom tags for a given browser, as well as to standard HTML tags.

CSS information can be specified by linking, embedding, or as an inline style modifier. An HTML document can use any combination of these three methods. However, the most common method is linking, because it establishes a basis for embedded and inline style modifications. An example of a link to a style sheet is shown here:

<LINK REL=STYLESHEET TYPE="text/css" HREF="./myCustom.css">

Note: Because of changes to the CSS standards recently adopted by the W3C, the CSS support in Internet Explorer 3.0 is not fully compatible with that found in Microsoft® Internet Explorer 4.0, which supports the new standard. A detailed description of the latest CSS 2.0 standard is available at https://www.w3.org/Style/.

Dynamic HTML

Dynamic HTML (DHTML), which is supported by Microsoft® Internet Explorer 5, is an emerging standard that is more than just an extension of standard HTML. With DHTML, you can easily add advanced functionality that was previously difficult to achieve without client-side controls. For example, you can:

  • Hide text and images in your document and keep this content hidden until a given time elapses or until the user wants to view it.

  • Animate text and images in your document, independently moving each element from any point to another in the document, following a path that you choose, or that you let the user choose.

  • Create a ticker that automatically refreshes its content with the latest news, stock quotes, or other data.

  • Create a form; then instantly read, process, and respond to the data the user enters in the form.

Internet Explorer 5 does not require additional support from Java applets or embedded controls to achieve these effects. It automatically reformats and redisplays the DHTML page to reflect dynamic changes in content styles. It does not need to reload the document, load a new document, or depend on the server to generate new content. Instead, it uses the power of the user's computer to calculate and carry out changes.

DHTML documents make heavy use of styles and script to process user input and directly manipulate the HTML tags, attributes, and text in the document. Through the Internet Explorer 5 object model, you can control every property of every HTML tag to precisely control the layout, appearance and function of your page.

Note: Not all the features of DHTML are compatible with all browsers. Web pages intended for viewing by browsers other than Internet Explorer 5 should be tested for compatibility with other browsers.

For more information about DHTML, see https://msdn.microsoft.com.

Data Binding

Using DHTML, the results of database queries can be "bound" to HTML elements, such as the rows of a table. (You can also use data-binding ActiveX controls, such as the Advanced Data Connector (ADC), included in earlier versions of Internet Explorer.) Data Binding allows you to remotely view and modify the results of database queries within the browser. It is a function of Remote Data Services (RDS), which is part of the Microsoft® ActiveX® Data Objects (ADO) family of data access components.

Note: Data binding is supported if you are using Microsoft® Internet Explorer 4.0 or later.

For more information about RDS and ADO, see "Data Access and Transactions" in this book or see the IIS 5.0 online product documentation. For more information about data binding, see https://msdn.microsoft.com.

Browser Support

In intranet scenarios where a single browser type can safely be assumed, you can design your sites around browser-specific technologies with impunity. (Just in case someone is an errant browser user, you should alert your users with a "Best viewed with" graphic on the site's home page.)

On the Internet, however, you can't assume that everyone has an up-to-date browser. And, even among newer browsers, several different types are available; Microsoft, Netscape, and Sun Microsystems have all released browsers with varying degrees of support for ActiveX, Java, scripting, and HTML. The question of what functionality to perform on the client depends on the variety and capabilities of browsers you want to support.

Using the lowest-common-denominator approach, pages contain no more functionality than the least capable browsers can process successfully. Content is guaranteed to be viewable in its entirety on any browser. Unfortunately, users might notice, and be disappointed by, the limited functionality this approach requires.

Some sites provide text-only versions of their pages, or frames-free areas for less capable browsers. This duplication ensures that all users get the same information, but it requires that you develop, test, and maintain multiple versions of your site. Often the less functional version remains underdeveloped, as the focus of development tends toward "bells and whistles."

The best approach may be to develop pages using basic technology, such as HTML and JavaScript, and to add specific features once you have determined the browser type. This is often a good middle ground, because all pages can be developed using one set of design elements and content. The advanced features of the Web site, such as data binding, are made available only to browsers that support them.

The Browser Capabilities component included in the scripting environment of ASP provides a way to detect the browser type and to tailor the returned document in order to exploit browser-specific capabilities. For more information about this component, see the IIS 5.0 online product documentation.

Table 6.1 summarizes browser support for different client technologies.

Table 6.1 Support for Client Technologies by Browser

Technology

Widely Supported

Internet Explorer 3.0x

Internet Explorer 4.0 and 5

HTML

X

X

X

Graphics and multimedia

X

X

X

Hyperlinks

X

X

X

JavaScript

X

X

X

VBScript

 


X

X

ActiveX controls

 


X

X

Cascading Style Sheets

 


X

X

Dynamic HTML

 


 


X

Limitations of Client Technologies

Although it's possible to create applications that rely exclusively on client-side technologies, they are somewhat limited in their capabilities, just as client/server architecture is. There are several reasons why client/server architecture isn't suitable for full-scale enterprise applications on the Internet:

  • A client application using client-side ActiveX controls or client-side scripting is not supported by all browsers. A line-of-business application for the Internet must work with as many browsers as possible, including those that do not support HTML tables, frames, Java applets, client-side scripting, or ActiveX controls.

  • Coding business logic as client-side script fails to protect your programming investment (because the source code is available to all). Java applets and ActiveX controls are more secure, but whenever you combine business logic with the user interface, your application becomes harder to support and to debug. In addition, the resulting components are less likely to be reusable in other applications.

  • Client-centric applications do not take full advantage of the three-tier programming model. Designs in which the client plays more than a supporting role typically take on tasks that are better suited for the server, such as resource management and data manipulation.

The Middle Tier

This section discusses what is perhaps the most important layer of Web programming, the middle tier. It is here that user input is combined with business logic to perform the work of your site.

The middle tier is not always just a single layer of logic. It can consist of many interrelated technologies, seamlessly combined to create the illusion of a single multipurpose layer. For example, the client's request could be preprocessed by an Internet Server Application Programming Interface (ISAPI) filter, then execute a script to run a custom-built component that manipulates a database with ADO. Technology integrates with technology, layer upon layer: a demonstration of true n-tier architecture.

Middle-tier technologies discussed in this section include CGI (Common Gateway Interface) applications, ISAPI extensions and filters, ASP, process isolation and crash recovery, and others.

CGI Applications

In the past, Web server application programming would usually require developing CGI programs or scripts.

CGI applications are most widely used on UNIX systems to create executable programs that run on the Web server. CGI programs are typically written in the C language, but can also be written in interpreted languages such as Perl. Remote users can start CGI applications on the server simply by requesting a URL containing the name of the CGI application. Arguments following the question mark in the URL are passed to the CGI application as environment strings. The output of a CGI application isn't much different from a desktop application; HTTP headers and HTML are generated using the basic output functions of the language (for example, printf in C).

CGI applications are easy to write, but scale very poorly on the Windows operating system. Because a separate process is spawned for each client request, hundreds of clients create hundreds of instances of the CGI program, each requiring its own memory space and system resources. This isn't such a bad thing on UNIX, which is designed to handle multiple processes with very little overhead. However, Microsoft® Windows® 2000, which is optimized for thread management inside a process, expends more system resources when creating and destroying application instances. As a result, ISAPI was developed specifically for IIS 5.0 as a high-performance Windows alternative to CGI.

ISAPI Extensions and Filters

An ISAPI extension is a run-time dynamic-link library (DLL) that is usually loaded in the same memory address space occupied by IIS 5.0. Since it is a DLL, only one instance of the ISAPI extension needs to be loaded at a time. Of course, the ISAPI extension must be thread-safe, so that multiple client requests can be received simultaneously.

Although ISAPI extensions are more complex than CGI applications, ISAPI uses a relatively simple Application Programming Interface (API). For each client request, the Web server invokes the HttpExtensionProc ISAPI call and passes a pointer to an ISAPI Extension Control Block (ECB), which contains information about the request. The ISAPI DLL can use server callback functions to access information such as server variables. The ISAPI ECB also provides the developer with access to some general-purpose support functions, such as URL redirection, session management, and response headers, which are not available to CGI applications.

Despite the obvious benefits over CGI, ISAPI extensions present some maintenance problems. For instance, if you want to make even a minor change to the HTML returned by an ISAPI extension, you have to recompile and link it. Also, an ISAPI DLL can cause the Web server to crash, if it hasn't been thoroughly tested and verified before being deployed and run in the Web server process.

You can select which ISAPI extensions are loaded in process with IIS 5.0 and which extensions should be loaded in a separate process. ISAPI extensions in a separate process can be stopped and restarted independently of the server process, and can be restarted automatically after a crash. Although out-of-process extensions are slower than in-process ones, being able to isolate and reload applications that are under development offers advantages in service reliability. For more information about out-of-process extensions, see "Data Access and Transactions" in this book.

ISAPI can also be used to create ISAPI filters. Filters are a fairly new concept in Web server extensibility—there is no CGI counterpart. ISAPI filters can intercept specific server events before the server itself handles them. The calling convention for filters is very similar to that of extensions. When a filter is loaded (usually as the Web service starts), it indicates what sort of event notifications it will handle. If these events occur, the filter has the option of processing the events, passing them on to other filters, or sending them to the server. In this way, you can use ISAPI filters to provide custom authentication techniques, or to automatically redirect requests based on HTTP headers sent by the client, such as Accept-Language.

However, filters can degrade performance, if they are not written carefully. With IIS 5.0, ISAPI filters can be loaded for the Web server as a whole or for specific Web sites. However, they cannot be run out of process.

Active Server Pages

The ASP scripting environment greatly simplifies server-side programming so that you can easily create dynamic content and powerful Web-based applications.

Scripts in ASP pages can perform the same sorts of tasks as CGI and ISAPI applications, but are much easier to write and modify. ASP creates a higher level of interactivity by managing application and session state on the server, thereby reducing the amount of information that needs to be transmitted back and forth between the server and the client. ASP makes it easy to work with information entered into HTML forms or in the URL as parameters. You can also control advanced HTTP features from client-side cookies and client security certificates. See the IIS 5.0 online product documentation for more information about advanced features.

At the heart of ASP is an ISAPI extension—Asp.dll— that compiles and caches .asp files in memory at run time using a script interpreter. The IIS 5.0 script map associates the .asp extension to Asp.dll. Because ASP must interpret and compile scripts before executing them, complex scripts can be about four times slower than plain HTML, and two to three times slower than ISAPI, when they are first requested. Afterward, the compiled version of the page is cached in server memory, making subsequent requests significantly faster and amortizing the initial cost of compilation over potentially thousands of page requests.

ASP is designed for usability and ease of development, giving you the opportunity to dramatically decrease time spent in development. However, it will never outperform static content, or custom-written, task-focused C++ ISAPI extensions. Only carefully designed ASP applications, combined with server-side components, can approach the speed and performance of ISAPI applications. For information about designing ASP pages, see the "Building ASP Pages" topic in the IIS 5.0 online product documentation.

ASP Server-Side Scripting

You can create highly interactive pages that are independent of the type of browser used to access those pages. Unlike client-side script, with ASP you can "hide" your scripting on the server, enabling you to protect your development ideas and intellectual property.

The ASP scripting environment is "language agnostic," meaning that it isn't limited to a particular language. VBScript, Microsoft® JScript®, or any language for which a third­party ActiveX scripting engine is available (such as PerlScript, REXX, or Python) can be used to create scripts in ASP pages.

ASP scripting instructions appear side-by-side with HTML. (In fact, you can create an ASP page simply by changing the file extension of a file in plain HTML to .asp.) To differentiate between HTML and script meant to run at the server, ASP uses special tags, called server-side scripting delimiters, to indicate server-side script: <%and%>. Script appearing inside these delimiters will be invoked on the server as the page is processed. A special form of these scripting delimiters, <%= expression %>,can be used as a shorthand for returning values from script.

The following line of server-side VBScript code returns the current date:

Today is <%= Date %>.

This instruction generates something like the following line (the exact text depends on the date):

Today is 7/4/99.

Note: You can also use the Write method of the ASP Response object to display text or the results of an expression on the page.

A slightly more complex example of ASP might use the conditional execution elements of the scripting language, intermixed with HTML, as follows:

<% If Hour(Now) < 12 Then %>
<FONT COLOR=YELLOW>Good Morning!</FONT>
<% ElseIf Hour(Now) < 18 Then %>
<FONT COLOR=LIME>Good Afternoon!</FONT>
<% Else %>
<FONT COLOR=ORANGE>Good Evening!</FONT>
<% End If %>

Although each page has a primary scripting language, you can use more than one scripting language on a single ASP page. You can set the primary scripting language for an application in Internet Services Manager. Use declaratives (also known as @-directives) to define the primary scripting language for a page. For more information about declaring scripting languages, see the IIS 5.0 online product documentation.

ASP subroutines and functions can be in any script language, although if you define them inline with the rest of the script, you're limited to the primary scripting language. To change the scripting language of the subroutine, you need to use HTML <SCRIPT> tags to define them. You will also need to add the RUNAT=SERVER attribute, to indicate that this script is intended for the server rather than for the client.

The following sample page demonstrates how to combine a variety of scripting languages and subroutine declaration styles into a single ASP file:

<%@ LANGUAGE="VBScript" %>
<HTML>
<HEAD>
<% Sub InlineSub %>
This text won't be displayed until this subroutine is called.<br>
<% End Sub %>
<SCRIPT LANGUAGE="VBScript" RUNAT=SERVER>
'Immediate script (outside a function).
Response.Write "This text is displayed last"
</SCRIPT>
<SCRIPT LANGUAGE="JavaScript" RUNAT=SERVER>
function TestJavaScript(str) {
Response.Write(str);
}
</SCRIPT>
<SCRIPT LANGUAGE="PerlScript" RUNAT=SERVER>
sub TestPerlScript {
$Response->Write($_[0]);
}
</SCRIPT>
</HEAD>
<BODY BGCOLOR=#FFFFFF>
<%
Response.Write "This is VBScript<br>"
TestJavaScript "This is JavaScript<br>"
TestPerlScript "This is PerlScript<br>"
InlineSub
%>
</BODY>
</HTML>

Note: You can use <SCRIPT> tags to enclose immediate server-side script, but don't expect it to be run until the entire page has been processed. Earlier versions of ASP used <SCRIPT> tags before the introduction of the <% %> delimiters, but their use for immediate script is now discouraged. You should reserve server-side <SCRIPT> tags for defining functions and subroutines only.

Execution Behavior of Scripts in ASP Pages

When you write ASP applications, you're operating in the world of IIS 5.0 and HTTP. Web developers who don't have a firm grasp of this architecture find themselves puzzled by strange errors in what seems to be straightforward code.

Consider the process shown in Figure 6.4.

Bb742411.iischp6_iis0604(en-us,TechNet.10).gif

Figure 6.4 Requesting an .asp File from IIS 5.0

When a client browser requests an ASP page, a number of events occur in the following sequence:

  1. The client requests an ASP page by sending an HTTP Request to the Web server.

  2. Because the page has the .asp extension, the service (IIS 5.0) recognizes it as a script­mapped file, and sends the file to the appropriate ISAPI extension (in this case, to Asp.dll) for processing. (This step does not occur when the client requests an HTML file.)

  3. The ASP ISAPI processes any server-side include directives first, before any server­side script is compiled. Next, the script is executed, and dynamic text, if any, is incorporated into the page that will be returned to the client. (This step only happens when the page is first requested. Previously compiled pages are retrieved from a server­side cache for faster performance.)

  4. The server creates the resulting HTML page to be sent back to the client. The page output is sent incrementally as the page is generated, or all at once if the response is buffered.

  5. Once the client receives the ASP page, it loads any client-side objects and Java applets, executes any immediate client-side script code and displays the Web page according to the HTML specification.

While this process looks simple, keep in mind that the client and server could be hundreds, or even thousands, of miles apart. Therefore, when a problem arises, you must determine where the error is occurring. Is it on the client or on the server? Equally important is understanding when each operation takes place. After ASP completes its processing in step 3 and the server sends the response in step 4, it moves on to other activities and other clients. The only way the client can recapture the server's attention is to request another page via the HTTP protocol. In other words, there is no real connection between the client and server. This is a very important concept.

Sometimes developers try to access server-side scripts or objects from the client, or conversely, client-side objects or scripts from the server. For example, consider client-side code that attempts to access one of ASP's built-in objects, such as the Sessionobject. The attempt is destined for failure, because the code running on the client has no way of accessing an object located on the server. A typical error message might appear as follows:

VBS Script Error: Object Required: Session

Now consider an example in which a server-side script attempts to manipulate a client-side object. Suppose the developer wants to use server-side script to populate a client-side control called ListBox1, using the following instruction:

<% ListBox1.AddItem Value1 %>

The problem is that the HTML page, including the list box, does not yet exist when the server-side code is executed. Therefore, this instruction generates an error.

On the other hand, you can use server-side code for generating client-side code in order to populate a list box. For example, you could create a Window_OnLoad event, which is executed by the browser as soon as the window and its child controls are created. The following code uses server-side script to provide the AddItem method with values stored in the variables Value1, Value2, and Value3.

<SCRIPT LANGUAGE="VBScript">
<!--
Sub Window_OnLoad()
ListBox1.AddItem "<%= Value1 %>"
ListBox1.AddItem "<%= Value2 %>"
ListBox1.AddItem "<%= Value3 %>"
End Sub
-->
</SCRIPT>

Note: If you use the HTML <SELECT> tag instead of an ActiveX control, the procedure is slightly more direct. Because a list box created with the <SELECT> tag is based on HTML code, you can use server-side scripting to generate the <OPTION> tags. Since the HTML is self-contained, you do not need to place any code inside a Window_OnLoad event.

Built-in Objects and Server-Side Components

If you have ever written client-side script, you have probably found yourself using built-in browser objects such as document, form, and window. These objects are provided as part of the browser's object model, and make interaction with the browser much more manageable. Likewise, ASP defines its own object model.

The Response object mentioned earlier is one of ASP's built-in objects, of which there are currently six: Server, Application, Session, Request, Response, and ObjectContext. These objects greatly simplify the interaction between the server and the client. A description of each appears in the sidebar, "The Built-in ASP Objects."

In addition to built-in ASP objects, you can create and manipulate a variety of custom server-side components. By combining active scripting with server-side COM components, also known as server-side objects, you extend the functionality of ASP with powerful, easy-to-use packages. You can instantiate server-side components by using the CreateObject method of the Server object and passing it the ProgID of the component you wish to create. Once the component is instantiated, you can access any of its properties or methods. For example, the following script instantiates a server-side object with Server.CreateObject and stores a reference to it in the variable objAdRotator:

<% Set objAdRotator = Server.CreateObject("MSWC.AdRotator") %>

The Built-in ASP Objects

ASP provides built-in objects that make it easier for you to gather information sent with a browser request, to respond to the browser, and to store information about a particular user.

Server Object

You use the Server object to access methods and properties on the server. The most frequently used method is the one that creates an instance of a COM component (Server.CreateObject). Other methods apply URL or HTML encoding to strings, map virtual paths to physical paths, and set the time-out period for a script.

Application Object

You use the Application object to store global application settings and to share information among all users of a given ASP application.

Session Object

You use the Session object to store information needed for a particular user session. Variables stored in the Session object are not discarded when the user jumps between pages in the application; instead, these variables persist for the entire time the user is accessing pages in an application. You can also use Session methods to explicitly end a session and to set the time-out period for an idle session.

Request Object

You use the Request object to gain access to any information that is passed with an HTTP request. This includes name/value pairs passed from an HTML form using either the POST method or the GET method, cookies, and client certificates. The Request object also gives you access to binary data sent to the server, such as file uploads.

Response Object

You use the Response object to control the information you send back to a user. This includes sending information to the browser, redirecting the browser to another URL, or setting cookie values.

ObjectContext Object

You use the ObjectContext object to either commit or abort a transaction initiated by a script in an ASP page. For more information, see "Data Access and Transactions" in this book. You can also use this object to access the other built-in ASP objects from within a component.

To get you started, the default ASP installation includes several task-oriented components, including the MyInfo, Ad Rotator, and Browser Capabilities components. When you install the Microsoft® Data Access Components (MDAC), included with IIS 5.0, you can also use ADO to access information stored in a SQL Server, Oracle, Microsoft® Access, or other database.

Why Components?

You could spend a lot of time writing scripts in ASP pages that emulate component functionality, but there are several reasons not to do so:

  • Script is much slower than a compiled object and will be less likely to scale to large numbers of users.

  • Script doesn't separate presentation from functionality. Undifferentiated script scatters business logic throughout the application, making it hard to find bugs and increasing the cost of maintenance.

  • Components are inherently reusable; scripts are not. Components may also be used by other applications, such as those built in Visual Basic or C++.

Therefore, your development motto should be: The less script, the better. If you are serious about performance and application scalability, you should use components to perform the bulk of your business logic.

So, what makes a good server-side object? Generally, anything that expands the functionality of the server and scripting language is a candidate for a server-side object. Some server-side objects generate HTML that cannot be easily generated by ASP itself. Others perform server functions, like accessing the registry, sending mail, or administering a resource.

A compact memory footprint, computational speed, and multiuser re-entrancy are high priorities for server-side objects. Component stability is key, too. Memory leaks affect other applications on the server, and badly behaved components can cause IIS 5.0 to crash.

Because ASP component objects exist on the server, they should never be written to rely on user-interface elements, like dialog boxes or pop-ups. Configure components to send errors to the Event Log of Windows 2000 Server, or return detailed error information in the Err object, so problems can be reported to the user with script.

Reuse, Buy, or Build

When it comes time to procure components for your application, reuse the objects you already have if possible. If none of your prebuilt objects will do, consider buying third-party components from a reputable vendor.

If you can't locate a prebuilt component, you will have to build one yourself. Server-side components can be built using any development tool that supports the creation of COM Automation servers, such as Visual Basic, Microsoft® Visual J++®, or Microsoft® Visual C++®. As with scripts, choose the component language that suits your needs. Microsoft® Visual Basic® 6.0 creates "Apartment" threaded components that can be used on a page-by-page basis. If computational speed and multiuser re-entrancy are important to your application, you should use Visual C or Visual C++ with Active Template Library (ATL) 2.0 to develop your component. Visual C and Visual C++ can create "Both" threaded components that are suitable for use in the application and session scope. (For more information about threading considerations, see "Selecting Object Scope" later in this chapter.)

For more information about creating a server-side component, see the "ASP Tutorial" topics in the "Active Server Pages Guide" section of the IIS 5.0 online product documentation.

Building Windows Script Components

As you develop Web applications, especially when using ASP, you will find that your scripts tend to grow beyond your original intentions. This is a natural evolution of the middle tier that often occurs in the planning and development of an n-tier Web application. You may also decide to develop COM components in order to handle middle-tier logic for your applications.

ASP supports Microsoft® Windows® Script Components, a technology that provides a way to develop COM components by using scripting languages, such as VBScript, Microsoft® JScript® 2.0, JavaScript 1.1, PERLScript, and other languages compatible with the ECMA 262 language specification. Windows Script Components, which you can use as you would other COM components, provide many benefits in that they:

  • Allow you to encapsulate scripted tasks as reusable COM components.

  • Enable you to easily develop prototype COM components to be converted using a suitable programming language, after you are satisfied with your component's design and functionality.

  • Provide access to a wide range of system services, just as other COM components do.

  • Are small and efficient.

  • Are easy to create, maintain, and deploy.

  • Are supported by Component Services, a run-time environment for COM components.

Windows Script Component technology consists of:

  • The script component run time (Scrobj.dll).

  • Interface handlers, which extend the run time. These are compiled components that implement specific COM interfaces. When you install the script component run time, you receive the Automation interface handler, which allows you to call your script component from an .asp file.

  • Your script component file (an .sct file), in which you specify which interface handler you want to use, and which methods can be called from an .asp file in order to accomplish the intended functionality.

ASP Applications

So far, this chapter has presented several examples of scripts in ASP pages that create HTML on the fly. Stand-alone pages, however, are only the beginning of what you can do with ASP. This section discusses how to use the ASP Session and Application objects to create a coherent application out of an independent series of ASP files. It also walks you through the different elements of an ASP application, and discusses how to configure it using Internet Services Manager.

ASP Session Management

Since HTTP is a stateless protocol, the Web server retains no memory of past actions and treats each browser request as a one-time event. This statelessness makes it difficult for Web developers to create the level of application interactivity that most users expect. Although persistent application state can be maintained in a database or stored in files, such solutions are often difficult to implement, and involve a hefty memory overhead and performance penalty. ASP surmounts these problems by providing its own session management.

Using the ASP Session object, one of the ASP built-in objects, developers can store any data they wish, including references to objects they have instantiated. The Session object is analogous to an associative array, into which values may be stored and retrieved by using a string (or keyword) as the index.

For example, the following instruction assigns "John Doe" to the MyName entry in the array:

<% Session("MyName") = "John Doe" %>

The value "John Doe" is retrieved from the Session object by using the MyName keyword, as shown here:

My name is: <%= Session("MyName") %>

The Session object exists in memory on the server and maintains its state for the duration of the user's Web session. You can disable this object on a page-by-page basis using the declarative <%@ EnableSessionState=False %>. Disabling the Session object does not affect values that have previously been stored in this object. However, it may speed up the processing of pages that don't use the Session object. Furthermore, multiple ASP pages in an HTML frameset may be serialized (run in sequential order) if they use session state. Disabling the Session object in child frames that don't require it will allow the frame requests to execute concurrently.

The Application object is used in a similar fashion to store values that persist for the lifetime of the ASP application; it is also used to share values across user sessions. The values stored in the Session and Application objects persist between page requests, and can be retrieved at any point using the keyword that they were assigned.

ASP uses HTTP cookies to identify user sessions with unique session keys. Once an ASP session begins, ASP responds to a user's request with a Set-CookieHTTP header. From that point on, each browser request is identified by the session ID cookie.

Web Clusters and ASPSessionState

ASP session information is stored in memory on the Web server, which creates a challenge for sites using ASP in a Web cluster environment. Web clusters balance the load of user requests among a number of Web servers. In order to use ASP Session management, the same Web server must handle all requests from a user for the life of the session, a prerequisite that most load-balancing schemes cannot guarantee.

Several options are available if you want to use ASP in a Web cluster:

  • Write your own session management logic to replace that provided by ASP. Keep your session state in a centralized place, such as a database.

  • Use a third-party solution. For example, Cisco Systems' LocalDirector hardware load balancer can ensure that the same client gets the same server for multiple connections.

  • Load balance new requests but, once a session begins, make sure that all subsequent requests return to the same server during the life of the session. This technique is called ASP session-aware load balancing.

ASP Session-Aware Load Balancing

In this scenario, all new requests continue to use the existing load-balancing mechanisms, such as round-robin Domain Name System (DNS), for distributing requests to a site's published URL. Then, during the Session_OnStart event, the script in an ASP page uses the Response object to redirect the browser to the application's start page by using the local computer's own Internet Protocol (IP) address or unique name, as follows:

Response.Redirect("https://w10.microsoft.com/Webapp/firstpage.asp")

Finally, to ensure the browser will confine its requests to the same Web farm server, all application links must be relative URLs. Relative URLs only specify path information relative to the current location, for example:

<A HREF="MyAsp.Asp">...</A>
<FORM METHOD=POST ACTION="./SubDir/FormAction.asp">...</FORM>

When one of these links is selected, the browser will construct the full URL path using the current address (specified in the redirect) and the relative URL, thus enabling it to locate the computer hosting the user session.

This technique may not be appropriate for all Web applications. Also, if users save URLs on their browsers using favorites or bookmarks, they will return to a specific computer, which can defeat the purpose of proper load balancing.

Here's an example of an ASP session ID cookie:

Set-Cookie: ASPSESSIONIDGGGGGJZB=EFENHLNDIIEHJGJOAGICNPEK; path=/

This is different from earlier versions of ASP, which would set the cookie path to that of the ASP application's virtual directory. In IIS 5.0, only one cookie is sufficient for all applications on the server. Once the cookie is received, every browser request includes the same HTTP cookie header:

Cookie: ASPSESSIONIDGGGGGJZB=EFENHLNDIIEHJGJOAGICNPEK

ASP uses this value to retrieve the correct Session object for the client connection. All requests to the application directory include the session ID cookie, even those for static HTML content in subdirectories of the ASP application. In this example, the cookie does not specify an expiration time, so it is only valid as long as there is an open client session. The cookie expires when the user closes the browser.

Note: If a user chooses not to accept the ASP cookie (or if the browser fails to return it in subsequent requests), the application will not be able to map the browser request to an existing Session object and will create a new one.

It is possible for several browser instances to share the same cookie (which means that more than one browser window can be accessing different sections of your application at the same time), but make modifications to the same Session object. This is especially bad for applications that make heavy use of session state, and is another reason to use session state only when necessary.

ASP session IDs are mapped to the in-memory Session object on the server. This works well when only one server is managing the user session. When using ASP in a Web cluster, however, more than one server may be handling the user's request. For more information about how to manage session IDs in a Web cluster, see the sidebar, "Web Clusters and ASP Session State" earlier in the chapter.

ASP Session IDs Are Not Unique

Applications that require a unique user identifier should not use the ASP session ID, which is unique only for the life of the current application. If the application restarts, the server may conceivably reassign the same session ID to another user. In a multiple-server environment, like a Web cluster, the likelihood of duplicate IDs increases. Consequently, it is not advisable to use an ASP-issued session ID as a unique key for tables, or for any persistent user identity.

Instead of using the ASP session ID, you must use a separate mechanism designed to create unique numbers across multiple servers and sessions. Component Services includes the component TakeANumber, which can be used to generate unique sequential numbers for user identification. (For more information about this component, see the sidebar "Take A Number: A Component Services Example.")

Take A Number: A Component Services Example

Component Services includes the TakeANumber component designed to produce sequential numbers. Because the number is incremented as part of a transaction, you are guaranteed a unique identifier that works across sessions and across servers.

The TakeANumber component is installed with Component Services, but requires a little preparation to use. You first need to define a SQL Server table in order to store the current number. This table must be named "TakeANumber" and contain two columns named "NextNumber" (integer type) and "PropertyGroupName" (string type). The PropertyGroupName column identifies which counter you are using—more than a single counter can be stored in the table. Once your table is ready, you need to enter the first number of the series. The following SQL statement will accomplish this:

INSERT INTO TakeANumber VALUES (1234, 'MyProp')

Finally, create a File Data Source Name (DSN) so that the component can connect to the table you just created. You can create a File DSN with the Data Sources (ODBC) application (in Administrative Tools) in Control Panel. For step-by-step instructions, see "Data Access and Transactions" in this book.

Now you're ready to use the component. The following instructions retrieve the next number from the MyProp series:

<%@ LANGUAGE=VBScript EnableSessionState=False %>
<HTML>
<HEAD><TITLE>Take A Number</TITLE></HEAD>
<BODY BGCOLOR=#FFFFFF>
<% Set tn = Server.CreateObject("MTS_TakeANumber.TakeANumber") %>
Next Number: <%= tn.GetANumber ("TakeANumber.dsn", "MyProp") %>
</BODY>
</HTML>

As long as each server in your site connects to the same TakeANumber database, you are guaranteed a unique identifier across servers.

ASP Session ID and Session Security

The cookie approach to session management could become a potential security problem. If an intruder were able to capture or guess the session ID cookie in use by an active session, he or she could submit valid HTTP requests that included this cookie. In this manner, an intruder could hijack, or steal, a user's active session. For example, if a user had supplied valid credit card information, and a script in an ASP page stored this information in the Session object, an intruder who managed to hijack the session could make purchases using the stolen session. For this reason, the following built-in security measures are taken when generating ASP session cookies:

  • Session ID values are 32-bit long integers.

  • Each time the Web server is restarted, a random session ID starting value is selected.

  • For each new ASP session that is created, the session ID value is incremented.

  • The 32-bit session ID is mixed with random data and encrypted to generate a 16­character cookie string. Later, when a cookie is received, the session ID is decrypted from the 16-character cookie string.

  • The encryption key is randomly selected each time the Web server is restarted.

ASP session ID values are selected from a huge range and are encrypted, making it difficult to capture a valid cookie. In addition, guessing a valid cookie once does not make it easy to guess another valid cookie.

If the complexity of the ASP cookie generation algorithm does not meet the security requirements of your site, user authentication and client certificates can be used in conjunction with session management to provide even more security to your Web applications. For more information, see "Security" in this book.

Building an ASP Application

In its simplest form, an ASP application consists of all the HTML and script files stored within an application boundary. Before any sessions are created, the application initializes, instantiates application-scope components, and imports type-library declarations. From that point on, each connected user has a separate and distinct session, with its own values and component instances. The rest of this section explains how application and session management is accomplished.

Application Boundaries

An ASP-based application consists of all the files in its root virtual directory and in any subdirectories. An application defines a namespace (also called the application root), that begins at the root directory and includes all files, directories, and virtual directories contained within—except for those that are application roots themselves or ancestors of another application root. For example, if a virtual directory "Applications," and its subdirectory "Isolated Applications," are both application roots, then URLs that contain only "/Application" are part of one application, and URLs that contain "/Application/Isolated Application" are part of the other. Figure 6.5 illustrates how this looks in Internet Services Manager.

Bb742411.iischp6_iis0605(en-us,TechNet.10).gif

Figure 6.5 Application Roots and Virtual Directories

In Figure 6.5, the virtual directories—ASPs and ISAPIs—are contained within the Applications namespace. The Isolated ASPs and Isolated ISAPIs virtual directories are part of the other application.

Application developers can enforce a logical division between applications with separate application roots. Any Application and Session variables created in an application's namespace are segregated from the Application and Session variables of other applications on the server. There is currently no way to view values from other applications.

Global.asa

Global.asa is used to store information used globally by the application; it does not generate content that is displayed to users. Global.asa must be stored in the starting point (normally the root directory) of the application. An application can have only one Global.asa file.

Global.asa files contain only the following: Application events, Session events, object declarations, and type library declarations. If you include text that is not enclosed by <SCRIPT> tags, or define an object that does not have session or application scope, the server returns an error. The server ignores tagged script that the application or session events do not use, as well as any HTML in the file.

Application and Session Events

Every application has two events associated with it: Application_OnStart and Application_OnEnd. The script for these events is defined with server-side <SCRIPT> tags within Global.asa. Event script can be written in any language supported by the server. Application_OnStart is called once for each application, when the first client makes a request for a page within the application boundaries. The Application_OnStart event procedure is a good place to set global state variables and to create any objects that will be used by all users of the application.

After the Application_OnStart event, and for each subsequent new session, the Session_OnStart event occurs. An ASP application should use the Session_OnStart event to perform any required session initialization tasks. At this point, a Session object and a Request object exist. The Session object includes a unique session ID; the Request object includes fully parsed collections of values passed by the browser, as well as the server environment variables.

The Session_OnStart event procedure is a good place to redirect to the start page of your application. If you don't redirect, the application begins execution with the document requested in the URL, or it will begin with one of the default documents configured in Internet Services Manager for the application's virtual root. Redirection allows you to control where your application will go first.

Since the Response.Write method is not available during the processing of event procedures, you won't immediately be able to report errors if they occur. However, if you want to notify the user of any problems, you can save the error text in the Session or Application object (depending on which event handler caused the error) and report it on the first page that is loaded thereafter.

A session ends either when it times out or when the Session.Abandon method is called. When this happens, the Session_OnEnd event procedure is called, giving you the chance to destroy any object references, and perform any other session cleanup. Of the server built-in objects, only the Application, Session and Server objects are available to the OnEnd event handlers. Additionally, you can't use the MapPath or CreateObject methods of the Server object during the Session_OnEnd event.

Ending the ASP Session

Unless you have provided a means for explicitly logging off, there is no way to determine if the user is still actively connected to your application. HTTP is a stateless protocol, and doesn't keep track of user connections.

For this reason, ASP provides a mechanism to close a session when a specified time-out period expires. If a user begins a session but stops making requests to the Web application, ASP automatically triggers the Session_OnEnd event. The time-out period defaults to 20 minutes, but can be adjusted by setting the Timeout property of the Session object. You can also change the default value.

To change the default value

  1. Right-click the application's virtual directory in Internet Services Manager, then click Properties.

  2. Click the Configuration button, and select the AppOptions tab.

  3. Type a value in the ASP Script time-out box.

For applications that cache a database connection or consume a lot of server resources, the session time-out period may represent a time that other users cannot access server resources. If your application falls into this category, you should consider letting the user end the session when finished. You can do this by simply providing a Log Out button. When the button is clicked, the application calls the Session.Abandon method, which immediately triggers the Session_OnEnd event.

This session-time-out characteristic of Web applications is equally troublesome to applications that rely on resources requiring user authentication. If the user ignores a running application for too long, the application will end the session and log off any connections it has established. If the user makes another request, the application may not function as expected.

You can avoid this time-out problem with a little planning. One popular method of detecting session time-outs is by storing the Session object's SessionID property as a Session variable. Then, each time the user tries to navigate to a page requiring a valid connection, you check the current SessionID against the ID stored in the Session object. If they do not match (or if the Session variable is empty), you have detected a session time­out, and you can take appropriate action.

Importing Type Library Constants with Global.asa

A COM component typically includes a list of named constants as part of its type library, along with other information about the component that enables it to be automated by other applications.

If your Web application uses a COM component that has declared enumerated data types in its type library, you can import them into your application space in Global.asa. Doing so makes it possible to refer to the data types declared in the type library by name from any script within the application boundary. The syntax for importing a type library is as follows:

<!--METADATA TYPE="TypeLib"
NAME="
typelibraryname
"
FILE="
file
"
UUID="
typelibraryuuid
"
VERSION="
majorversionnumber.minorversionnumber
"
-->

You're required to specify either the File or the UUID parameter, but your application will be more portable if you specify both. The Name parameter may come in handy if you have to disambiguate, or need to establish a single interpretation for enumerated constants that have the same name but are from different type libraries. The following example imports the constants declared in the ADO type library:

<!--METADATA TYPE="TypeLib" NAME="ADO"
FILE="C:\Program Files\Common Files\System\ado\msado15.dll"
UUID="00000200-0000-0010-8000-00AA006D2EA4" VERSION="2.0"
-->
Declaring Objects in Global.asa

You can declare objects in Global.asa with session or application scope by using the extended <OBJECT> tag. This tag is placed outside of any <SCRIPT> tags. In addition to setting ID and ClassID parameters, you must set SCOPE (either "Session" or "Application") and RUNAT=SERVER. This example creates an instance of the Page Counter component:

<OBJECT ID=GlobalPageCounter SCOPE=Application RUNAT=Server
CLASSID="clsid:4B0BAE86-567A-11D0-9607-444553540000">
</OBJECT>

Having defined the GlobalPageCounter object in Global.asa, you can use it from any ASP file in your application without first retrieving it from the Application or Session object, or specifically calling Server.CreateObject first.

There have been <%= GlobalPageCounter.Hits("default") %> hits.

Note: The objects declared in Global.asa with <OBJECT> tags are not fully instantiated until the server processes a script that references that object. This saves resources by creating objects only as necessary.

Selecting Object Scope

Components can exist and operate within the scope of an application or session, or they can be created and destroyed on a page-by-page basis.

Objects stored in the Application object are available to all users of the application. The Application object is a suitable place to store objects that have no user affinity, such as a page counter. Normally, values and objects with application scope are created when the application begins, and are accessed in a read-only fashion by users of the application. When you make changes to Application objectvalues, you should use the Lock and Unlock methods to prevent users from accessing data while it is changing.

In general, very few objects should be given application scope. They should support the "Both" threading model, as "Apartment" threaded objects force the Application object into a single thread of execution, and free-threaded components tend to be slower overall. COM components should not be given application scope, because they would be created when the application starts up, even though they might not yet be needed.

Because the Session object is designed to store information about the current user's session, components stored in this object can exist as long as the user's session is active. Since each value and object stored in the Session object increases the server's memory requirements for each user of the application, you should store values for only as long as they are necessary, and free them when they are no longer needed.

Page-scope objects are created each time the page is requested. They can use any threading model, but only the "Apartment" and "Both" models are recommended for scalability purposes.

Process Isolation and Crash Recovery

Besides adding the power of transactions to ordinary Web pages, the combination of IIS 5.0 and Component Services produces another powerful result: process isolation.

Applications and Processes

Extending a Web server has always involved tradeoffs between performance and safety. In early versions of IIS, all ISAPI applications (including ASP) shared the resources and memory of the server process. Although this design increased performance, unstable components could cause the server to crash—not an acceptable behavior for mission-critical applications like IIS. To make matters worse, in-process components couldn't be unloaded unless the server was restarted—which meant that reloading existing components required all sites that shared the same server to restart, whether they were directly affected by the upgrade or not.

Running isolated processes in IIS 5.0 combines the performance of the ISAPI/ASP model with the safety of the CGI model. Segregating applications into their own process space protects the entire Web server from unexpected application failures. This is the main idea behind process isolation.

Bb742411.iischp6_iis0606(en-us,TechNet.10).gif

Figure 6.6 Process Flow for IIS 5.0 Applications Running In and Out of Process

Out-of-process applications run within their own processes, separate from the rest of IIS 5.0. When an ASP file is requested by the browser, the Web server first assigns a thread from the pool to handle the request. Then, IIS 5.0 determines how to handle the requested file according to its internal Multipurpose Internet Mail Extensions (MIME) and script mappings. For an ASP file, the ISAPI filter for ASP is invoked.

Next, the Web Application Manager (WAM) determines whether the file is part of an application marked to run in a separate memory space. If so, the request will be handled by an external Component Services process. Component Services, which is a run-time environment for COM components, hosts a WAM proxy object for each isolated process. Otherwise, the request is completed in process with IIS 5.0. WAM hosts all Web applications, whether they're in-process applications or not, and also hosts controls that load and interface with ISAPI DLLs. WAM itself is a COM component, and can be run in the IIS 5.0 process or isolated in a separate process. There are three main reasons for running an application as an isolated process: component development, fault isolation, and Web site safety.

Component Development

Rather than taking down the entire server to update a single component, process isolation makes it possible to stop and restart just a single application. You can use Internet Services Manager to add an updated component to an application.

To add an updated component

  1. Right-click the application root directory, then click Properties.

  2. Select the Virtual Directory tab; click Unload.

Once the old component has been replaced, IIS 5.0 will restart the application when it receives the first request.

Fault Isolation

Process isolation limits the effects of a crash to the single application that caused it. In addition to protecting your server from the crash, the application can be configured to restart automatically. In the case of a fatal error, the application's process is automatically terminated. Because the application is running in the Component Services system process, all transactions in progress are aborted. The Windows® Event Log stores a record of the event, and Component Services restarts the application. The only ones affected by the failure are clients with outstanding requests to that specific application.

Web Site Safety

The process isolation that Component Services provides makes it possible to host untested or unstable applications without risking the stability of the entire server. Now the Web server can gracefully tolerate failures with minimal disruption to clients of other applications. By separating unstable applications into their own memory space, you prevent a single application error from bringing down the rest of your site.

Configuring an Isolated Process

By default, an IIS 5.0 application runs in the IIS 5.0 process. If you decide to run your application as a separate process, you can set this up with Internet Services Manager.

To run an application as a separate process

  1. Right-click the application root directory, then click Properties.

  2. Select the Virtual Directory tab. (You must first create an application for your virtual root, if you haven't already done so. For more information, see "ASP Best Practices" in this book.)

  3. Select the Run in separate memory space check box, and click OK.

Internet Services Manager automatically creates a Component Services package for your application. For example, if you were to configure the IIS 5.0 Help application to run in an isolated process, a new Component Services package would be created, named IIS-{Default Web Site//Root/IISHELP/}. Transactional components to be run within the context of this new process can be installed into this Component Services package by using Internet Services Manager.

Out-of-Process Components

Out-of-process components are COM components implemented as executables; these start as a separate process on the same computer as the client application. Out-of-process components, also called "local servers," are different from out-of-process applications (known, once again, as isolated processes). When you start an out-of-process component on the Web server, IIS 5.0 becomes the client application.

COM objects are implemented inside a server. Most of the time, the COM server is implemented as a DLL that executes in the same process as the client application. Sometimes the COM server is implemented as an executable that runs in a separate process from the client. For instance, when you create an instance of an Active Document server such as Microsoft® Word, you actually start a copy of the server application. When you instantiate such an object from ASP, you create a new process on the Web server. Because the Active Document server is an executable rather than a DLL, it cannot be loaded into the IIS 5.0 process.

When you use Server.CreateObject in an ASP page to start an out-of-process component, and IIS 5.0 has not been configured to allow out-of-process components, it will return the following error:

Server object error 'ASP 0196'
Cannot launch out of process component
/myvroot/launch_exe.asp, line 16

This is the result of an ASP safety mechanism that prevents executables (but not DLLs) from being started directly from ASP.

There are several reasons for this safeguard. Not all executables are safe to use on the server, and some may pose security risks. Also, because in-process component DLLs are faster, more secure, and can be hosted by Component Services, they are much better suited for server-side use.

Furthermore, out-of-process components often create individual server processes for each object instance, reducing their performance to that of CGI applications. They simply do not scale as well as component DLLs that run in process with IIS 5.0 or Component Services. If performance and scalability are priorities for your site, using out-of-process components is strongly discouraged. On the other hand, intranet sites that receive moderate to low traffic might be able to use an out-of-process component without adversely affecting the site's overall performance.

Using the Metabase

The IIS 5.0 metabase stores configuration settings for IIS 5.0. It performs some of the same functions as the system registry, but uses Microsoft® Active Directory Service Interfaces" (ADSI) to administer this high-use storage facility.

If you want to enable the use of out-of-process components, you must set the IIS 5.0 metabase property AspAllowOutOfProcComponents to TRUE. This setting is accessible from either the IIsWebService or IIsWebVirtualDir Admin objects.

If you set the AspAllowOutOfProcComponents property to TRUE on the IIsWebService object, all in-process applications will be able to start executables from script. An in-process application is a virtual directory that has been marked as an application starting point, but which does not have the Run in separate memory space option selected in Internet Services Manager.

If you set the AspAllowOutOfProcComponents property to TRUE on the IIsWebVirtualDir object, and it contains an application that has been marked to Run in separate memory space as an isolated process, only the affected application may start executables from script. If the application is set to run in process, the setting will have no effect.

You must have adequate permissions to modify the IIS 5.0 metabase. If you try to modify it without proper permissions, you might encounter an error message.

The following ASP code demonstrates the steps required to set the AspAllowOutOfProcComponents parameter on the IIsWebService Admin object. You will need to restart the Web server service (by stopping and starting the IIS Admin Service in the Service Control Manager) after making this change.

<%
'Get the IIsWebService Admin object.
Set oWebService = GetObject("IIS://LocalHost/W3svc")
'Enable AspAllowOutOfProcComponents.
oWebService.Put "AspAllowOutOfProcComponents", True
'Save the changed value to the metabase.
oWebService.SetInfo
%>
Security Considerations

Out-of-process applications and components, including ISAPI extensions, are not able to access IIS 5.0 metabase properties using the built-in administration objects of IIS 5.0. This restriction is designed to prevent changes to the metabase from unauthorized sources. If you want to allow out-of-process applications to access the metabase, change the identity of the out-of-process Component Services package from the interactive user to a specific user account, and give that account access to the metabase. This is also somewhat risky, but the risk is limited to a single application package.

A Note About Application Testing

Performance testing, especially using multiuser scenarios, is a critical part of Web application design and development, and needs to be considered as a part of the overall planning of the application. Performance testing is much more critical for server applications than for desktop applications, because managing simultaneous users places higher demands on the application.

Remember that using the Session object to store values increases the memory requirements of your application, and therefore decreases the number of concurrent users an application can support. When you build your application, the following three factors determine how much server memory your application will consume:

  • Concurrent Sessions The number of sessions that exist at any given moment is cumulative over the lifetime of the Session object. So, if you have a Session.Timeout value of 20 minutes, your concurrent sessions will be equal to the number of connections you expect to service over a 20-minute period.

  • Variables and Objects Per Session How many objects or variables are you storing? A few session settings are fine. Long lists of session-scope variables (especially if they are components) should be avoided. As the number of variables increases, the time it takes to retrieve them also increases.

  • Size of Each Variable or Object Stored Are you storing lengthy strings or large component objects? Store them for as long as necessary, and then free them—replace long strings with empty string and objects with Nothing (or Null).

Design Patterns for Web Applications

Web application developers may find that the model used by applications on the Web conflicts with the conceptual model they have developed for other platforms. For instance, unless you use client-side ActiveX controls or Java applets, your user interface is limited to an HTML description of the form inputs or a list of links to other areas of your site. Despite the availability of DHTML and ASP, most Web-based applications will never consist of more than an interconnected series of dynamically generated static states. Click a button and something happens. Click a link and a new page appears.

This final section of the chapter explores effective Web-application design, and covers a variety of techniques that you can use to enhance your applications.

Factoring Your Application

A Web application is a hierarchy of interdependent pages, each one representing a distinct stage of the application. Web applications map an ordered sequence of input events to a corresponding sequence of output events, using a finite number of elements.

Most Web applications make use of (or should make use of) the following: content, hyperlinks, forms, components, server-side actions, redirection, and loops.

  • Content Content—information presented in the form of text, graphics, or even music or video files—is the most common element in most Web applications. Content can be presented on static HTML pages or be dynamically generated by an ASP page. Content elements usually have one well-defined entry point with links to other pages.

  • Hyperlinks The primary purpose of hyperlinks is to facilitate movement to other pages or parts of the same page. Site maps, toolbars, HREFs, anchors, form buttons, and navigational controls are all examples of hyperlinks. These elements often appear in their own browser frame and control the navigation throughout the site. They can also appear as a separate index page or table of contents, which is replaced once a link is selected. Hyperlinks represent a user choice, very much like a menu option does in a stand-alone application.

  • Forms Forms are used to collect information from the user. Depending on how a form is designed, its outcome might change once it is submitted, as a function of user input. Forms can be chained together consecutively to create "Form wizards." Client-side or server-side actions usually process these forms.

  • Components A component is any code-level unit providing a relatively independent piece of logic that can be used either separately or in combination with other components throughout the application. The level of granularity and scope of responsibility distinguishes components from other application elements. Components can generate content or provide server-side logic.

  • Action Action in this case means how an application responds to user action. When the user submits a form, for example, the application processes the form. Action pages perform business logic, such as data entry, calculations, or administrative functions. Action (processing) can occur both on the server and on the client, and can be used to generate content such as confirmation or error messages.

  • Redirection A redirection page or script can perform logic to branch the flow of the application. Although it doesn't actually perform a redirect, a page containing a frameset can also be considered a redirection page, since it causes other pages to load. Redirection is usually more adaptable and flexible if implemented as a separate ASP page.

  • Loops Loop pages are ASP pages that refer to themselves in order to present different content, depending on user input. For example, a page might select content based on which browser is being used.

You can create a huge variety of complex applications using the elements just described in this list.

Using Forms for Input

The standard method of interacting with Web pages is the HTML form. Forms can contain any number of inputs, including text entry, command buttons, "radio" selection controls, and check boxes. Forms can be as simple as a single button, or they can contain a complex layout of client-side controls. A Web page might have several distinct form structures, each with its own processing logic to be performed when the form is submitted.

Suppose you want your users to log on to your Web application by providing a user name and password. To accomplish this task, you create a simple HTML page with two text fields and a Submit button in an HTML form.

The HTML for the form might look like the following example:

<FORM ACTION="./Logon.asp" METHOD="GET">
Your name: <INPUT TYPE="TEXT" NAME="User">
Your password: <INPUT TYPE="PASSWORD" NAME="Pwd">
<INPUT TYPE="SUBMIT" VALUE="Log On">
</FORM>

When this form is submitted, the values that the user enters are collected and sent to the server as a request. These values are passed as name/value pairs to the page referenced in the ACTION attribute of the <FORM> tag. They are appended to the requested URL after a question mark (?) and are separated by ampersands (&). If the user had entered "John Doe" as the user name, and "Amnesia" as the password, the following URL would be requested when the Submit button was clicked.

https://myServer/test/Logon.asp?User=John+Doe&Pwd=Amnesia

Any information appended to the URL like this is said to be URL encoded. URL encoding replaces reserved characters, like spaces and ampersands, with URL-neutral characters. The space in "John Doe" is replaced by a plus character (+). Pluses, equal signs, commas, percent symbols, and question marks also need to be encoded. These and other special characters can be represented in the format **%**hh, where hh is the hexadecimal value of the ASCII code for that character.

ASP provides a server-side method, Server.UrlEncode, to perform encoding (and decoding) for your parameterized URLs. Whenever you create hyperlinks that contain name/value pairs, you should always encode them to avoid invalid URL syntax. Unfortunately, you cannot use this ASP method on the client, since it is a method of a server-side object. You could write a client-side script function to do this, but it's easier to let the form processing logic of the browser do it for you.

The Difference between GET and POST

When the user enters information in a form and clicks Submit, there are two ways the information can be sent from the browser to the server: in the URL, or within the body of the HTTP request.

The GET method, which was used in the example earlier, appends name/value pairs to the URL. Unfortunately, the length of a URL is limited, so this method only works if there are only a few parameters. The URL could be truncated if the form uses a large number of parameters, or if the parameters contain large amounts of data. Also, parameters passed on the URL are visible in the address field of the browser—not the best place for a password to be displayed.

The alternative to the GET method is the POST method. This method packages the name/value pairs inside the body of the HTTP request, which makes for a cleaner URL and imposes no size limitations on the form's output. It is also more secure.

ASP makes it simple to retrieve name/value pairs. If the form's output is passed after the question mark (?) on the URL, as occurs when using the GET request method, the parameters can be retrieved using the Request.QueryString collection. Likewise, if the form is sent using the POST method, the form's output is parsed into the Request.Form collection. These collections let you address the form and URL parameters by name. For example, the value of the form variable User can be passed into a VBScript variable with one line of script:

<% UserName = Request.Form("User") %>

You don't need to specify the collection (Form or QueryString) in which you expect to find the User parameter. The following is an equally valid method of searching for the User parameter:

<% UserName = Request("User") %>

In the absence of a specific collection, the Request object will search all of its collections for a matching parameter. This is meant to be a programming convenience. However, the ASP Request object also contains collections for ServerVariables and ClientCertificates, which contain sensitive server and user authentication information. To avoid the possibility of "spoofed" values, which are values entered by the user in the URL, it is highly recommended that you explicitly use the collection name when searching for parameters from these collections.

The following script combines a form and an action (the script that processes the form) into a single page. By posting the form data back to the same ASP page that displays the form, server-side script can process the output of the form. This is perfectly valid, and for simple script is often more convenient than posting to a second ASP page.

<%@ LANGUAGE="VBScript" %>
<!-- FILE: logon.asp -->
<HTML>
<HEAD>
<TITLE>Authentication Form</TITLE>
</HEAD>
<BODY BGCOLOR=#FFFFFF>
<% If Request.Form("User") = "" Then %>
<P>Please enter your Name:
<FORM ACTION="./logon.asp" METHOD="POST">
Your name: <INPUT TYPE="TEXT" NAME="User">
Your password: <INPUT TYPE="PASSWORD" NAME="Pwd">
<INPUT TYPE="SUBMIT" VALUE="Log On">
</FORM>
<% Else 'User verification and logon code goes here %>
Welcome <%= Request.Form("User") %>!
<% End If %>
</BODY>
</HTML>

Note: If you use a separate ASP file to handle the processing of a form, the Request.Form collection will be emptied when you redirect to the new page. In order to retain the form values, you must copy them to the Session object from which they can be accessed on subsequent pages.

Although the sample authentication form shown here works, there's a good reason why you would not want to use it in practice. Logon information is sensitive and should be subject to rigorous protection from prying eyes. Although you can use the POST method to contain the user's password within the body of the HTTP response, it is still possible to intercept and read it.

For mission-critical applications, IIS 5.0 provides both secure authentication with integrated Windows authentication and Client Certificates, as well as data encryption with Secure Sockets Layer (SSL). For more information about authentication and encryption, see "Security" in this book.

Client-Side Form Validation

Forms require some sort of input. If the user hasn't entered any information or has entered a bad combination of information, it makes little sense to send the form back to the server. Forms that are submitted without preliminary data validation increase the server's load (and the user's frustration) unnecessarily. Validate information as much as possible when the form is submitted.

In fact, don't stop with the client tier. Just in case the client doesn't support client-side scripting, validate again as data is passed to the middle tier. Most importantly, use the data integrity and validation rules of your database to protect yourself against inadvertent mistakes in your own business logic. This defensive approach to data validation can save you frustration in the long run; you'll know that data is protected at all levels of your application.

To validate form input with client-side script, you need to declare an event-handler for the submit event of the form. If your form uses a SUBMIT input type, create an event handler using the name of the form followed by an underscore and the command: "_OnSubmit." To submit the form, return True from the validation routine. Return False to abort the submission and to return to the form.

If you prefer to use the BUTTON input type on your form, you'll need to define an event handler using the name of the button followed by an underscore and the command: "_OnClick." Instead of returning True to submit the form, however, you will need to call the Submit method of the Form object explicitly. This method may not be available on some browsers.

The following client-side script verifies that the form's user name and e-mail address fields (both required by the form) have been filled in. It then submits the form by returning True from the OnSubmit event-handler:

<SCRIPT LANGUAGE="VBScript">
<!--
Function FeedbackForm_OnSubmit()
'Disallow submit until the form fields have been validated.
FeedbackForm_OnSubmit = False
'Get a reference to the form.
Set theForm = Document.FeedbackForm
'First, check that UserName has been filled in.
If Trim(theForm.UserName.Value) = "" Then
MsgBox "Enter your name.", vbCritical, "Need Input"
theForm.UserName.Focus
Else
'Next, check for the e-mail name.
If Trim(theForm.UserEmail.Value) = "" Then
MsgBox "Enter your e-mail address.", vbCritical, "Need Input"
theForm.UserEmail.Focus
Else
'Continue with submission.
FeedbackForm_OnSubmit = True
End If
End If
End Function
--></SCRIPT>
<form name=FeedbackForm action=TeeFeedback.asp method=POST>
<B>Tell us how to get in touch with you:</B>
<PRE>
Name <INPUT type=TEXT name="UserName"> (Required)
E-mail <INPUT type=TEXT name="UserEmail"> (Required)
Tel <INPUT type=TEXT name="UserTel">
</PRE>
<INPUT type=SUBMIT value="Submit Comments"> 
<INPUT type=RESET value="Clear Form">
</FORM>
Hidden Form Fields

When you chain forms together to create a "Form wizard," the information entered on each form needs to be stored until the last form has been filled in. There are three ways to pass values between ASP files:

  • Accumulate information in the Session object.

  • Append information to the end of the URL and use QueryString to pass it.

  • Use hidden HTML form variables.

Sometimes the requirements of your application don't permit you to store form data in the Session object, even temporarily. This might be the case for a large-scale site with thousands of concurrent users, where memory is at a premium.

Passing values on the URL works for small amounts of information, but will be insufficient when the quantity of data is large.

So, although the amount of information passed between the client tier and the middle tier increases, hidden form fields make it possible to include previously entered or application­specific information as part of the current form submission. A hidden form field isn't displayed to the user, but is sent as a name/value pair when the form is submitted.

Note: You should avoid using hidden form fields to send back information that you are using for security or authentication purposes. Since they are available as text in the form body, these values can easily be "spoofed" by anyone who can view the HTML source. Even an unsophisticated intruder could develop a small routine to try many possible values, in an attempt to crash (or otherwise break) whatever server code is using the hidden value.

Redirection

Sometimes, the page being requested by the browser isn't the one you'd like to send. For example, suppose a user requests Oldpage.htm, which has been replaced by NewPage.asp. Standard HTML syntax provides a means whereby you can redirect (or divert) a request to another location. The syntax looks like this:

<HEAD><META HTTP-EQUIV="REFRESH" CONTENT="0;URL=NewPage.asp"></HEAD>

You can also use ASP to redirect a request to another page based on the logic of your application. The syntax is simple:

Response.Redirect "./NewPage.asp"

The Redirect method of the Response object operates by sending the "302 Object Moved" response header, plus the new location of the file, to the client. When it receives this response, the user's browser automatically requests the new page.

Because redirection depends on HTTP headers, which come at the beginning of the document, you can't redirect once text has been sent to the client. If you redirect in a server-side script after data has been sent to the client, the following error occurs:

Header Error
The HTTP headers are already written to the client browser. Any HTTP header modifications must be made before writing page content.

If you don't know at the beginning of the page whether you need to redirect, you can use the buffering capabilities of the Response object. If Response.Buffer is True, HTML output is collected in a buffer and sent all at once to the client. If at some point you need to redirect to another page, ASP automatically discards any existing output in the buffer when you call Response.Redirect. You may also use Response.Clear at any time to clear the buffer and start again.

The following example demonstrates this concept:

<% 'Begin buffering the HTML.
Response.Buffer = True %>
<HTML>
<BODY>
HTML text before potential redirect.
<%
On Error Resume Next
'Script generates an error here.
If Err.Number > 0 Then
Response.Clear
Response.Redirect "./error.asp"
End If
%>

Once you call Response.Redirect, the script ends and the redirection headers are sent immediately. Any code following the redirect will not be executed, although it is required for syntactical correctness.

You can use Response.End to end a response from the server immediately and to send the current output to the browser. Since the response ends at that point, any HTML that follows is not sent. The following script detects that the user has connected anonymously (the LOGON_USER server variable is empty) and forces a logon by returning a "401 Access Denied" message.

<%
strLogon = Request.ServerVariables("LOGON_USER")
If IsEmpty(strLogon) Or strLogon = "" Then
'Up to this point, no HTML has actually been sent.
Response.Status = "401 Access Denied"
Response.End
End If
%>
<HTML>
You are logged on as: <%= strLogon %>
</HTML>

A well-behaved browser will try this page a second time, with logon credentials. If you don't use Response.End, the script in the ASP page will execute twice, possibly leading to unexpected side effects (such as adding duplicate records to a database).

Client-Side Redirection

Redirection can also occur in client-side script. A client-side redirect shifts focus from the current page to another. It can occur as the page is loaded, or can be deferred until the user performs an action, such as clicking a button.

The browser's object model includes a Location object, which represents the URL of the content currently displayed in the browser window. The following example uses this object to reload a frame-dependent page inside its parent frame. The script (which appears at the top of each frame page) detects that the page has been loaded as the top frame, and changes the browser window location to the parent frame. (Note that this script can be placed in an include file, to avoid duplicating it on all your pages.)

<SCRIPT LANGUAGE=JavaScript>
<!--
if(top == self) {
var currURL = unescape(window.location.pathname);
var newURL = "parentFrame.asp?" + currURL;
var appVer = navigator.appVersion;
var NScp = (navigator.appName == 'Netscape') &&
((appVer.indexOf('3') != -1) ||
(appVer.indexOf('4') != -1));
var MSIE = (appVer.indexOf('MSIE 4') != -1);
if (NScp || MSIE)
location.replace(newURL);
else
location.href = newURL;
}
//-->
</SCRIPT>

This method requires that the parent frame accept the child frame location as a URL parameter, and that it deal with this parameter appropriately. The following script manages the last part of this redirection:

<%@ LANGUAGE=VBScript EnableSessionState=False %>
<%
frmSrc = Request.QueryString 'Get entire URL parameter.
If frmSrc = "" Then frmSrc = "homepage.htm"
%>
<FRAMESET rows="60,*" frameborder=0 framespacing=0>
<FRAME name="nav_fr" src="navbar.htm" scrolling=auto noresize>
<FRAME name="page_fr" src="<%=frmSrc%>" scrolling=auto>
</FRAMESET>
Redirecting During Session_OnStart

Redirection during the Session_OnStart event is possible, and sometimes required by your application (see the sidebar "Web Clusters and ASP Session State" earlier in this chapter). Here is an example:

Sub Session_OnStart
Response.Redirect "MyStartPage.asp"
End Sub

Not all of the Response object's methods are available during the Session_OnStart event. Most notably, you cannot use the Write method to display messages, since the client would never see them. However, you can redirect to an error message, if your application cannot successfully initialize a new user session.

Debugging Applications and Components

Debugging can be the most frustrating part of the Web application developer's job. Unlike desktop applications, which are self-contained, Web applications can be spread over several systems and frequently combine different programming languages and technologies. Although some application errors can be avoided with careful planning, others are unexpected side effects of complex component interactions. Tracking down these problems often requires the use of a variety of debugging and development tools.

This section contains debugging tips for the ASP environment. It also contains information about setting up a debugging environment for ISAPI extensions and server-side components.

For information about common error messages and error logging, see the IIS 5.0 online product documentation.

Script Debugging in Active Server Pages

Because ASP pages often contain a mixture of HTML, server-side script, and components, problems can be difficult to track down. There are basically three ways to deal with errors: avoidance, debugging, and script management.

Avoiding Common Mistakes

Studies show that developers tend to make the same types of mistakes no matter what programming language they are using. Rather than spending a lot of time tracking down bugs, it is always easier to avoid creating them in the first place. Here are some common scripting mistakes that lead to bugs, as well as ways to avoid them.

Misspelling Variables

Most scripting run-time errors can be attributed to misspelled variables. VBScript automatically declares unrecognized variables, which can lead to subtle errors in your code. To avoid this problem, use the Option Explicit statement as the first line of script on every page. The Option Explicit statement requires variables to be explicitly declared with a Dim statement.

JScript has no counterpart to the Option Explicit statement. Unrecognized variables are automatically declared when they are defined. Some ASP methods, such as Response.Write, produce script errors when used with an undeclared variable. If a variable is declared, but not defined, it will return as either "Undefined" or "NaN." (For more information, see the JScript documentation.)

Using an Object or Variable Out of Scope

A common problem is not understanding which objects are available in a given context. For example, you might attempt to set properties for a Session object in a client script, or to use the Internet Explorer object model in a script running on a different browser.

Make sure that the objects you reference are available in the current scope. Be aware that objects (such as ASP built-in objects) are not an inherent part of a language such as VBScript; instead, they are part of the environment in which a script is running.

Not Using the Web Server to View ASP Pages

To view ASP pages in the browser, you need to request them from the Web server by using HTTP syntax. If you try to view them directly from the file system, the browser will either try to download the file, or will display the script without executing it. Here are some other reasons for ASP pages not displaying properly: not having script (or execute) permissions set for the directory in which the page is stored, and not using the .asp extension when naming files.

Using the Scripting Language Inefficiently

Sometimes when developers are unfamiliar with a scripting language, they write code that fails to take advantage of the language's capabilities. Consequently, their code runs more slowly than correctly implemented script. Likewise, if they don't understand the language conventions, they might inadvertently make syntax errors, such as using the wrong type of quotation marks to enclose string literals. This is an easy mistake to make when switching between languages such as Visual Basic and SQL.

Always seek to familiarize yourself with the native functionality, operators, and conventions of the scripting language you are using.

Mixing Data Types

Because all ASP scripting variables are variants (meaning they can hold values of any type), you can easily assign inappropriate values to variables. For example, you could assign an object to a variable you intend to use for strings.

To help you remember what type of data a variable is supposed to contain, use a naming convention that will help you remember variable types. For a table of recommended variable names for VBScript, see "ASP Best Practices" in this book.

Misusing the Equal Sign

Visual Basic users expect the single "=" (equal sign) to evaluate the equality of the two operands. In JScript, however, the single "=" will assign the right-hand value to the left-hand operand. (JScript uses a double "=" to express equality.) Mistakenly using a single "=" in an expression will not only overwrite the left-hand value, it will cause the expression to evaluate to TRUE. If this happens in an If statement, the inner logic is executed; in a While statement, it creates an infinite loop.

Using Procedures Incorrectly

Not understanding when to use a function or procedure, or calling the incorrect function, is a common problem in any language. You can avoid using the incorrect arguments for functions, or passing arguments in the wrong order, by double-checking the function definition. Additionally, make sure that the function you are calling performs the task you want it to, check syntax for functions whenever using them, and avoid relying on default argument values.

Not Handling Errors

You should always check for unexpected user input, such as a string when prompted for a number, or a number value outside the bounds of what the program can use. Likewise, you must anticipate errors from within the program, and understand the meaning of values returned by the functions you use.

To handle internal program errors in VBScript, you must use the On Error Resume Next statement. Without this instruction, the script will abort as soon as the error is detected, which prevents you from quietly handling errors with script. Here are a few precautions to keep in mind when using the On Error Resume Next statement:

  • When debugging, it is often necessary to disable error handling, since it will not be apparent where errors are occurring. Re-enable it when you are finished debugging.

  • Because you are resuming program execution at the next statement whenever an error occurs, you should avoid using script that may produce an error as the logic portion of an If statement, or the test of a While loop. If these statements cause errors, the inner logic of the If statement will not be executed, and the While loop will never complete.

Making Off-by-One Errors with Collections

Not all collections in VBScript begin with element 1. Some collections—the ADO Fields collection, for example—start at element 0. When looping through a complete collection, it is often safest to use the VBScript methods LBound and UBound to specify the starting and ending array indexes, respectively. Note that JScript also defines these methods on its VBArray object.

Forgetting Ending Braces, Delimiters, and Statements

The longer the script becomes, the more likely that end braces, the ending statement of a code block, such as End If,or an ending code delimiter (such as %>) can be overlooked. The result is often confusing, and ambiguous script errors can occur. To avoid such problems, make a practice of typing the closing portion of a statement as soon as you type the opening portion.

Debugging ASP

Until the introduction of Microsoft® Script Debugger, debugging scripts in ASP pages often required lots of debugging output and an intimate knowledge of how the scripting language processed the code. Script Debugger makes this process much easier.

Microsoft Script Debugger

You can use Script Debugger to test scripts written in VBScript and JScript, as well as applications written in Java. You can also debug scripts in other languages that support host-independent debugging, such as REXX or PerlScript.

Using Script Debugger, you can:

  • View the source code of the script you are debugging.

  • Control execution line by line through the script.

  • View and alter variable and property values.

  • Set breakpoints and view the call stack.

  • Switch between threads of execution.

You can use Script Debugger to debug both client-side and server-side scripts. Debugging must be enabled for each ASP application that you want to debug.

To enable debugging

  1. In Internet Services Manager, right-click an application virtual directory and click Properties.

  2. On the Virtual Directory tab, click Configuration.

  3. On the App Debugging tab, select Enable ASP server-side script debugging and click OK.

To begin editing a document in Script Debugger, first open the Running Documents window (from the View menu) and double-click to open a document. Before you can open the script, it must be loaded locally in the browser, or in the ASP environment on the local Web server, if one is installed. Once the script is loaded, you can set breakpoints, and step through the application.

You can view and change the values of individual variables in the Command window when a script is running. To view a variable value, type a question mark (?) followed by the name of the variable, and press Enter. You can also evaluate simple numeric expressions using the "?" command form. To edit the value, type the name of the variable followed by an equal sign (=) and a new numeric or string value.

Since Script Debugger only works with scripts running on the local Web server, you cannot debug scripts running on remote Web servers. Because the script debugger interrupts script execution when it encounters an error, you won't want to enable script debugging on systems that are hosting applications critical to your business. For these systems, it is better to trace execution by using logs and output.

For more information about Script Debugger, see the IIS 5.0 online product documentation.

Debug Tracing

ASP debug tracing is limited to the following three methods:

  • Liberal use of debug Response or Write statements throughout the code.

  • Response.AppendToLog to write script values and events to the IIS 5.0 Server log.

  • A custom logging component that writes to the Windows® Event Log in Windows 2000 Server, to a database, or to an application-specific text file.

Implement debug tracing wisely. When you have to insert debug messages into your script, you should emphasize events that might enable you to reconstruct the flow through the code leading up to an error. Capture function calls and values of parameters. Log important values only; if you write too many messages, you can slow down the application considerably.

Try to make it easy to disable and enable debug tracing. For small projects, it's okay to comment out the debug trace statements that you want to disable. For larger projects, however, you should encapsulate the debug trace code in a script or component function that can be enabled and disabled independently of the code.

Tracking Events in Global.asa

Debugging Session and Application events can be tricky, because you can't use the Response.Write method to display messages to the client browser. One useful technique is to use the Scripting.FileSystemObject to generate a simple log file with text messages indicating the success or failure of these events. The following script writes notifications for Application start and end events, as well as for every Session start and end event.

<!--METADATA TYPE="TypeLib"
NAME="Scripting" FILE="C:\Winnt\System32\scrrun.dll"
UUID="420B2830-E718-11CF-893D-00A0C9054228" VERSION="1.0"
-->
<OBJECT ID="AppFileSystemObject" SCOPE=APPLICATION RUNAT=SERVER
PROGID="Scripting.FileSystemObject">
</OBJECT>
<SCRIPT LANGUAGE=VBScript RUNAT=SERVER>
Sub OutputDebugFile(ByRef strText)
Dim ts
Application.Lock
Set ts = AppFileSystemObject.OpenTextFile(Application("DebugFile"),_
ForAppending, True, TristateUseDefault)
ts.WriteLine Now & ": " & strText
ts.Close
Application.Unlock
End Sub
Sub Application_OnStart
'DebugFile must be defined before calling OutputDebugFile.
Application("DebugFile") = "c:\webs\appevnts.log"
OutputDebugFile("Application Started")
End Sub
Sub Application_OnEnd
OutputDebugFile("Application Ended")
End Sub
Sub Session_OnStart
OutputDebugFile("Session Started: " & Session.SessionID)
Response.Redirect "./end.asp" 'End Session (this creates a loop!).
End Sub
Sub Session_OnEnd
OutputDebugFile("Session Ended: " & Session.SessionID)
End Sub
</SCRIPT>
Script Management

This section discusses some techniques to help you manage longer scripts, and to minimize subtle errors in large projects.

Establishing a Library of Helper Routines

It's often a good idea to collect commonly used functions into one file and to include that file in all pages requiring those functions.

The include file can contain plaintext, declare functions and subroutines with <SCRIPT> tags, or can define variables and constants. To include the file, use a server-side include directive at the top of your file. Here is an example:

<!-- #include virtual="/MyRoot/include/funclib.inc" -->

Usually, include files are named with the .inc extension. Although this differentiates the file, it may pose a security threat if you have directory browsing enabled. The .inc extension is not normally script-mapped by IIS 5.0, and most browsers don't recognize it. Consequently, anyone who knows where to find the files can download and open them. You can prevent this from happening by either associating the .inc extension with Asp.dll in the script map (use the App Mappings tab of the Application Configuration dialog box), or by storing the include files in their own subdirectory and disallowing directory browsing as well as read access.

The entire text of the include file is incorporated into the .asp source file at the point where the include statement appears. Although there is technically no limit to the number of files you can include, each one adds to the size of the compiled .asp file—so it's better to include only files that you know you will need.

Since all include files are processed before the script executes, you cannot dynamically decide which file to include. For the same reason, you cannot use the include directive in Global.asa.

Using Dictionaries to Partition the Session Namespace

When used indiscriminately, the Session object is no more than a global data area. For large Web applications, the Session collection can become quite cluttered. This increases the likelihood that some part of the application might inadvertently make changes that have unexpected repercussions elsewhere. To avoid this situation, development teams must either use a naming convention that will decrease the chance of duplicate Session key names, or must use other methods of storing session values.

One such method uses the Dictionary object to further partition the global session namespace. Like the Session object, the Dictionary object can store any number of values and keywords in an associative array. Disparate sections of the application can create individual Dictionary objects as needed to contain local values, and can also store a single reference to their namespace in the Session object. Not only can groups of values be managed as a single entity, the Dictionary object makes it easy to free resources all at once when they are no longer required by the application. For more information about the Dictionary object, see the IIS 5.0 online product documentation.

Debugging ISAPI and Server Components

This section explains how to connect to running applications, as well as how to use the Script Debugger in order to step through the source code of your components, ISAPI filters, and extensions.

Disabling Debug Exception Handling

ASP experts should recognize the following error message:

Error 'ASP 0115'
Unexpected error
A trappable error occurred in an external object. The script cannot 
continue running.

This error is caused by an access violation in a component process. The reason ASP is able to display a message is because the crash is detected by the built-in exception handling of the ASP process.

When you are debugging an application component, it is often easier to detect where faults occur when exception handling is disabled. You can disable exception handling by using the Process Options tab of the Application Configuration dialog box in Internet Services Manager. The Process Options tab appears either when editing global properties, which affect all in-process applications, or when editing the properties of an out-of-process application marked to run within its own memory space.

The extra overhead in system resources required to run an application in its own process is worth the added security of knowing a crash will not affect other applications. When you clear the Enable debug exception catching check box on the Process Options tab, ASP will not capture serious application errors. Instead, they will appear as faults in both the isolated application process and in-process components.

Debugging IIS 5.0

Debugging IIS 5.0 may be necessary should any of the following events occur:

  • You receive ASP 0115 error messages from pages that call server-side components.

  • IIS 5.0 stops serving ASP pages.

  • The Web service stops and clients cannot connect to the server.

  • Inetinfo.exe causes a Dr. Watson to appear.

  • Inetinfo.exe stops running for no apparent reason.

There are two ways to establish an environment for debugging server components and ISAPI extensions:

  • Attach a debugging tool to the IIS 5.0 process.

  • Use the World Wide Web Publishing Service to start a debugging tool.

Attach a Debugging Tool to the IIS 5.0 Service

If you are using a debugging tool capable of attaching to a Windows 2000 Server process, you can use it to debug your component or extension.

To debug an ISAPI extension with Visual Studio

  1. Start the iisadmin process. This can be done from the command line with the command net start iisadmin. You can also use the Services application (in Administrative Tools) in Control Panel to start the IIS Admin Service; this will start the iisadmin process.

  2. Run Visual Studio and select the Attach to Process command from the Start Debug submenu of the Build menu.

  3. Click the Show System Process check box.

  4. Select the Inetinfo process from the list and click OK.

  5. Start the w3svc service. This can be done from the command line with the command net start w3svc. You can also use the Services application in Control Panel to start the World Wide Web Publishing Service.

Use Publishing Service to Start a Debugging Tool

If the debugging tool you are using is not capable of attaching to a Windows 2000 Server process, you will need to establish an appropriate debugging environment.

To start a debugging tool

  1. Right-click on MyComputer and click Manage.

  2. Open the SystemTools menu and click Services.

  3. Right-click IIS Admin Service and click the Logon tab.

  4. Select the Allow Service to Interact with Desktop check box and click OK.

  5. Repeat steps 2 and 3 for all processes that run under the IIS Admin process, for example, World Wide Web Publishing Service and FTP Publishing Service.

  6. Use the Registry Editor to add a subkey named "Inetinfo.Exe" to the following key:
    HKEY_LOCAL_MACHINE
    \Software
    \Microsoft
    \WindowsNT
    \CurrentVersion
    \Image File Execution Options key

  7. Add the following entry to this new key:
    Debugger = DebuggerExeName, where DebuggerExeName is the full path to the debugger you are using.

Caution Do not use a registry editor to edit the registry directly unless you have no alternative. The registry editors bypass the standard safeguards provided by administrative tools. These safeguards prevent you from entering conflicting settings or settings that are likely to degrade performance or damage your system. Editing the registry directly can have serious, unexpected consequences that can prevent the system from starting and require that you reinstall Windows 2000. To configure or customize Windows 2000, use the programs in Control Panel or Microsoft® Management Console (MMC) whenever possible.

When the World Wide Web Publishing Service is started, your debugger will run as well. You can now set appropriate breakpoints in your ISAPI extension.

You won't be able to set breakpoints in a component's source code until the component has been loaded into memory. First, you will need to start Internet Explorer and view the ASP page containing the object. As soon as the page is loaded, you should be able to set breakpoints in your component. Click Refresh to view the page again, and trigger the breakpoints you selected. If the component cannot be loaded even once (for instance, if the fault occurs in component startup code), you will need to load the component DLL prior to starting the debugging session.

Inability to Create Components

Sometimes the Server.CreateObject method fails and produces an "ASP 0177: Server.CreateObject Failed" error. This can happen even if the component works fine in Visual Basic on the same computer or while using ASP on other computers.

One likely cause for this behavior is that the authenticated user does not have permission to invoke the COM object. In the simplest scenario, the authenticated user doesn't have access to the component DLL or executable. In many cases, however, the component depends on other DLLs that the authenticated user does not have permission to access. Usually, a majority of users access a site anonymously, using the IUSR_computername account.

To verify that this is a permissions problem, try invoking the component from another tool such as Visual Basic. This approach verifies that the component is registered properly on the server. If the component cannot be created from Visual Basic, you are probably not dealing with a permissions problem. (The component might need to be registered.)

If you believe you are dealing with a permissions problem, check permissions on the component and on any dependent files, such as other DLLs. If you still are unable to track down the problem, you may need to systematically search permissions.

Additional Resources

The following Web sites and books provide additional information about IIS 5.0 and about other features of Windows 2000 Server, as well as features for developing Web applications.

https://www.15seconds.com/

A free resource for developers working with Microsoft Internet solutions. This site includes the 15 Seconds newsletter, Stephen Genusa's Frequently Asked Questions, List Servers, and the Consultant Program. There are also book reviews, how-to articles, and job opportunities that deal with ASP and Microsoft Internet solutions.

https://www.chilisoft.com

ChiliSoft's Chili!Soft ASP brings the power of ASP to servers other than IIS. Chili!Soft ASP can host ASP pages and components on a variety of Web servers without any changes to code. Includes support for Windows-based Netscape Web servers.

https://www.microsoft.com/solutions/msi/

This comprehensive Web site called TechNet has everything you need to plan and build an intranet site. Explore white papers, FAQs, and case studies, or download free intranet solutions written by top Microsoft® Solution Providers.

https://msdn.microsoft.com

Open the Libraries menu and point to Web Workshop Home. From there, point to Server Technologies. This is the Active Server Pages workshop area of Microsoft MSDN; a must-see resource.

Books

ActiveX Web Programming by Adam Blum,1996, New York: John Wiley & Sons, Inc.

Although written while ASP was still in development, this book is an excellent introduction to CGI, ISAPI, ActiveX controls, and client-side scripting.

Working with Active Server Pages by Michael Corning, 1997, Indianapolis: Que Corporation.

Covers design, development, and implementation of ASP pages. Includes examples of database-driven customer scenarios using ASP and ADO.

Professional Active Server Pages 2.0 by Alex Federov, 1998, Chicago: Wrox Press Ltd.

A highly recommended and comprehensive tutorial of ASP and ADO. Includes practical techniques for creating n-tier Web applications.

Bb742411.spacer(en-us,TechNet.10).gif