Delphi Programming Guide
Delphi Programmer 

Menu  Table of contents
Bookmark and Share

Part I - Foundations
  Chapter 1 Delphi 7 and Its IDE
  Chapter 2 The Delphi Programming Language
  Chapter 3 The Run-Time Library
  Chapter 4 Core Library classes
  Chapter 5 Visual Controls
  Chapter 6 Building the User Interface
  Chapter 7 Working with Forms
Part II - Delphi Object-Oriented Architectures
  Chapter 8 The Architecture of Delphi Applications
  Chapter 9 Writing Delphi Components
  Chapter 10 Libraries and Packages
  Chapter 11 Modeling and OOP Programming (with ModelMaker)
  Chapter 12 From COM to COM+
Part III - Delphi Database-Oriented Architectures
  Chapter 13 Delphi's Database Architecture
  Chapter 14 Client/Server with dbExpress
  Chapter 15 Working with ADO
  Chapter 16 Multitier DataSnap Applications
  Chapter 17 Writing Database Components
  Chapter 18 Reporting with Rave
Part IV - Delphi, the Internet, and a .NET Preview
  Chapter 19 Internet Programming: Sockets and Indy
  Chapter 20 Web Programming with WebBroker and WebSnap
  Chapter 21 Web Programming with IntraWeb
  Chapter 22 Using XML Technologies
  Chapter 23 Web Services and SOAP
  Chapter 24 The Microsoft .NET Architecture from the Delphi Perspective
  Chapter 25 Delphi for .NET Preview: The Language and the RTL
  Appendix A Extra Delphi Tools by the Author
  Appendix B Extra Delphi Tools from Other Sources
  Appendix C Free Companion Books on Delphi
  List of Figures    
  List of tables    
  List of Listings    
  List of Sidebars  

Previous Section Next Section

Delphi's WebBroker Technology

The CGI code snippet I've shown you demonstrates the plain, direct approach to this protocol. I could have provided similar low-level examples for ISAPI or Apache modules, but in Delphi it's more interesting to use the WebBroker technology. This comprises a class hierarchy within VCL and CLX (built to simplify server-side development on the Web) and a specific type of data modules called WebModules. Both the Enterprise Studio and Professional editions of Delphi include this framework (as opposed to the more advanced and newer WebSnap framework, which is available only in the Enterprise Studio version).

Using the WebBroker technology, you can begin developing an ISAPI or CGI application or an Apache module easily. On the first page (New) of the New Items dialog box, select the Web Server Application icon. The subsequent dialog box will offer you a choice among ISAPI, CGI, Apache 1 or 2 module, and the Web App Debugger:

In each case, Delphi will generate a project with a WebModule, which is a non-visual container similar to a data module. This unit will be identical, regardless of the project type; only the main project file changes. For a CGI application, it will look like this:

program Project2;
  Unit1 in 'Unit1.pas' {WebModule1: TWebModule};
{$R *.res}
  Application.CreateForm(TWebModule1, WebModule1);

Although this is a console CGI program, the code looks similar to that of a standard Delphi application. However, it uses a trick—the Application object used by this program is not the typical global object of class TApplication but an object of a new class. This Application object is of class TCGIApplication or another class derived from TWebApplication, depending on your web project type.

The most important operations take place in the WebModule. This component derives from TCustomWebDispatcher, which provides support for all the input and output of your programs. The TCustomWebDispatcher class defines Request and Response properties, which store the client request and the response you're going to send back to the client. Each of these properties is defined using a base abstract class (TWebRequest and TWebResponse), but an application initializes them using a specific object (such as the TISAPIRequest and TISAPIResponse subclasses). These classes make available all the information passed to the server, so you have a single approach to accessing all the information. The same is true of a response, which is easy to manipulate. The key advantage of this approach is that the code written with WebBroker is independent of the type of application (CGI, ISAPI, Apache module); you'll be able to move from one to the other, modifying the project file or switching to another one, but you won't need to modify the code written in a WebModule.

This is the structure of Delphi's framework. To write the application code, you can use the Actions editor in the WebModule to define a series of actions (stored in the Actions array property) depending on the pathname of the request:

Click To expand

