Delphi allows you to create Web server applications as CGI applications or dynamic-link libraries (DLLs). These Web server applications can contain any nonvisual component. Special components on the Internet palette page make it easy to create event handlers that are associated with a specific Uniform Resource Identifier (URI) and, when processing is complete, to programmatically construct HTML documents and transfer them to the client.
Frequently, this content is drawn from databases. Internet components can be used to automatically manage connections to databases, allowing a single DLL to handle numerous simultaneous, thread-safe database connections.
This chapter describes these Internet components, and discusses the creation of several types of Internet applications.
Note: You can also use ActiveForms as Internet server applications. For more information about ActiveForms, see "Generating an ActiveX control based on a VCL form".
Many of the protocols that control activity on the Internet are defined in Request for Comment (RFC) documents that are created, updated, and maintained by the Internet Engineering Task Force (IETF), the protocol engineering and development arm of the Internet. There are several important RFCs that you will find useful when writing Internet applications:
The IETF maintains a library of the RFCs on their Web site, www.ietf.cnri.reston.va.us
The Uniform Resource Locator (URL) is a complete description of the location of a resource that is available over the net. It is composed of several parts that may be accessed by an application. These parts are illustrated in Figure 29.1:
The first portion (not technically part of the URL) identifies the protocol (http). This portion can specify other protocols such as https (secure http), ftp, and so on.
The Host portion identifies the machine that runs the Web server and Web server application. Although it is not shown in the preceding picture, this portion can override the port that receives messages. Usually, there is no need to specify a port, because the port number is implied by the protocol.
The ScriptName portion specifies the name of the Web server application. This is the application to which the Web server passes messages.
Following the script name is the pathinfo. This identifies the destination of the message within the Web server application. Path info values may refer to directories on the host machine, the names of components that respond to specific messages, or any other mechanism the Web server application uses to divide the processing of incoming messages.
The Query portion contains a set a named values. These values and their names are defined by the Web server application.
The URL is a subset of the Uniform Resource Identifier (URI) defined in the HTTP standard, RFC1945. Web server applications frequently produce content from many sources where the final result does not reside in a particular location, but is created as necessary. URIs can describe resources that are not location-specific.
HTTP request messages contain many headers that describe information about the client, the target of the request, the way the request should be handled, and any content sent with the request. Each header is identified by a name, such as "Host" followed by a string value. For example, consider the following HTTP request:
GET /art/gallery.dll/animals?animal=dog&color=black HTTP/1.0 Connection: Keep-Alive User-Agent: Mozilla/3.0b4Gold (WinNT; I) Host: www.TSite.com:1024 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
The first line identifies the request as a GET. A GET request message asks the Web server application to return the content associated with the URI that follows the word GET (in this case /art/gallery.dll/animals?animal=doc&color=black). The last part of the first line indicates that the client is using the HTTP 1.0 standard.
The second line is the Connection header, and indicates that the connection should not be closed once the request is serviced. The third line is the User-Agent header, and provides information about the program generating the request. The next line is the Host header, and provides the Host name and port on the server that is contacted to form the connection. The final line is the Accept header, which lists the media types the client can accept as valid responses.
The client/server nature of Web browsers is deceptively simple. To most users, retrieving information on the World Wide Web is a simple procedure: click on a link, and the information appears on the screen. More knowledgeable users have some understanding of the nature of HTML syntax and the client/server nature of the protocols used. This is usually sufficient for the production of simple, page-oriented Web site content. Authors of more complex Web pages have a wide variety of options to automate the collection and presentation of information using HTML.
Before building a Web server application, it is useful to understand how the client issues a request and how the server responds to client requests.
When an HTML hypertext link is selected (or the user otherwise specifies a URL), the browser collects information about the protocol, the specified domain, the path to the information, the date and time, the operating environment, the browser itself, and other content information. It then composes a request.
For example, to display a page of images based on criteria selected by clicking buttons on a form, the client might construct this URL:
http://www.TSite.com/art/gallery.dll/animals?animal=dog&color=black
which specifies an HTTP server in the www.TSite.com domain. The client contacts www.TSite.com, connects to the HTTP server, and passes it a request. The request might look something like this:
GET /art/gallery.dll/animals?animal=dog&color=black HTTP/1.0 Connection: Keep-Alive User-Agent: Mozilla/3.0b4Gold (WinNT; I) Host: www.TSite.com:1024 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
The Web server receives a client request and can perform any number of actions, based on its configuration. If the server is configured to recognize the /gallery.dll portion of the request as a program, it passes information about the request to that program. The way information about the request is passed to the program depends on the type of Web server application:
In all cases, the program acts on the request of and performs actions specified by the programmer: accessing databases, doing simple table lookups or calculations, constructing or selecting HTML documents, and so on.
When a Web server application finishes with a client request, it constructs a page of HTML code or other MIME content, and passes it back (via the server) to the client for display. The way the response is sent also differs based on the type of program:
Creating a Web server application as a DLL reduces system load and resource use by reducing the number of processes and disk accesses necessary to service an individual request.
Web server applications extend the functionality and capability of existing Web servers. The Web server application receives HTTP request messages from the Web server, performs any actions requested in those messages, and formulates responses that it passes back to the Web server. Any operation that you can perform with a Delphi application can be incorporated into a Web server application.
Using the internet components, you can create four types of Web server applications. Each type uses a type-specific descendant of TWebApplication, TWebRequest, and TWebResponse:
An ISAPI or NSAPI Web server application is a DLL that is loaded by the Web server. Client request information is passed to the DLL as a structure and evaluated by TISAPIApplication, which creates TISAPIRequest and TISAPIResponse objects. Each request message is automatically handled in a separate execution thread.
A CGI stand-alone Web server application is a console application that receives client request information on standard input and passes the results back to the server on standard output. This data is evaluated by TCGIApplication, which creates TCGIRequest and TCGIResponse objects. Each request message is handled by a separate instance of the application.
A Win-CGI stand-alone Web server application is a Windows application that receives client request information from a configuration settings (INI) file written by the server and writes the results to a file that the server passes back to the client. The INI file is evaluated by TCGIApplication, which creates TWinCGIRequest and TWinCGIResponse objects. Each request message is handled by a separate instance of the application.
All new Web server applications are created by selecting File|New from the menu of the main window and selecting Web Server Application in the New Items dialog. A dialog box appears, where you can select one of the Web server application types:
Choose the type of Web Server Application that communicates with the type of Web Server your application will use. This creates a new project configured to use Internet components and containing an empty Web Module.
The Web module (TWebModule) is a descendant of TDataModule and may be used in the same way: to provide centralized control for business rules and non-visual components in the Web application.
Add any content producers that your application uses to generate response messages. These can be the built-in content producers such as TPageProducer, TDataSetPageProducer, TDataSetTableProducer, TQueryTableProducer and TMIDASPageProducer, or descendants of TCustomContentProducer that you have written yourself. If your application generates response messages that include material drawn from databases, you can add data access components or special components for writing a Web server that acts as a client in a MIDAS application.
In addition to storing non-visual components and business rules, the Web module also acts as a dispatcher, matching incoming HTTP request messages to action items that generate the responses to those requests.
You may have a data module already that is set up with many of the non-visual components and business rules that you want to use in your Web application. You can replace the Web module with your pre-existing data module. Simply delete the automatically generated Web module and replace it with your data module. Then, add a TWebDispatcher component to your data module, so that it can dispatch request messages to action items, the way a Web module can. If you want to change the way action items are chosen to respond to incoming HTTP request messages, derive a new dispatcher component from TCustomWebDispatcher, and add that to the data module instead.
Your project can contain only one dispatcher. This can either be the Web module that is automatically generated when you create the project, or the TWebDispatcher component that you add to a data module that replaces the Web module. If a second data module containing a dispatcher is created during execution, the Web server application generates a runtime error.
Note: The Web module that you set up at design time is actually a template. In ISAPI and NSAPI applications, each request message spawns a separate thread, and separate instances of the Web module and its contents are created dynamically for each thread.
Warning: The Web module in a DLL-based Web server application is cached for later reuse to increase response time. The state of the dispatcher and its action list is not reinitialized between requests. Enabling or disabling action items during execution may cause unexpected results when that module is used for subsequent client requests.
The project that is set up for your Web application contains a global variable named Application. Application is a descendant of TWebApplication (either TISAPIApplication or TCGIApplication) that is appropriate to the type of application you are creating. It runs in response to HTTP request messages received by the Web server.
Warning: Do not include the forms unit in the project uses clause after the CGIApp or ISAPIApp unit. Forms also declares a global variable named Application, and if it appears after the CGIApp or ISAPIApp unit, Application will be initialized to an object of the wrong type.
When the Web application receives an HTTP request message, it creates a TWebRequest object to represent the HTTP request message, and a TWebResponse object to represent the response that should be returned. The application then passes these objects to the Web dispatcher (either the Web module or a TWebDispatcher component).
The Web dispatcher controls the flow of the Web server application. The dispatcher maintains a collection of action items (TWebActionItem) that know how to handle certain types of HTTP request messages. The dispatcher identifies the appropriate action items or auto-dispatching components to handle the HTTP request message, and passes the request and response objects to the identified handler so that it can perform any requested actions or formulate a response message. It is described more fully in the section "The Web dispatcher".
The action items are responsible for reading the request and assembling a response message. Specialized content producer components aid the action items in dynamically generating the content of response messages, which can include custom HTML code or other MIME content. The content producers can make use of other content producers or descendants of THTMLTagAttributes, to help them create the content of the response message. For more information on content producers, see "Generating the content of response messages".
If you are creating the Web Client in a multi-tiered database application, your Web server application may include additional, auto-dispatching components that represent database information encoded in XML and database manipulation classes encoded in javascript. The dispatcher calls on these auto-dispatching components to handle the request message after it has tried all of its action items.
When all action items (or auto-dispatching components) have finished creating the response by filling out the TWebResponse object, the dispatcher passes the result back to the Web application. The application sends the response on to the client via the Web server.
If you are using a Web module, it acts as a Web dispatcher. If you are using a pre-existing data module, you must add a single dispatcher component (TWebDispatcher) to that data module. The dispatcher maintains a collection of action items that know how to handle certain kinds of request messages. When the Web application passes a request object and a response object to the dispatcher, it chooses one or more action items to respond to the request.
Open the action editor from the Object Inspector by clicking the ellipsis on the Actions property of the dispatcher. Action items can be added to the dispatcher by clicking the Add button in the action editor.
Add actions to the dispatcher to respond to different request methods or target URIs. You can set up your action items in a variety of ways. You can start with action items that preprocess requests, and end with a default action that checks whether the response is complete and either sends the response or returns an error code. Or, you can add a separate action item for every type of request, where each action item completely handles the request.
Action items are discussed in further detail in "Action items".
When the dispatcher receives the client request, it generates a BeforeDispatch event. This provides your application with a chance to preprocess the request message before it is seen by any of the action items.
Next, the dispatcher looks through its list of action items for one that matches the pathinfo portion of the request message's target URL and that can provide the service specified as the method of the request message. It does this by comparing the PathInfo and MethodType properties of the TWebRequest object with the properties of the same name on the action item.
When the dispatcher finds an appropriate action item, it causes that action item to fire. When the action item fires, it does one of the following:
After checking all its action items, if the message is not handled the dispatcher checks any specially registered auto-dispatching components that do not use action items. These components are specific to multi-tiered database applications, which are described in "Building Web applications using InternetExpress"
If, after checking all the action items and any specially registered auto-dispatching components, the request message has still not been fully handled, the dispatcher calls the default action item. The default action item does not need to match either the target URL or the method of the request.
If the dispatcher reaches the end of the action list (including the default action, if any) and no actions have been triggered, nothing is passed back to the server. The server simply drops the connection to the client.
If the request is handled by the action items, the dispatcher generates an AfterDispatch event. This provides a final opportunity for your application to check the response that was generated, and make any last minute changes.
Each action item (TWebActionItem) performs a specific task in response to a given type of request message.
Action items can completely respond to a request or perform part of the response and allow other action items to complete the job. Action items can send the HTTP response message for the request, or simply set up part of the response for other action items to complete. If a response is completed by the action items but not sent, the Web server application sends the response message.
Most properties of the action item determine when the dispatcher selects it to handle an HTTP request message. To set the properties of an action item, you must first bring up the action editor: select the Actions property of the dispatcher in the Object Inspector and click on the ellipsis. When an action is selected in the action editor, its properties can be modified in the Object Inspector.
The dispatcher compares the PathInfo property of an action item to the PathInfo of the request message. The value of this property should be the path information portion of the URL for all requests that the action item is prepared to handle. For example, given this URL,
http://www.TSite.com/art/gallery.dll/mammals?animal=dog&color=black
and assuming that the /gallery.dll part indicates the Web server application, the path information portion is
/mammals
Use path information to indicate where your Web application should look for information when servicing requests, or to divide you Web application into logical subservices.
The MethodType property of an action item indicates what type of request messages it can process. The dispatcher compares the MethodType property of an action item to the MethodType of the request message. MethodType can take one of the following values:
Each action item has an Enabled property that can be used to enable or disable that action item. By setting Enabled to False, you disable the action item so that it is not considered by the dispatcher when it looks for an action item to handle a request.
A BeforeDispatch event handler can control which action items should process a request by changing the Enabled property of the action items before the dispatcher begins matching them to the request message.
Caution: Changing the Enabled property of an action during execution may cause unexpected results for subsequent requests. If the Web server application is a DLL that caches Web modules, the initial state will not be reinitialized for the next request. Use the BeforeDispatch event to ensure that all action items are correctly initialized to their appropriate starting states.
Only one of the action items can be the default action item. The default action item is selected by setting its Default property to True. When the Default property of an action item is set to True, the Default property for the previous default action item (if any) is set to False.
When the dispatcher searches its list of action items to choose one to handle a request, it stores the name of the default action item. If the request has not been fully handled when the dispatcher reaches the end of its list of action items, it executes the default action item.
The dispatcher does not check the PathInfo or MethodType of the default action item. The dispatcher does not even check the Enabled property of the default action item. Thus, you can make sure the default action item is only called at the very end by setting its Enabled property to False.
The default action item should be prepared to handle any request that is encountered, even if it is only to return an error code indicating an invalid URI or MethodType. If the default action item does not handle the request, no response is sent to the Web client.
Caution: Changing the Default property of an action during execution may cause unexpected results for the current request. If the Default property of an action that has already been triggered is set to True, that action will not be re-evaluated and the dispatcher will not trigger that action when it reaches the end of the action list.
The real work of the Web server application is performed by action items when they execute. When the Web dispatcher fires an action item, that action item can respond to the current request message in two ways:
If the action item's content can be generated by a single content producer, it is simplest to assign the content producer as the action item's Producer property. However, you can always access any content producer from the OnAction event handler as well. The OnAction event handler allows more flexibility, so that you can use multiple content producers, assign response message properties, and so on.
Both the content-producer component and the OnAction event handler can use any objects or runtime library methods to respond to request messages. They can access databases, perform calculations, construct or select HTML documents, and so on. For more information about generating response content using content-producer components, see "Generating the content of response messages".
An OnAction event handler can send the response back to the Web client by using the methods of the TWebResponse object. However, if no action item sends the response to the client, it will still get sent by the Web server application as long as the last action item to look at the request indicates that the request was handled.
You can respond to a request from a single action item, or divide the work up among several action items. If the action item does not completely finish setting up the response message, it must signal this state in the OnAction event handler by setting the Handled parameter to False.
If many action items divide up the work of responding to request messages, each setting Handled to False so that others can continue, make sure the default action item leaves the Handled parameter set to True. Otherwise, no response will be sent to the Web client.
When dividing the work among several action items, either the OnAction event handler of the default action item or the AfterDispatch event handler of the dispatcher should check whether all the work was done and set an appropriate error code if it is not.
When an HTTP request message is received by the Web server application, the headers of the client request are loaded into the properties of a TWebRequest object. In NSAPI and ISAPI applications, the request message is encapsulated by a TISAPIRequest object. Console CGI applications use TCGIRequest objects, and Windows CGI applications use TWinCGIRequest objects.
The properties of the request object are read-only. You can use them to gather all of the information available in the client request.
Most properties in a request object contain information about the request that comes from the HTTP request header. Not every request supplies a value for every one of these properties. Also, some requests may include header fields that are not surfaced in a property of the request object, especially as the HTTP standard continues to evolve. To obtain the value of a request header field that is not surfaced as one of the properties of the request object, use the GetFieldByName method.
The full target of the request message is given by the URL property. Usually, this is a URL that can be broken down into the protocol (HTTP), Host (server system), ScriptName (server application), PathInfo (location on the host), and Query.
Each of these pieces is surfaced in its own property. The protocol is always HTTP, and the Host and ScriptName identify the Web server application. The dispatcher uses the PathInfo portion when matching action items to request messages. The Query is used by some requests to specify the details of the requested information. Its value is also parsed for you as the QueryFields property.
The request also includes several properties that provide information about where the request originated. These include everything from the e-mail address of the sender (the From property), to the URI where the message originated (the Referer or RemoteHost property). If the request contains any content, and that content does not arise from the same URI as the request, the source of the content is given by the DerivedFrom property. You can also determine the IP address of the client (the RemoteAddr property), and the name and version of the application that sent the request (the UserAgent property).
The Method property is a string describing what the request message is asking the server application to do. The HTTP 1.1 standard defines the following methods:
The Method property may indicate any other method that the Web client requests of the server.
The Web server application does not need to provide a response for every possible value of Method. The HTTP standard does require that it service both GET and HEAD requests, however.
The MethodType property indicates whether the value of Method is GET (mtGet), HEAD (mtHead), POST (mtPost), PUT (mtPut) or some other string (mtAny). The dispatcher matches the value of the MethodType property with the MethodType of each action item.
The Accept property indicates the media types the Web client will accept as the content of the response message. The IfModifiedSince property specifies whether the client only wants information that has changed recently. The Cookie property includes state information (usually added previously by your application) that can modify the response.
Most requests do not include any content, as they are requests for information. However, some requests, such as POST requests, provide content that the Web server application is expected to use. The media type of the content is given in the ContentType property, and its length in the ContentLength property. If the content of the message was encoded (for example, for data compression), this information is in the ContentEncoding property. The name and version number of the application that produced the content is specified by the ContentVersion property. The Title property may also provide information about the content.
In addition to the header fields, some request messages include a content portion that the Web server application should process in some way. For example, a POST request might include information that should be added to a database maintained by the Web server application.
The unprocessed value of the content is given by the Content property. If the content can be parsed into fields separated by ampersands (&), a parsed version is available in the ContentFields property.
When the Web server application creates a TWebRequest object for an incoming HTTP request message, it also creates a corresponding TWebResponse object to represent the response message that will be sent in return. In NSAPI and ISAPI applications, the response message is encapsulated by a TISAPIResponse object. Console CGI applications use TCGIResponse objects, and Windows CGI applications use TWinCGIResponse objects.
The action items that generate the response to a Web client request fill in the properties of the response object. In some cases, this may be as simple as returning an error code or redirecting the request to another URI. In other cases, this may involve complicated calculations that require the action item to fetch information from other sources and assemble it into a finished form. Most request messages require some response, even if it is only the acknowledgment that a requested action was carried out.
Most of the properties of the TWebResponse object represent the header information of the HTTP response message that is sent back to the Web client. An action item sets these properties from its OnAction event handler.
Not every response message needs to specify a value for every one of the header properties. The properties that should be set depend on the nature of the request and the status of the response.
Every response message must include a status code that indicates the status of the response. You can specify the status code by setting the StatusCode property. The HTTP standard defines a number of standard status codes with predefined meanings. In addition, you can define your own status codes using any of the unused possible values.
Each status code is a three-digit number where the most significant digit indicates the class of the response, as follows:
Associated with each status code is a string that explains the meaning of the status code. This is given by the ReasonString property. For predefined status codes, you do not need to set the ReasonString property. If you define your own status codes, you should also set the ReasonString property.
When the status code is in the 300-399 range, the client must perform further action before the Web server application can complete its request. If you need to redirect the client to another URI, or indicate that a new URI was created to handle the request, set the Location property. If the client must provide a password before you can proceed, set the WWWAuthenticate property.
Some of the response header properties describe the capabilities of the Web server application. The Allow property indicates the methods to which the application can respond. The Server property gives the name and version number of the application used to generate the response. The Cookies property can hold state information about the client's use of the server application which is included in subsequent request messages.
Several properties describe the content of the response. ContentType gives the media type of the response, and ContentVersion is the version number for that media type. ContentLength gives the length of the response. If the content is encoded (such as for data compression), indicate this with the ContentEncoding property. If the content came from another URI, this should be indicated in the DerivedFrom property. If the value of the content is time-sensitive, the LastModified property and the Expires property indicate whether the value is still valid. The Title property can provide descriptive information about the content.
For some requests, the response to the request message is entirely contained in the header properties of the response. In most cases, however, action item assigns some content to the response message. This content may be static information stored in a file, or information that was dynamically produced by the action item or its content producer.
You can set the content of the response message by using either the Content property or the ContentStream property.
The Content property is a string. Delphi strings are not limited to text values, so the value of the Content property can be a string of HTML commands, graphics content such as a bit-stream, or any other MIME content type.
Use the ContentStream property if the content for the response message can be read from a stream. For example, if the response message should send the contents of a file, use a TFileStream object for the ContentStream property. As with the Content property, ContentStream can provide a string of HTML commands or other MIME content type. If you use the ContentStream property, do not free the stream yourself. The Web response object automatically frees it for you.
Note: If the value of the ContentStream property is not nil, the Content property is ignored.
If you are sure there is no more work to be done in response to a request message, you can send a response directly from an OnAction event handler. The response object provides two methods for sending a response: SendResponse and SendRedirect. Call SendResponse to send the response using the specified content and all the header properties of the TWebResponse object. If you only need to redirect the Web client to another URI, the SendRedirect method is more efficient.
If none of the event handlers send the response, the Web application object sends it after the dispatcher finishes. However, if none of the action items indicate that they have handled the response, the application will close the connection to the Web client without sending any response.
Delphi provides a number of objects to assist your action items in producing content for HTTP response messages. You can use these objects to generate strings of HTML commands that are saved in a file or sent directly back to the Web client. You can write your own content producers, deriving them from TCustomContentProducer or one of its descendants.
TCustomContentProducer provides a generic interface for creating any MIME type as the content of an HTTP response message. Its descendants include page producers and table producers:
Page producers (TPageProducer and its descendants) take an HTML template and convert it by replacing special HTML-transparent tags with customized HTML code. You can store a set of standard response templates that are filled in by page producers when you need to generate the response to an HTTP request message. You can chain page producers together to iteratively build up an HTML document by successive refinement of the HTML-transparent tags.
An HTML template is a sequence of HTML commands and HTML-transparent tags. An HTML-transparent tag has the form
<#TagName Param1=Value1 Param2=Value2 ...>
The angle brackets (< and >) define the entire scope of the tag. A pound sign (#) immediately follows the opening angle bracket (<) with no spaces separating it from the angle bracket. The pound sign identifies the string to the page producer as an HTML-transparent tag. The tag name immediately follows the pound sign with no spaces separating it from the pound sign. The tag name can be any valid identifier and identifies the type of conversion the tag represents.
Following the tag name, the HTML-transparent tag can optionally include parameters that specify details of the conversion to be performed. Each parameter is of the form ParamName=Value, where there is no space between the parameter name, the equals symbol (=) and the value. The parameters are separated by whitespace.
The angle brackets (< and >) make the tag transparent to HTML browsers that do not recognize the #TagName construct.
While you can create your own HTML-transparent tags to represent any kind of information processed by your page producer, there are several predefined tag names associated with values of the TTag data type. These predefined tag names correspond to HTML commands that are likely to vary over response messages. They are listed in the following table:
Any other tag name is associated with tgCustom. The page producer supplies no built-in processing of the predefined tag names. They are simply provided to help applications organize the conversion process into many of the more common tasks.
Note: The predefined tag names are case insensitive.
Page producers provide you with many choices in how to specify the HTML template. You can set the HTMLFile property to the name of a file that contains the HTML template. You can set the HTMLDoc property to a TStrings object that contains the HTML template. If you use either the HTMLFile property or the HTMLDoc property to specify the template, you can generate the converted HTML commands by calling the Content method.
In addition, you can call the ContentFromString method to directly convert an HTML template that is a single string which is passed in as a parameter. You can also call the ContentFromStream method to read the HTML template from a stream. Thus, for example, you could store all your HTML templates in a memo field in a database, and use the ContentFromStream method to obtain the converted HTML commands, reading the template directly from a TBlobStream object.
The page producer converts the HTML template when you call one of its Content methods. When the Content method encounters an HTML-transparent tag, it triggers the OnHTMLTag event. You must write an event handler to determine the type of tag encountered, and to replace it with customized content.
If you do not create an OnHTMLTag event handler for the page producer, HTML-transparent tags are replaced with an empty string.
A typical use of a page producer component uses the HTMLFile property to specify a file containing an HTML template. The OnAction event handler calls the Content method to convert the template into a final HTML sequence:
procedure WebModule1.MyActionEventHandler(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin PageProducer1.HTMLFile := 'Greeting.html'; Response.Content := PageProducer1.Content; end;
Greeting.html is a file that contains this HTML template:
<HTML> <HEAD><TITLE>Our brand new web site</TITLE></HEAD> <BODY> Hello <#UserName>! Welcome to our web site. </BODY> </HTML>
The OnHTMLTag event handler replaces the custom tag (<#UserName>) in the HTML during execution:
procedure WebModule1.PageProducer1HTMLTag(Sender : TObject;Tag: TTag; const TagString: string; TagParams: TStrings; var ReplaceText: string); begin if CompareText(TagString,'UserName') = 0 then ReplaceText := TPageProducer(Sender).Dispatcher.Request.Content; end;
If the content of the request message was the string Mr. Ed, the value of Response.Content would be
<HTML> <HEAD><TITLE>Our brand new web site</TITLE></HEAD> <BODY> Hello Mr. Ed! Welcome to our web site. </BODY> </HTML>
Note: This example uses an OnAction event handler to call the content producer and assign the content of the response message. You do not need to write an OnAction event handler if you assign the page producer's HTMLFile property at design time. In that case, you can simply assign PageProducer1 as the value of the action item's Producer property to accomplish the same effect as the OnAction event handler above.
The replacement text from an OnHTMLTag event handler need not be the final HTML sequence you want to use in the HTTP response message. You may want to use several page producers, where the output from one page producer is the input for the next.
The simplest way is to chain the page producers together is to associate each page producer with a separate action item, where all action items have the same PathInfo and MethodType. The first action item sets the content of the Web response message from its content producer, but its OnAction event handler makes sure the message is not considered handled. The next action item uses the ContentFromString method of its associated producer to manipulate the content of the Web response message, and so on. Action items after the first one use an OnAction event handler such as the following:
procedure WebModule1.Action2Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin Response.Content := PageProducer2.ContentFromString(Response.Content); end;
For example, consider an application that returns calendar pages in response to request messages that specify the month and year of the desired page. Each calendar page contains a picture, followed by the name and year of the month between small images of the previous month and next months, followed by the actual calendar. The resulting image looks something like this:
The general form of the calendar is stored in a template file. It looks like this:
<HTML> <Head></HEAD> <BODY> <#MonthlyImage> <#TitleLine><#MainBody> </BODY> </HTML>
The OnHTMLTag event handler of the first page producer looks up the month and year from the request message. Using that information and the template file, it does the following:
The OnHTMLTag event handler of the next page producer uses the content produced by the first page producer, and replaces the <#Image Month=January Year=1997> tag with the appropriate HTML <IMG> tag. Yet another page producer resolves the #Calendar tags with appropriate HTML tables.
The response to an HTTP request message may include information taken from a database. Specialized content producers on the Internet palette page can generate the HTML to represent the records from a database in an HTML table.
As an alternate approach, special components on the Web MIDAS page of the component palette let you build Web servers that are part of a multi-tiered database application. See "Building Web applications using InternetExpress" for details.
Both console CGI applications and Win-CGI applications are launched in response to HTTP request messages. When working with databases in these types of applications, you can use the default session to manage your database connections, because each request message has its own instance of the application. Each instance of the application has its own distinct, default session.
When writing an ISAPI application or an NSAPI application, however, each request message is handled in a separate thread of a single application instance. To prevent the database connections from different threads from interfering with each other, you must give each thread its own session.
Each request message in an ISAPI or NSAPI application spawns a new thread. The Web module for that thread is generated dynamically at runtime. Add a TSession object to the Web module to handle the database connections for the thread that contains the Web module.
Separate instances of the Web module are generated for each thread at runtime. Each of those modules contains the session object. Each of those sessions must have a separate name, so that the threads that handle separate request messages do not interfere with each other's database connections. To cause the session objects in each module to dynamically generate unique names for themselves, set the AutoSessionName property of the session object. Each session object will dynamically generate a unique name for itself and set the SessionName property of all datasets in the module to refer to that unique name. This allows all interaction with the database for each request thread to proceed without interfering with any of the other request messages. For more information on sessions, see "Managing database sessions."
Specialized Content producer components on the Internet palette page supply HTML commands based on the records of a dataset. There are two types of data-aware content producers:
Dataset page producers work like other page producer components: they convert a template that includes HTML-transparent tags into a final HTML representation. They include the special ability, however, of converting tags that have a tagname which matches the name of a field in a dataset into the current value of that field. For more information about using page producers in general, see "Using page producer components".
To use a dataset page producer, add a TDataSetPageProducer component to your web module and set its DataSet property to the dataset whose field values should be displayed in the HTML content. Create an HTML template that describes the output of your dataset page producer. For every field value you want to display, include a tag of the form
<#FieldName>
in the HTML template, where FieldName specifies the name of the field in the dataset whose value should be displayed.
When your application calls the Content, ContentFromString, or ContentFromStream method, the dataset page producer substitutes the current field values for the tags that represent fields.
The Internet palette page includes two components that create an HTML table to represent the records of a dataset:
Using either of the two table producers, you can customize the appearance of a resulting HTML table by specifying properties for the table's color, border, separator thickness, and so on. To set the properties of a table producer at design time, double-click the table producer component to display the Response Editor dialog.
Table producers use the THTMLTableAttributes object to describe the visual appearance of the HTML table that displays the records from the dataset. The THTMLTableAttributes object includes properties for the table's width and spacing within the HTML document, and for its background color, border thickness, cell padding, and cell spacing. These properties are all turned into options on the HTML <TABLE> tag created by the table producer.
At design time, specify these properties using the Object Inspector. Select the table producer object in the Object Inspector and expand the TableAttributes property to access the display properties of the THTMLTableAttributes object.
You can also specify these properties programmatically at runtime.
Similar to the table attributes, you can specify the alignment and background color of cells in the rows of the table that display data. The RowAttributes property is a THTMLTableRowAttributes object.
At design time, specify these properties using the Object Inspector by expanding the RowAttributes property. You can also specify these properties programmatically at runtime.
You can also adjust the number of rows shown in the HTML table by setting the MaxRows property.
If you know the dataset for the table at design time, you can use the Columns editor to customize the columns' field bindings and display attributes. Select the table producer component, and right-click. From the context menu, choose the Columns editor. This lets you add, delete, or rearrange the columns in the table. You can set the field bindings and display properties of individual columns in the Object Inspector after selecting them in the Columns editor.
If you are getting the name of the dataset from the HTTP request message, you can't bind the fields in the Columns editor at design time. However, you can still customize the columns programmatically at runtime, by setting up the appropriate THTMLTableColumn objects and using the methods of the Columns property to add them to the table. If you do not set up the Columns property, the table producer creates a default set of columns that match the fields of the dataset and specify no special display characteristics.
You can embed the HTML table that represents your dataset in a larger document by using the Header and Footer properties of the table producer. Use Header to specify everything that comes before the table, and Footer to specify everything that comes after the table.
You may want to use another content producer (such as a page producer) to create the values for the Header and Footer properties.
If you embed your table in a larger document, you may want to add a caption to the table. Use the Caption and CaptionAlignment properties to give your table a caption.
TDataSetTableProducer is a table producer that creates an HTML table for a dataset. Set the DataSet property of TDataSetTableProducer to specify the dataset that contains the records you want to display. You do not set the DataSource property, as you would for most data-aware objects in a conventional database application. This is because TDataSetTableProducer generates its own data source internally.
You can set the value of DataSet at design time if your Web application always displays records from the same dataset. You must set the DataSet property at runtime if you are basing the dataset on the information in the HTTP request message.
You can produce an HTML table to display the results of a query, where the parameters of the query come from the HTTP request message. Specify the TQuery object that uses those parameters as the Query property of a TQueryTableProducer component.
If the request message is a GET request, the parameters of the query come from the Query fields of the URL that was given as the target of the HTTP request message. If the request message is a POST request, the parameters of the query come from the content of the request message.
When you call the Content method of TQueryTableProducer, it runs the query, using the parameters it finds in the request object. It then formats an HTML table to display the records in the resulting dataset.
As with any table producer, you can customize the display properties or column bindings of the HTML table, or embed the table in a larger HTML document.
Debugging Web server applications presents some unique problems, because they run in response to messages from a Web server. You can not simply launch your application from the IDE, because that leaves the Web server out of the loop, and your application will not find the request message it is expecting. How you debug your Web server application depends on its type.
ISAPI and NSAPI applications are actually DLLs that contain predefined entry points. The Web server passes request messages to the application by making calls to these entry points. You will need to set your application's run parameters to launch the server. Set up your breakpoints so that when the server passes a request message to your DLL, you hit one of your breakpoints, and can debug normally.
Note: Before launching the Web server using your application's run parameters, make sure that the server is not already running.
Under Windows NT, you must have the correct user rights to debug a DLL. In the User Manager, add your name to the lists granting rights for
To debug a Web server application using Microsoft IIS server (version 3 or earlier), choose Run|Parameters and set your application's run parameters as follows:
Host Application: c:\winnt\system32\inetsrv\inetinfo.exe Run Parameters: -e w3svc
This starts the IIS Server and allows you to debug your ISAPI DLL.
Note: Your directory path for inetinfo.exe may differ from the example.
If you are using version 4 or later, you must first make some changes to the Registry and the IIS Administration service:
{61738644-F196-11D0-9953-00C04FD919C1} // IIS WAMREG admin
Service
{9F0BD3A0-EC01-11D0-A6A0-00A0C922E752} // IIS Admin Crypto
Extension
{A9E69610-B80D-11D0-B9B9-00A0C922E750} // IISADMIN Service
In addition, under the AppID node, remove the "RunAs" keyword from the first two of these subkeys. To the last subkey listed, add "Interactive User" as the value of the "RunAs" keyword.
c:\\winnt\\system32\\inetsrv\\inetinfo.exe -e w3svc
(the path for inetinfo.exe may differ for your system).
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\IISADMIN] [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\MSDTC] [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC]
Now you are ready to debug in the same way as when using IIS version 3 or earlier. That is, choose Run|Parameters and set your application's run parameters as follows:
Host Application: c:\winnt\system32\inetsrv\inetinfo.exe Run Parameters: -e w3svc
Note: When you have finished debugging, you will need to back out all the Registry changes you made in steps 2 through 4.
Another approach you can take when using IIS is to configure your Web directory as an MTS Library package. You can then debug your ISAPI dll by running it under MTS.
To configure the Web directory as an MTS Library package, use the following steps:
The previous steps configure your Web directory. After doing so, you can debug your ISAPI DLL as follows:
c:\winnt\system32\mtx.exe
In the parameters field, paste the name of the package, and then surround it with double quotes and precede the quoted string with /p:. The resulting parameters field should look similar to the following:
/p:"IIS-{Default Web Site//ROOT/WEBPUB/DEMO}"
Note that there should not be a space between the colon and the package name.
Tip: When the Web directory is installed as an MTS package, you can also use MTS to easily shut down the DLL. Just expand the Microsoft Transaction Server tree in the Internet Service Manager so that you can see the items under the "Packages Installed" node. Right click on the package with the same suffix as the Web directory and choose Shut Down.
To debug a Web server application using Personal Web Server, set your application's run parameters as follows:
Host Application: c:\Program Files\websvc\system\inetsw95.exe Run Parameters: -w3svc
This starts the Personal Web Server and allows you to debug your ISAPI DLL.
Note: Your directory path for inetsw95.exe may differ from the example.
Before using Web server applications on Netscape servers, you must make certain configuration changes.
First, copy the ISAPITER.DLL file (from the Bin directory) into the C:\Netscape\Server\Nsapi\Examples directory. (Your directory path may differ.)
Next, make the following modifications to the server configuration files located in the C:\Netscape\Server\Httpd-<servername>\Config directory.
Init funcs="handle_isapi,check_isapi,log_isapi" fn="load_modules"
shlib="c:/netscape/server/nsapi/examples/ISAPIter.dll"
Init fn=load-types mime-types=mime.types
NameTrans from="/scripts" fn="pfx2dir" dir="C:/Netscape/Server/docs/scripts"
name="isapi"
NameTrans fn=document-root root="C:/Netscape/Server/docs"
<Object name="isapi"> PathCheck fn="check_isapi" ObjectType fn="force-type" type="magnus-internal/isapi" Service fn="handle_isapi" </Object>
type=magnus-internal/isapi exts=dll
Note: Line breaks are included in steps 1 and 2 above only to enhance readability. Do not type carriage-returns when you place these lines in configuration files.
To debug a Web server application using Netscape Fast Track server, set the application's run parameters as follows:
Host Application: c:\Netscape\server\bin\httpd\httpd.exe Run Parameters: c:\Netscape\server\httpd-<servername>\config
This starts the server and indicates to the server where the configuration files are located.
It is more difficult to debug CGI and Win-CGI applications, because the application itself must be launched by the Web server.
For Win-CGI applications, you can simulate the server by manually writing the configuration settings file that contains the request information. Then launch the Win-CGI application, passing the location of the file containing the client information and the location of a file that the Win-CGI program should use to create content. You can then debug normally.
Another approach you can take with both CGI and Win-CGI applications is first to create and debug your application as an ISAPI or NSAPI application. Once your ISAPI or NSAPI application is working smoothly, convert it to a CGI or Win-CGI application. To convert your application, use the following steps:
1. Right-click the Web module and choose Add To Repository.
2. In the Add To Repository dialog, give your Web module a title, text description, repository page (probably Data Modules), author name, and icon.
3. Choose OK to save your web module as a template.
4. From the main menu, choose File|New and select Web Server Application. In the New Web Server Application dialog, choose CGI or Win-CGI, as appropriate.
5. Delete the automatically generated Web Module.
6. From the main menu, choose File | New and select the template you saved in step 3. This will be on the page you specified in step 2.
CGI and Win-CGI applications are simpler than ISAPI and NSAPI applications. Each instance of a CGI or Win-CGI application must handle only a single thread. Thus, these applications do not encounter the multi-threading issues that ISAPI and NSAPI applications must deal with. They also are immune to the problems that can arise from the caching of Web modules in ISAPI and NSAPI applications.
pubsweb@inprise.com
Copyright © 1999, Inprise Corporation. All rights reserved.