In order to do any nontrivial task with ASP, and indeed, even to do some trivial tasks in a clean way (see Listing 9-4), you need to understand the native objects available in ASP applications. There are six major objects available to ASP applications:
Some objects you will virtually always use, such as the Response and Session objects, and some you will seldom use, such as the ObjectContext object. The next several sections describe each object in detail. You'll also find tables with their commonly used methods, properties, and events. You'll find more information about these objects in the MSDN documentation.
The Application object is used to share information among sessions using a single application. The Application object also exposes two events that allow setup and shutdown operations to take place for the application. Table 9-1 details the Application object's major properties and methods.
Table 9-1 Application Object Collections, Methods, and Events
Name | Description |
---|---|
Contents | A collection that contains all items added to the application object through scripting |
StaticObjects | A collection that contains objects added to the application through the <OBJECT> tag |
Lock | A method to lock the application, serializing access to the application object (required because more than one user can be interacting with the application object at one time) |
Unlock | A method to unlock the application after it has been locked using the Lock method |
Application_OnEnd | An event fired when the application ends; used for cleanup of anything created in the Application_OnStart event |
Application_OnStart | An event fired when an application starts; used to set up any application-level information that is required, fired once in the life of the application |
To fully understand the Application object, you must understand the structure of an ASP application. Separate applications are set up in separate virtual directories. (See Appendix A for information on setting up virtual directories in IIS 5.) When an application is run ( specifically , when the first ASP document is requested ), IIS looks in the root directory of the application's virtual directory for a file named Global.asa. Listing 9-5 shows a simple example of a Global.asa file.
NOTE
One thing that you will notice about ASP VBScript code is that it often uses shortcuts to get at some member variables . For instance, look at the following line of code from Listing 9-5:
Application("SQLLOGIN")="UID=sa;PWD=PASSWORD;DSN=EXCEPTIONS"The expression on the left of the equal sign is a shortcut to the actual variable, Application.Contents("SQLLOGIN") . Contents is the default property for the application object, and thus can be eliminated in expressions that would otherwise require it.
This type of shortcut is common, and virtually all of the objects have default properties that allow you to essentially subscript the object itself to access elements within it. Some of the objects have more complex behaviors than a default property.
Listing 9-5
Global.asa <SCRIPTLANGUAGE="VBScript"RUNAT="Server"> 'Youcanaddspecialeventhandlersinthisfilethatwillgetrun 'automaticallywhenspecialActiveServerPageseventsoccur.To 'createthesehandlers,justcreateasubroutinewithanamefrom 'thelistbelowthatcorrespondstotheeventyouwanttouse. subApplication_OnStart'Runsoncewhenthefirstpageofyour 'applicationisrunforthefirsttimeby 'anyuser. Application("SQLLOGIN")="UID=sa;PWD=PASSWORD;DSN=EXCEPTIONS" endsub 'subApplication_OnEnd'RunsoncewhentheWebservershutsdown. 'endsub SubSession_OnStart session("User")="" session("UserCode")=0 session("UserLevel")=0 session("UserFirstName")="" response.redirect"login.asp" EndSub SubSession_OnEnd session("UserCode")=0 EndSub </SCRIPT> |
The .asa extension stands for Active Server Application. Within this file, IIS executes the Application_OnStart event handler if it exists. In Listing 9-5, the application variable SQLLOGIN is set. The other bookend for Application_OnStart is Application_OnEnd . In Listing 9-5, the Application_OnEnd event handler is commented out. It is faster not to have one of these handlers present if no code needs to be run. In Listing 9-5, no resources are allocated in Application_OnStart , so Application_OnEnd is not needed.
Two other points are worth noting about this file. An application-level variable is set within Application_OnStart , and there is no synchronization of the variable using Application . Lock and Application.Unlock . Synchronization is not necessary here because this event will be called exactly once and no other application-level code can execute until Application_OnStart returns. The second notable feature of this file is that while the Application_OnEnd event is supported, it is not a good idea to actually place any critical resource deinitialization operations in this event handler, because in certain cases it might not be called reliably. I'll discuss similar concerns about the Session_OnEnd event shortly. The Application_OnEnd handler is especially prone not to get called ”for instance, if the server just goes down rather than properly shutting down. In many cases, the resource leak won't matter because if the server drops , the disposition of the resources is not a major concern. However, in situations in which a resource is owned on behalf of the server by another server, a resource leak can be a concern.
Because access to application scope variables must be gated with calls to Application.Lock and Application.Unlock , avoid using application-level variables. Using application-level variables thus can cause the application to have a choke point ”a point through which the application is serialized. In Listing 9-5, the application-level variable that is set is treated as a read-only variable, and so its use will not cause a problem.
NOTE
One concern when using ASP script files is the possibility that the script might become visible or readable by clients . This is not entirely unfounded, as IIS 3 did have a bug that caused a fully qualified URL, including a file name entered with a trailing period (for instance, http://pro/default.asp .), to display the contents of the ASP file rather than the pure HTML code that would be generated if the script was properly processed . Though this bug has been corrected in subsequent versions of IIS, its recurrence is still a concern. One precaution I take is never to place anything that I would not like displayed to end users (such as the user name and password for access to the SQL Server database) in an ASP file. Therefore, I set an application variable in the Global.asa file that will allow all ASP files to get at the information indirectly.
The Session object contains per-session information and allows an application to track the state of a single user's session. The Session object is similar to the Application object in that it exposes events that are placed in the Global.asa file. In the Session_OnStart event handler in Listing 9-5, session variables are initialized and the user is then redirected to a specific page ”Login.asp ”using the Response object, which I'll discuss shortly. Table 9-2 details the Session object's major properties and methods.
NOTE
Since an application can have a default page, why is it important to redirect a user to a page here? Suppose that the default page in the ASP application is Default.asp. Now suppose that the end user happens to know that there was a page in the application called TopSecret.asp. If rather than entering only the virtual directory name (for instance http://pro/ ), the user entered the virtual directory name with the other ASP file name appended (for instance http://pro/topsecret.asp ), he could bypass the default start page for the site. By redirecting the user in the Session_OnStart event handler, we force the user to pass through the initial page that we specify.
Table 9-2 Session Object Collections, Methods, and Events
Name | Description |
---|---|
Contents | A collection that contains all items added to the Session object through scripting |
StaticObjects | A collection that contains objects added to the Session object through the <OBJECT> tag and given session scope |
CodePage | A code page used for symbol mapping |
LCID | A locale identifier |
SessionID | Returns the session identification for this user. The SessionID property is a LONG value and should not be used for generation of database keys or for any other persistent data store. SessionID values are reused when the server is restarted. |
Timeout | The timeout period, in minutes, for this session state. This property determines how long the session needs to be inactive before it will be terminated . |
Abandon | This method destroys a Session object and frees the contents. The actual destruction of the Session object is delayed until the current page has exited. |
Session_OnEnd | An event fired when the session ends. Used for cleanup of anything created in the Session_OnStart event. |
Session_OnStart | An event fired when a user's session starts. Used to set up any session-level information that is required. This event is fired once for each session. |
Most of the Session object's collections, methods, and events are either similar to those of the Application object, self-explanatory, or not that significant. One exception is the Timeout property, which determines the amount of time a session needs to be inactive before the session is killed . This can be useful in situations where the system should time out if inactive ”for example, a clinical system in a hospital. Often healthcare workers are called away in the middle of using the system, and these systems might be located in semi-public places. In such cases, you might set a shorter inactivity timeout. Very busy sites might also set a shorter timeout to free resources sooner from inactive users. When the timeout expires , the Session_OnEnd event handler will be called.
The Server object is a utility object that provides access to methods and properties on the server. Unlike the Application and Session objects, the Server object has no collections associated with it. Table 9-3 provides an overview of the important properties and methods of the Server object.
Hypertext Transfer ProtocolHypertext Transfer Protocol (HTTP) is a stateless protocol. This means that the server sees each request and response as a new event ”not tied to previous requests . How then does the Session object establish sessions and keep track of session-level variables and inactivity timeouts? The answer is through the use of cookies. Cookies are files stored on the local hard drive by browsers that accept cookies. These cookies have been the cause of some privacy concerns, and there certainly are abuses of cookies. For example, seemingly unrelated sites share information about cookies so that when you visit one site, the second site will change behavior based upon your visit to the first site. Most modern browsers let you turn off cookies, but doing so will most likely deprive you of some of their benefits such as persistence of user preferences.
Are there alternatives to cookies for maintaining state? If you wish to step outside specific support within IIS, you can certainly manage your own concept of state by using hidden text entry fields on each form that will contain information, allowing your application to know who your users are and where in the application they have been. This seems like an awful lot of work and is probably only worth pursuing if you need to communicate with users over the Internet and you don't want to do anything that might discourage users from using your site.
Table 9-3 Server Object Methods and Properties
Name | Description |
---|---|
ScriptTimeout | A property that specifies the amount of time a script can run before being terminated. This timeout value does not apply while a server component is running. Additionally, this value cannot be less than the value set in the IIS metabase.* |
CreateObject | A method used to create an instance of a server component. The method takes a single argument ”a string representing the program ID (for instance, ADODB.Recordset ). There is no DestroyObject method. Objects are destroyed by settingthem equal to the VBScript keyword Nothing . |
Execute | Execute is a new method in IIS 5. It executes the ASP file specified as the argument as if it were part of the current ASP file. This method is intended to act like procedure calls in other languages, allowing applications to be structured neatly and providing for reuse. |
GetLastError | This is another new method in IIS 5. It might sound familiar to Win32 programmers; its function is similar to the Win32 function. It returns an ASPErrorObject that gives more complete error information than previous error handling provided. |
HTMLEncode | This method encodes HTML so that the code itself can be displayed rather than being rendered as HTML. |
MapPath | This method maps a virtual path to a physical path. For instance, if you passed in a virtual path of \troubleshoot\default.asp ,this method might return something like d:\inetpub\ wwwroot \troubleshoot\default.asp |
Transfer | This method is new for IIS 5. It sends all of the current state information to another ASP for processing. Even if the ASP specified is in another virtual path, the variables defined within the current application will be available in the called ASP. |
URLEncode | This method encodes a Uniform Resource Locator (URL) so that it can be displayed directly. |
* The IIS metabase is the repository for most IIS configuration information. In versions of IIS prior to version 5, a large amount of this configuration information was stored in the registry. IIS 5 uses virtually no registry entries for configuration information.
The CreateObject method is the one element of the Server object that virtually all applications will use. This is the great escape clause that allows ASP applications to move beyond scripts and leverage existing code ”at least existing code that can be modified to work within an ActiveX component. The Execute and Transfer methods are the most important new additions to IIS 5. While much of the functionality provided by these two methods can also be provided using the Response.Redirect method, Execute and Transfer offer the following advantages.
The GetLastError method returns an ASPError object that provides the following information about the most recent error:
This information is a significant improvement over previously available error information.
The Request object is the object that allows the user of a browser to provide information to the server to process the user's request. The information in the Request object is generally what drives the progress of the application. Table 9-4 provides an overview of the collections, properties, and methods of the Request object.
Of the members of the Request object, TotalBytes and BinaryRead are outside the scope of this discussion. The collections operate in a somewhat peculiar way. For instance, if we were to try and send to the browser Request("MY_VAR") , not a single default collection would be searched. Each of the collections will be searched in the order shown here.
Table 9-4 Request Object Collections, Properties, and Methods
Name | Description |
---|---|
ClientCertificate | This collection contains the certification fields (as specified in the X.509 standard) from a request issued by the Web browser. These certificates are generally available only when connecting to a server using a URL beginning in https :// rather than http:// . The Web server must request the client certificates. |
Cookies | This collection contains the values sent to the server from the client browser during a request. Cookies are used, among other things, to establish session state. |
Forms | This collection contains the values of form elements in the HTTP request. This can be used to retrieve values entered on forms. The values can be from text boxes, list boxes, or any of the other supported HTTP controls. |
QueryString | This collection contains the values passed as part of the URL. For instance, given the URL http://dual/default.asp?N=Doug , Request.QueryString("N") would be equal to "Doug". |
ServerVariables | This collection contains the values of a set of predetermined environment variables. These variables are further explained in the MSDN documentation and will be displayed by the ServerVariables.asp example ASP page presented in this section. |
TotalBytes | This read-only property contains the number of bytes the client is sending in the request. |
BinaryRead | This method reads the number of bytes specified by its single argument and returns a safe array with those bytes. BinaryRead is generally used in conjunction with the TotalBytes property and only if specialized access to the request is required. Once BinaryRead is called, trying to access Request.Form will generate an error, and calling BinaryRead after Request.Form has been accessed will also generate an error. |
This can lead to some interesting problems and some useful optimizations. If you had two variables named MY_VAR , one in the QueryString collection, and another in the Forms collection, you would get the copy in QueryString unless you fully qualified the variable ”for instance, Request.Form("MY_VAR") . In a complex application, and especially when searching the ServerVariables collection, it is much more efficient to specifically look for the variable in the collection it is in.
The ServerVariables collection contains many details about the current session. Listing 9-6 contains code that prints out all of the server variable names and values. Listing 9-7 contains the results of running this code on my machine, detailing all of the server variables available.
Listing 9-6
ServerVariables.asp <%@Language=VBScript%> <HTML> <HEAD> <METANAME="GENERATOR"Content="MicrosoftVisualStudio6.0"> </HEAD> <BODY> <% dimiLoop Response.Write("<H1>ServerVariables</H1><P>") foriLoop=1toRequest.ServerVariables.Count Response.Write(Request.ServerVariables.Key(iLoop)&"-") Response.Write(Request.ServerVariables.Item(iLoop)&"<es>") next %> <P> </P> </BODY> </HTML> |
Listing 9-7
Output from ServerVariables.asp ALL_HTTP-HTTP_ACCEPT:*/*HTTP_ACCEPT_LANGUAGE:en-us APPL_MD_PATH-/LM/W3SVC/1/Root/Chap09 APPL_PHYSICAL_PATH-\PRO\e\TEXT\ntbook\Chap09\ AUTH_PASSWORD- AUTH_TYPE-NTLM AUTH_USER-DUAL\Administrator CERT_COOKIE- CERT_FLAGS- CERT_ISSUER- CERT_KEYSIZE- CERT_SECRETKEYSIZE- CERT_SERIALNUMBER- CERT_SERVER_ISSUER- CERT_SERVER_SUBJECT- CERT_SUBJECT- CONTENT_LENGTH-0 CONTENT_TYPE- GATEWAY_INTERFACE-CGI/1.1 HTTPS-off HTTPS_KEYSIZE- HTTPS_SECRETKEYSIZE- HTTPS_SERVER_ISSUER- HTTPS_SERVER_SUBJECT- INSTANCE_ID-1 INSTANCE_META_PATH-/LM/W3SVC/1 LOCAL_ADDR-127.0.0.1 LOGON_USER-DUAL\Administrator PATH_INFO-/chap09/ServerVariables.asp PATH_TRANSLATED-\PRO\e\TEXT\ntbook\Chap09\ServerVariables.asp QUERY_STRING- REMOTE_ADDR-127.0.0.1 REMOTE_HOST-127.0.0.1 REMOTE_USER-DUAL\Administrator REQUEST_METHOD-GET SCRIPT_NAME-/chap09/ServerVariables.asp SERVER_NAME-localhost SERVER_PORT-80 SERVER_PORT_SECURE-0 SERVER_PROTOCOL-HTTP/1.1 SERVER_SOFTWARE-Microsoft-IIS/4.0 URL-/chap09/ServerVariables.asp HTTP_ACCEPT-*/* HTTP_ACCEPT_LANGUAGE-en-us HTTP_CONNECTION-Keep-Alive HTTP_HOST-localhost HTTP_USER_AGENT-Mozilla/4.0(compatible;MSIE5.0;WindowsNT;DigExt) HTTP_COOKIE-ASPSESSIONIDQQQQQQCG=ONMLKIEDDAEFEHOAHOFHIBIL HTTP_AUTHORIZATION-NTLMTlRMTVNTUAADAAAAAAAAAEAAAAAAAAAAQAAAAAAAAA BAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAABcIBAA== HTTP_ACCEPT_ENCODING-gzip,deflate HTTP_EXTENSION-Security/Remote-Passphrase |
The code in Listing 9-6 uses a variable to loop through Request.ServerVariables.Count variables, printing the "Key" (actually the name of the variable) and the "Item" (the value itself). A couple of the results related to security require some explanation. Notice that AUTH_USER is DUAL/Administrator, as I am logged in as administrator on the machine DUAL. This page is using Windows NT Challenge/Response as the authentication, so the AUTH_TYPE reports itself as NTLM, for NT LAN Manager. AUTH_PASSWORD is blank because NTLM authentication does not store the actual password. If the page were set up to allow Basic authentication, the AUTH_TYPE would be BASIC, and AUTH_PASSWORD would contain the password as entered. This is not an ideal authentication method, since the user name and password are sent using only simple Base64 encoding ”essentially equivalent to sending it as clear text.
Notice also that the APPL_PHYSICAL_PATH shows that the virtual directory is actually on another machine ”in this case, on the server PRO ”since the path is\\PRO\e\TEXT\ntbook\Chap09\. Some of the other information would be more interesting when the page is not run from the workstation where IIS is loaded ”for instance, the REMOTE_ADDR and LOCAL_ADDR, which here are set to the loop back address 127.0.0.1. If you were creating an application on an intranet that used static IP addresses, the addresses could be used to provide additional security, allowing the application to grant or deny access to a given page based upon the user as well as the address of the request.
The Response object is used to send output to the client. Some of the methods in the Response object will be used constantly in any real-world application. An example of this is Response.Write . If you elect not to use nonscripted HTML to send output to the browser, Response.Write is commonly how you would send output. Other methods, such as Response.BinaryWrite , are much less common in the real world. Table 9-5 lists the collections, properties, and methods of the Response object.
Of the six native IIS objects, the Response object contains the most members. Since the goal of any Web application is to present information to the user, this should not be surprising. Output can be sent to users using ASP pages in many ways, and subtle differences in the method used to present the information can make dramatic differences in performance, and more importantly in perceived performance.
The Buffer property is the key to some performance improvements. When all output is saved and sent together after all processing is done, performance is enhanced. Of course, there is one problem. The user's perception of speed might be based upon seeing some immediate activity on the browser. Buffering prevents this from happening. The page will be blank until the output is flushed. Unfortunately, either all or none of the output to the browser can be buffered. In addition, HTML's way of handling some kinds of output can complicate matters. IIS does not send tables to the browser, regardless of the setting of the Response.Buffer property, until the entire table has been output. So, if you are outputting a page that is primarily a table, setting Response.Buffer to True might not make any real difference in how quickly the user sees the information because the table will be buffered in any event.
Table 9-5 Response Object Collections, Properties, and Methods
Name | Description |
---|---|
Cookies | This collection allows you to set the value of each cookie. If the cookie does not exist, IIS creates it. |
Buffer | This property specifies whether the output is buffered. (The behavior of this property has changed in IIS and will be discussed later in this section.) |
CacheControl | This property determines whether the proxy server can cache the output of ASP pages. Generally this will not help performance on ASP pages that generate custom HTML for every request. |
CharSet | This property appends a string to the content-type header in the response object. While it is expected that this will be a valid character set, such as ISO-LATIN-7, no checking is done. |
ContentType | This property changes the content type from the default of text/HTML. |
Expires | This property specifies the length of time before a page cached on the browser expires. This will affect what happens when a browser requests a page more than once. Setting Expires to a value less than 0 immediately expires the page. |
ExpiresAbsolute | This property specifies a date and time when the page expires. |
IsClientConnected | This is a read-only property that indicates whether the client is still connected. This can be useful when processing a client's request takes a long time and uses lots of resources. It might be worthwhile to check periodically to see if the client is still connected while processing the request. |
PICS | This property adds a value to the pics-label field of the response header. |
Status | This property modifies the status line returned by the server. This should be a 3-digit number followed by an explanation ”for instance, 401 Unauthorized . |
AddHeader | This advanced method allows you to add a header with a specified value to the response. You can find further details on AddHeader in the MSDN documentation. |
AppendToLog | This method appends a message to the end of the server log entry for this request. |
BinaryWrite | This method allows you to write information to the response without character conversion. This could be used to send nonstring information to the browser. |
Clear | This method clears buffered HTML output. An error occurs if this is called when nonbuffered output has already been sent. |
End | This method stops processing and returns the current content. The balance of the output specified is skipped . |
Flush | This method sends buffered output to the browser immediately. |
Redirect | This method causes the browser to try to redirect to the URL specified as the argument to this method. Compare this to the behavior of Server.Execute and Server.Transfer , where no client involvement is needed. |
Write | The Write method is the workhorse of the Response object. The string passed as the single argument to this method is either buffered for output to the server or is immediately sent to the server, based upon the setting of the Buffer property. |
To further complicate matters, IIS 5 changes the default value of Response.Buffer . This property had defaulted to False, but now will default to True. An additional twist is that in an attempt not to break existing applications, upgraded servers will continue to use False as the default value for Response.Buffer .
The remaining members of the Response object are either obscure (such as PICS) or self-explanatory. The sample application presented at the end of this chapter makes extensive use of the Response object.
The ObjectContext object is perhaps the most misunderstood of the native objects. The ObjectContext object is used to manage transactions managed by Component Services in Windows 2000 (or Microsoft Transaction Server in Windows NT 4). Often when creating Web applications, you are required to perform a series of operations in multiple steps. Sometimes it is crucial that the actions should be completed in an all-or-nothing fashion. The @TRANSACTION directive within ASP pages is designed for those situations. Pages that use the @TRANSACTION directive are transactional, and either succeed completely or fail. The @TRANSACTION directive must be the first line in the ASP file, and @TRANSACTION must be set to one of the choices shown here.
|
|
|
|
Transactions are one area in which the new Server.Execute and Server.Transfer are required. If you have two pages ”one called by another ”and you want them both to be part of a single transaction, Response.Redirect will not work, but either of the Server object methods will. Table 9-6 presents the ObjectContext methods and events.
Table 9-6 ObjectContext Object Methods and Events
Name | Description |
---|---|
SetComplete | This method declares that the script knows of no reason why the transaction should not complete. If all components involved in the transaction also call SetComplete , the transaction will be committed. |
SetAbort | This method declares that the transaction initiated by the script has not completed, and the resources involved should not be changed. |
OnTransactionCommit | This event handler is called after a transaction commits and is found on the page where the transaction is located. |
OnTransactionAbort | This event handler is called if the transaction is aborted and is found on the page where the transaction is located. |
Not every action taken by a script will be rolled back when a transaction aborts. For example, if you are writing to a file on the server that is not part of a database, that write to the file will not roll back. Components used by the transactional page must also be MTS-aware if the actions taken are to be all-or-nothing.