This pathname is a portion of the CGI or ISAPI application's URL, which comes after the program name and before the parameters, such as path1 in the following URL:

By providing different actions, your application can easily respond to requests with different pathnames, and you can assign a different producer component or call a different OnAction event handler for every possible pathname. Of course, you can omit the pathname to handle a generic request. Also consider that instead of basing your application on a WebModule, you can use a plain data module and add a WebDispatcher component to it. This is a good approach if you want to turn an existing Delphi application into a web server extension.


The WebModule inherits from the base WebDispatcher class and doesn't require it as a separate component. Unlike WebSnap applications, WebBroker programs cannot have multiple dispatchers or multiple web modules. Also note that the actions of the WebDispatcher have nothing to do with the actions stored in an ActionList or ActionManager component.

When you define the accompanying HTML pages that launch the application, the links will make page requests to the URLs for each of those paths. Having a single library that can perform different operations depending on a parameter (in this case, the pathname) allows the server to keep a copy of this library in memory and respond much more quickly to user requests. The same is partially true for a CGI application: The server has to run several instances but can cache the file and make it available more quickly.

In the OnAction event, you write the code to specify the response to a given request, the two main parameters passed to the event handler. Here is an example:

procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
  Response.Content :=
    '<html><head><title>Hello Page</title></head><body>' +
    '<h1>Hello</h1>' +
    '<hr><p><i>Page generated by Marco</i></p></body></html>';

In the Content property of the Response parameter, you enter the HTML code you want users to see. The only drawback of this code is that the output in a browser will be correctly displayed on multiple lines, but looking at the HTML source code, you'll see a single line corresponding to the entire string. To make the HTML source code more readable by splitting it onto multiple lines, you can insert the #13 newline character (or, even better, the cross-platform sLineBreak value).

To let other actions handle this request, you set the last parameter, Handled, to False. The default value is True; if this value is set, then once you've handled the request with your action, the WebModule assumes you're finished. Most of a web application's code will be in the OnAction event handlers for the actions defined in the WebModule container. These actions receive a request from the client and return a response using the Request and Response parameters.

When you're using the producer components, your OnAction event often returns as Response.Content the Content of the producer component, with an assignment operation. You can shortcut this code by assigning a producer component to the Producer property of the action, and you won't need to write these event handlers any more (but don't do both things, because doing so might get you into trouble).


As an alternative to the Producer property, you can use the ProducerContent property. This property allows you to connect custom producer classes that don't inherit from the TCustomContentProducer class but implement the IProduceContent interface. The ProducerContent property is almost an interface property: It behaves the same way, but this behavior is due to its property editor and is not based on Delphi's support for interfaced properties.

Debugging with the Web App Debugger

Debugging web applications written in Delphi is often difficult. You cannot simply run the program and set breakpoints in it, but must convince the web server to run your CGI program or library within the Delphi debugger. You can do so by indicating a host application in Delphi's Run Parameters dialog box, but this approach implies letting Delphi run the web server (which is often a Windows service, not a stand-alone program).

To solve these issues, Borland has developed a specific Web App Debugger program. This program, which is activated by the corresponding item on the Tools menu, is a web server that waits for requests on a port you can set up (1024 by default). When a request arrives, the program can forward it to a stand-alone executable. In Delphi 6, this communication was based on COM techniques; in Delphi 7 it is based on Indy sockets. In both cases, you can run the web server application from within the Delphi IDE, set all the breakpoints you need, and then (when the program is activated through the Web App Debugger) debug the program as you would a plain executable file.

The Web App Debugger does a good job of logging all the received requests and the responses returned to the browser. The program also has a Statistics page that tracks the time required for each response, allowing you to test the efficiency of an application in different conditions. Another new feature of the Web App Debugger in Delphi 7 is that it is now a CLX application instead of a VCL application. This user interface change and the conversion from COM to sockets were both done to make the Web App Debugger available in Kylix.


Because the Web App Debugger uses Indy sockets, your application will receive frequent exceptions of type EidConnClosedGracefully. For this reason, this exception is automatically disabled in all Delphi 7 projects.

By using the corresponding option in the New Web Server Application dialog, you can easily create a new application compatible with the debugger. This option defines a standard project, which creates both a main form and a web module. The (useless) form includes code for providing initialization code and adding the application to the Windows Registry:


The Web App Debugger uses this information to get a list of the available programs. It does so when you use the default URL for the debugger, indicated in the form as a link, as you can see (for example) in Figure 20.2. The list includes all the registered servers, not only those that are running, and can be used to activate a program. This is not a good idea, though, because you have to run the program within the Delphi IDE to be able to debug it. (Notice that you can expand the list by clicking View Details; this view includes a list of the executable files and many other details.)

Click To expand
Figure 20.2: A list of applications registered with the Web App Debugger is displayed when you hook to its home page.

The data module for this type of project includes initialization code:

uses WebReq;
  if WebRequestHandler <> nil then
    WebRequestHandler.WebModuleClass := TWebModule2;

The Web App Debugger should be used only for debugging. To deploy the application, you should use one of the other options. You can create the project files for another type of web server program and add to the project the same web module as the debug application.

The reverse process is slightly more complex. To debug an existing application, you have to create a program of this type, remove the web module, add the existing one, and patch it by adding a line to set the WebModuleClass of the WebRequestHandler, as in the preceding code snippet.


Although in most cases you'll be able to move a program from one web technology to another, this is not always the case. For example, in the CustQueP example (discussed later), I had to avoid the request's ScriptName property (which is fine for a CGI program) and use the InternalScriptName property instead.

There are two other interesting elements involved in using the Web App Debugger. First, you can test your program without having a web server installed and without having to tweak its settings. In other words, you don't have to deploy your programs to test them— you can try them right away. Second, instead of doing early development of an application as CGI, you can begin experimenting with a multithreaded architecture immediately, without having to deal with the loading and unloading of libraries (which often implies shutting down the web server and possibly even the computer).

Building a Multipurpose WebModule

To demonstrate how easily you can build a feature-rich server-side application using Delphi's support, I created the BrokDemo example. I built this example using the Web App Debugger technology, but it should be relatively simple to recompile as a CGI or a web server library.

A key element of the WebBroker example is the list of actions. The actions can be managed in the Actions editor or directly in the Object TreeView. Actions are also visible in the Designer page of the editor, so you can graphically see their relationship with database objects. If you examine the source code, you'll notice that every action has a specific name. I also gave meaningful names to the OnAction event handlers. For instance, TimeAction as a method name is much more understandable than the WebModule1WebActionItem1Action name automatically generated by Delphi.

Every action has a different pathname, and one is marked as a default and executed even if no pathname is specified. The first interesting idea in this program is the use of two PageProducer components, PageHead and PageTail, which are used for the initial and final portion of every page. Centralizing this code makes it easier to modify, particularly if it is based on external HTML files. The HTML produced by these components is added at the beginning and the end of the resulting HTML in the web module's OnAfterDispatch event handler:

procedure TWebModule1.WebModule1AfterDispatch(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
  Response.Content := PageHead.Content + Response.Content + PageTail.Content;

You add the initial and final HTML at the end of the page generation because doing so allows the components to produce the HTML as if they were making all of it. Starting with HTML in the OnBeforeDispatch event means that you cannot directly assign the producer components to the actions, or the producer component will override the Content you've already provided in the response.

The PageTail component includes a custom tag for the script name, replaced by the following code, which uses the current request object available in the web module:

procedure TWebModule1.PageTailHTMLTag(Sender: TObject; Tag: TTag;
  const TagString: String; TagParams: TStrings; var ReplaceText: String);
  if TagString = 'script' then
    ReplaceText := Request.ScriptName;

This code is activated to expand the <#script> tag of the PageTail component's HTMLDoc property.

The code for the time and date actions is straightforward. The really interesting part begins with the Menu path, which is the default action. In its OnAction event handler, the application uses a for loop to build a list of the available actions (using their names without the first two letters, which are always Wa in this example), providing a link to each of them with an anchor (an <a> tag):

procedure TWebModule1.MenuAction(Sender: TObject; Request: TWebRequest;
  Response: TWebResponse; var Handled: Boolean);
  I: Integer;
  Response.Content := '<h3>Menu</h3><ul>'#13;
  for I := 0 to Actions.Count - 1 do
    Response.Content := Response.Content + '<li> <a href="' +
      Request.ScriptName + Action[I].PathInfo + '"> ' +
      Copy (Action[I].Name, 3, 1000) + '</a>'#13;
  Response.Content := Response.Content + '</ul>';

The BrokDemo example also provides users with a list of the system settings related to the request, which is useful for debugging. It is also instructive to learn how much information (and exactly what information) the HTTP protocol transfers from a browser to a web server and vice versa. To produce this list, the program looks for the value of each property of the TWebRequest class, as this snippet demonstrates:

procedure TWebModule1.StatusAction(Sender: TObject; Request: TWebRequest;
  Response: TWebResponse; var Handled: Boolean);
  I: Integer;
  Response.Content := '<h3>Status</h3>'#13 +
    'Method: ' + Request.Method + '<br>'#13 +
    'ProtocolVersion: ' + Request.ProtocolVersion + '<br>'#13 +
    'URL: ' + Request.URL + '<br>'#13 +
    'Query: ' + Request.Query + '<br>'#13 + ...

Dynamic Database Reporting

The BrokDemo example defines two more actions, indicated by the /table and /record pathnames. For these two last actions, the program produces a list of names and then displays the details of one record, using a DataSetTableProducer component to format the entire table and a DataSetPageProducer component to build the record view. Here are the properties of these two components:

object DataSetTableProducer1: TDataSetTableProducer
  DataSet = dataEmployee
  OnFormatCell = DataSetTableProducer1FormatCell
object DataSetPage: TDataSetPageProducer
  HTMLDoc.Strings = (
    '<h3>Employee: <#LastName></h3>'
    '<ul><li> Employee ID: <#EmpNo>'
    '<li> Name: <#FirstName> <#LastName>'
    '<li> Phone: <#PhoneExt>'
    '<li> Hired On: <#HireDate>'
    '<li> Salary: <#Salary></ul>')
  OnHTMLTag = PageTailHTMLTag
  DataSet = dataEmployee

To produce the entire table, you connect the DataSetTableProducer to the Producer property of the corresponding actions without providing a specific event handler. The table is made more powerful by adding internal links to the specific records. The following code is executed for each cell of the table but a link is created only for the first column and not for the first row (the one with the title):

procedure TWebModule1.DataSetTableProducer1FormatCell(Sender: TObject;
  CellRow, CellColumn: Integer; var BgColor: THTMLBgColor;
  var Align: THTMLAlign; var VAlign: THTMLVAlign;
  var CustomAttrs, CellData: String);
  if (CellColumn = 0) and (CellRow <> 0) then
    CellData := '<a href="' + ScriptName + '/record?LastName=' +
      dataEmployee['Last_Name'] + '&FirstName=' +
      dataEmployee['First_Name'] + '"> '
      + CellData + ' </a>';

You can see the result of this action in Figure 20.3. When the user selects one of the links, the program is called again, and it can check the QueryFields string list and extract the parameters from the URL. It then uses the values corresponding to the table fields used for the record search (which is based on the FindNearest call):

procedure TWebModule1.RecordAction(Sender: TObject; Request: TWebRequest;
  Response: TWebResponse; var Handled: Boolean);
  // go to the requested record
  dataEmployee.Locate ('LAST_NAME;FIRST_NAME',
    Request.QueryFields.Values['FirstName']]), []);
  // get the output
  Response.Content := Response.Content + DataSetPage.Content;
Click To expand
Figure 20.3: The output corresponding to the table path of the BrokDemo example, which produces an HTML table with internal hyperlinks

Queries and Forms

The previous example used some of the HTML producer components introduced earlier in this chapter. This group includes another component you haven't used yet: QueryTableProducer (for the BDE) and its sister SQL QueryTableProducer (for dbExpress). As you'll see in a moment, this component makes building even complex database programs a breeze.

Suppose you want to search for customers in a database. You might construct the following HTML form (embedded in an HTML table for better formatting):

<h4>Customer QueryProducer Search Form</h4>
<form action="<#script>/search" method="POST">
  <td><input type="text" name="State"></td></tr>
  <td><input type="text" name="Country"></td></tr>
  <td><center><input type="Submit"></center></td></tr>

As in Delphi, an HTML form hosts a series of controls. There are visual tools to help you design these forms, or you can manually enter the HTML code. The available controls include buttons, input text (or edit boxes), selections (or combo boxes), and input buttons (or radio buttons). You can define buttons as specific types, such as Submit or Reset, which imply standard behaviors. An important element of forms is the request method, which can be either POST (data is passed behind the scenes, and you receive it in the ContentFields property) or GET (data is passed as part of the URL, and you extract it from the QueryFields property).

You should notice a very important element in the form: the names of the input components (State and Country), which should match the parameters of a SQLQuery component:

SELECT Customer, State_Province,  Country
  State_Province = :State OR Country = :Country

This code is used in the CustQueP (customer query producer) example. To build it, I placed a SQLQuery component inside the WebModule and generated the field objects for it. In the same WebModule, I added a SQLQueryTableProducer component connected to the Producer property of the /search action. The program generates the proper response. When you activate the SQLQuery-TableProducer component by calling its Content function, it initializes the SQLQuery component by obtaining the parameters from the HTTP request. The component can automatically examine the request method and then use either the QueryFields property (if the request is a GET) or the ContentFields property (if the request is a POST).

One problem with using a static HTML form as you did earlier is that it doesn't tell you which states and countries you can search for. To address this issue, you can use a selection control instead of an edit control in the HTML form. However, if the user adds a new record to the database table, you'll need to update the element list automatically. As a final solution, you can design the ISAPI DLL to produce a form on-the-fly, and you can fill the selection controls with the available elements.

You'll generate the HTML for this page in the /form action, which you connect to a PageProducer component. The PageProducer contains the following HTML text, which embeds two special tags:

<h4>Customer QueryProducer Search Form</h4>
<form action=" <#script>/search" method="POST">
  <td><select name="State"><option></option><#State_Province></select></td></tr>
  <td><select name="Country"><option></option><#Country></select></td></tr>
  <td><center><input type="Submit"></center></td></tr>

You'll notice that the tags have the same name as some of the table's fields. When the PageProducer encounters one of these tags, it adds an <option> HTML tag for every distinct value of the corresponding field. Here's the OnTag event handler's code, which is generic and reusable:

procedure TWebModule1.PageProducer1HTMLTag(Sender: TObject; Tag: TTag;
  const TagString: String; TagParams: TStrings; var ReplaceText: String);
  ReplaceText := '';
  SQLQuery2.SQL.Add ('select distinct ' + TagString + ' from customer');
      while not SQLQuery2.EOF do
        ReplaceText := ReplaceText +
          '<option>' + SQLQuery2.Fields[0].AsString + '</option>'#13;
    ReplaceText := '{wrong field: ' + TagString + '}';

This method uses a second SQLQuery component, which I manually placed on the form and connected to a shared SQLConnection component. It produces the output shown in Figure 20.4.

Click To expand
Figure 20.4:  The form action of the CustQueP example produces an HTML form with a selection component dynamically updated to reflect the current status of the database.

This web server extension, like many others, allows the user to view the details of a specific record. As in the previous example, you can accomplish this by customizing the output of the first column (column zero), which is generated by the QueryTableProducer component:

procedure TWebModule1.QueryTableProducer1FormatCell(
  Sender: TObject; CellRow, CellColumn: Integer;
  var BgColor: THTMLBgColor; var Align: THTMLAlign;
  var VAlign: THTMLVAlign; var CustomAttrs, CellData: String);
  if (CellColumn = 0) and (CellRow <> 0) then
    CellData := '<a href="' + Request.ScriptName + '/record?Company=' + CellData +
      '">' + CellData + '</a>'#13;
  if CellData = '' then
    CellData := '&nbsp;';

When you have an empty cell in an HTML table, most browsers render it without the border. For this reason, I added a non-breaking space symbol (&nbsp;) in each empty cell. You'll have to do this in each HTML table generated with Delphi's table producers.

The action for this link is /record, and you pass a specific element after the ? parameter (without the parameter name, which is slightly nonstandard). The code you use to produce the HTML tables for the records doesn't use the producer components as you've been doing; instead, it shows the data of every field in a custom-built table:

procedure TWebModule1.RecordAction(Sender: TObject; Request: TWebRequest;
  Response: TWebResponse; var Handled: Boolean);
  I: Integer;
  if Request.QueryFields.Count = 0 then
    Response.Content := 'Record not found'
    Query2.SQL.Add ('select * from customer ' +
      'where Company="' + Request.QueryFields.Values['Company'] + '"');
    Response.Content :=
      '<html><head><title>Customer Record</title></head><body>'#13 +
      '<h1>Customer Record: ' + Request.QueryFields[0] + '</h1>'#13 +
      '<table border>'#13;
    for I := 1 to Query2.FieldCount - 1 do
      Response.Content := Response.Content +
        '<tr><td>' + Query2.Fields [I].FieldName + '</td>'#13'<td>' +
        Query2.Fields [I].AsString + '</td></tr>'#13;
    Response.Content := Response.Content + '</table><hr>'#13 +
      // pointer to the query form
      '<a href="' + Request.ScriptName + '/form">' +
      ' Next Query </a>'#13 + '</body></html>'#13;

Working with Apache

If you plan to use Apache instead of IIS or another web server, you can take advantage of the CGI technology to deploy your applications on almost any web server. However, using CGI means reduced speed and some trouble handling state information (because you cannot keep any data in memory). This is a good reason to write an ISAPI application or a dynamic Apache module. Using Delphi's WebBroker technology, you can also easily compile the same code for both technologies, so that moving your program to a different web platform becomes much simpler. You can also recompile a CGI program or a dynamic Apache module with Kylix and deploy it on a Linux server.

As I've mentioned, Apache can run traditional CGI applications but also has a specific technology for keeping the server extension program loaded in memory at all times for faster response. To build such a program in Delphi, you can use the Apache Shared Module option in the New Web Server Application dialog box; choose Apache 1 or Apache 2, depending on the version of the web server you plan to use.


While D7 supports version 2.0.39 of Apache, it doesn't support the current popular version 2.0.40 due to a change in library interfaces. Use of version 2.0.39 is discouraged because it has a security problem. Information on how to fix the VCL code and make your modules compatible with Apache 2.0.40 and above have been posted by Borland R&D members on the newsgroups and are currently available also on Bob Swart's website at the URL

If you choose to create an Apache module, you end up with a library having this type of source code for its project:

library Apache1;
  ApacheWm in 'ApacheWm.pas' {WebModule1: TWebModule};
{$R *.res}
  apache_module name 'apache1_module';
  Application.CreateForm(TWebModule1, WebModule1);

Notice in particular the exports clause, which indicates the name used by Apache configuration files to reference the dynamic module. In the project source code, you can add two more definitions—the module name and the content type—in the following way:

ModuleName := 'Apache1_module';
ContentType:= 'Apache1-handler';

If you don't set these values, Delphi will assign them defaults, which are built adding the _module and -handler strings to the project name (resulting in the two names I used here).

An Apache module is generally not deployed within a script folder, but within the modules subfolder of the server (by default, c:\Program Files\Apache\modules). You have to edit the http.conf file, adding a line to load the module, as follows:

LoadModule apache1_module modules/apache1.dll

Finally, you must indicate when the module is invoked. The handler defined by the module can be associated with a given file extension (so that your module will process all the files having a given extension) or with a physical or virtual folder. In the latter case, the folder doesn't exist, but Apache pretends it is there. This is how you can set up a virtual folder for the Apache1 module:

<Location /Apache1>SetHandler Apache1-handler</Location>

Because Apache is inherently case sensitive (because of its Linux heritage), you also might want to add a second, lowercase virtual folder:

<Location /apache1>SetHandler Apache1-handler</Location>

Now you can invoke the sample application with the URL http://localhost/Apache1. A great advantage of using virtual folders in Apache is that a user doesn't really distinguish between the physical and dynamic portions of your site, as you can see by experimenting with the Apache1 example (which includes the code discussed here).

Previous Section Next Section



Copyright © 2004-2016 "Delphi Sources". Delphi Programming Guide
     Twitter     Facebook