WebBroker是一套VCL工具,可以帮助您建立Web服务器程序。WebBroker与Delphi企业版一同发布,也可单独购买并与Delphi专业版配合使用。WebBroker可用于建立Web服务器,支持ISAPI、NSAPI或CGI协议。ISAPI(Internet Services API)和NSAPI(Netscape Services API)通过TISAPIApplication Web应用程序组件来支持。CGI(Common Gateway Interface)通过TCGIApplication Web应用程序组件来支持。Web服务器Apache是通过新的TApacheApplication组件来支持。对于开发者来说,这意味着使用对大多数Internet服务器可用的通用协议编写Web程序更为容易。

本章我们将特别注重用于建立Web服务器的组件,它们位于Internet属性页上。所有的Web服务器都包含一个TWebModule组件,或者是一个TDataModule和一个TWebDispatcher组件。在讨论这些组件之前,我们先快速浏览一下HTML的基础知识。理解URL(Uniform Resource Locator)请求和HTML页面的基本结构是很有必要的,因为用户会调用Web服务器,而Web服务器要提供Web页面服务。

17.1  HTML基础

Web服务器是URL路径的一部分。WebBroker服务器将基于URL请求的内容和Web服务器的设计返回HTML页面。通常,这些响应都是以HTML文档的形式进行。文档可能会包括到其他Web站点或Web服务的超链接。

本节包含了对URL分解、HTML文档和可替换参数标记的简要综述,这些机制有助于返回动态Web页面。

17.1.1  URL(Uniform Resource Locator)

URL由协议标签、主机名、脚本或服务器程序、路径信息和一些由用户向服务器提供的查询信息构成。参见下面的代码和图17.1,可以看到一个URL的各个部分,该URL指向如图所示的Web服务器。

http://localhost/scripts/iserver.dll/runquery?CustNo=1645

本例中,所用协议为HTTP,即超文本传输协议(Hypertext Transfer Protocol)。HTTP可能是最常用的浏览器协议,但从第16章是可知,并非只有这一种可能(回忆第16章,可以知道有几种TCP/IP协议,像FTP和安全HTTP,都可以用作URL中的协议)。例子中的主机名是LocalHost,它代表客户机。LocalHost也是计算机名或IP地址127.0.0.1(127.0.0.1也称为回送IP地址)。主机名可以是任何IP地址或DNS表中的名字;例如www.microsoft.com、www.softconcepts.com或www.amazon.com。URL的脚本部分是可选

图17.1  URL在Web浏览器的地址栏中输入,也可

能出现在HTML文档中的HREF标记之后

的。对Web服务器而言,即包含服务器程序的文件夹。如果在Windows 2000系统下运行IIS或在PC上运行Peer Web Services,那么默认情况下脚本位于c:/inetpub/scripts目录(见图17.2)。服务管理器将虚拟路径脚本映射到物理上的目录。脚本后紧接着是服务器程序。本例的服务器程序是iserver.dll。例子中的路径信息由run-query表示。最后一部分信息示范了如何向服务器发送查询参数。在例子中,CustNo=1645将发送到服务器程序。

图17.2  Windows 2000 专业版系统中Scripts Properties对话框,

可以看到虚拟的脚本路径被映射到物理路径

注意:本例中所示的请求摘自Delphi中的例子iserver.dpr,该工程位于$(DELPHI)/Demos/WebServ/IIS文件夹中。上文中的请求是对biolife.db表发出的,该文件是Delphi附带的。

您可能已经熟悉协议标签加上主机名的请求类型。但如果您对电子商务有一定程度的了解,例如在amazon.com进行购物,您可能已经看到过另外一些URL请求类型。

注意:最熟悉的URL形如http://www.digitalblasphemy.com,在请求中不存在路径、脚本、查询和特定的页面信息。只涉及到Web服务器上的一个页面。Microsoft公司的Web服务器IIS在默认情况下返回default.asp页面,但可以进行配置以返回任何页面。http://www.softconcepts.com站点返回的页面是index.htm。默认页面是可配置的。管理和配置IIS或其他Web服务器已经超出了本书的范围;但在这方面有很多书籍可供参考。

如果给出了Web站点和路径来运行脚本(例如上文例子中的runquery)但没有找到,则Delphi的WebBroker组件允许指定默认路径。按照惯例,本章中的默认路径是/root。

17.1.2  基本的HTML结构

上一节中的请求是要求WebBroker服务器返回特定页面。如果提供查询信息,则页面内容可以由查询值来控制。可以通过设计,对用户屏蔽Web页面的细节,如CustNo,这是个好主意,把细节嵌入到HTML源文件中即可完成;而用户可通过输入URL直接发送请求。无论如何,基本的HTML页面的结构都是一致的。

提示:HTML(Hypertext Markup Language)中包括标记,标记语言的读者可以认为这些标记是指令。

注意:请记住,页面可能会非常复杂。其中可能包括Active Server Pages和JavaScript或VBScript,而且像FrontPage这样的页面设计工具可能会添加相对数量和种类的修饰。不考虑这些修饰,HTML页面包含了某些一致的元素。

基本的HTML页面由标记组成,它们定义了文档的结构。许多标记是对称的,包括开始标记和结束标记。例如,HTML文档以<html>标记开始,结束标记为</html>(请注意,/用于结束标记)。在文档标记之内是文档体标记<body>和</body>。通常所看到的文档内容是在文档体标记之内定义的。下面列出的代码使用<html>和<body>标记示范了框架性的Web页面,与本书开头的Hello World程序差不多。

<html>

<body>

Welcome to ValhallaTowerMaterial Defender!

</body>

</html>

上述Web页面是无法得到任何“本年度××Web站点”奖励的,但它确实示范了HTML的简单的和基本的特性。作为练习,打开notepad.exe并输入上面的代码。将文件保存为hello.htm。然后打开Web浏览器。单击File | Open并浏览hello.htm的位置,再单击OK。可以看到,浏览器中显示了文档体标记之间的文本(五、六年前这可是件了不起的事情)。还有许多标记以及使得这些标记易于使用的工具,但你还是可以使用简单的文本编辑器。下面示范了常用的其他标记,可以帮助您入门。

定制文档体

可以向文档体添加额外的特征。使用<bgcolor>标记,可以将页面的背景颜色指定为颜色名或十六进制数字。在文档体标记中,还可以指定背景图像。下面对文档体标记进行了修改,示范了背景颜色和背景图像的使用。

注意:本章中的HTML文档使用Notepad.exe生成,并在Internet Explorer 5.5上进行了测试。无法保证其他的特定Web浏览器或早期版本是否能够对这些超文本标记进行渲染。

<body bgcolor=#00FFFF>

<body background="bubbles.bmp">

背景颜色由颜色名或48比特的RGB(红、绿、蓝各16比特)颜色表示。颜色数字由三个十六进制数组成,各16比特。这里没有设置红色的比特位,而设置了所有的绿色和蓝色比特位。可以用颜色名cyan或blue-green来替换上述的颜色值。Background属性指向一个图形文件,该文件将在文档的背景上绘出。

有许多标记,一些标记有各种属性,特定的浏览器可能支持其中的全部或一部分。由于标记的种类和属性很多,因此需要使用Web页面设计程序如FrontPage或Hot Metal等(该程序与某些版本的Delphi捆绑发行)。

使用水平规则

具有3D效果的线由水平规则标记<hr>表示,该标记指示浏览器在HTML文档体中绘出一条具有浮雕效果的线。

行结束和段落标记

在HTML文档体中文本结束处的<br>将向文本插入一个硬回车。如果在文档体中使用<p> </p>标记,浏览器会在该位置创建段落。例如,

<html>

<body>

This is <p>some</p> text.

</body>

</html>

在Web页面中显示如下:

This is

Some

Text.

可以使用<br>、<p></p>以及<pre></pre>标记格式化文档中的文本。最后一对标记是预格式化标记,它表示文本块在Web页面中的显示方式与其书写方式相同。

使用标题

<title></title>标记可以为页面指定标题。标题标记通常位于<body>标记之前,在<html>开始标记之后。下面的代码示范了标题标记。

<html>

<title>Hello World!<title>

<body>

Welcome to ValhallaTowerMaterial Defender!

</body>

</html>

上述HTML文档将在浏览器的标题栏显示文本“Hello World!”。

添加超链接

<A HREF="path">页面上显示的文本</A>标记用于向HTML文档体中添加超链接。例如,

<A HREF="http://www.microsoft.com>Microsoft</A>

将显示带下划线的文字Microsoft,它是超链接。用户单击该超链接时,浏览器将重定向到http://www.microsoft.com。Web本身就是由数以百万计的页面组成,页面中可能包含指向其他页面的超链接。实际上,每个页面都可以想像成Web的一个点,而每个超链接都是万维网的一条线。请记住,超链接可能会指向Web服务,而服务再生成页面,我们在这里正是这样做的。

书签

书签标记表示为<A NAME="#text">text</A>,它可以作为Web页面的定位器,使用户可以在页面内移动。使用<HREF>标记既可以表示当前页面内的位置,也可表示另外的页面中的位置。下面是一些例子:

<A NAME="#location">Location Title</A>

<A HREF="http://www.mypage.com/index.html#location>Goto Index.htm

Location</A>

<A HREF="#location">location</A>

注意:当键入的URL中有空格时,请在URL中使用空格字符的值(ASCII字符32)。字面上是%20,在十六进制中20是十进制的2*16,即32;在URL中要使用空格之处插入%20即可。

上述例子的第一行定义了一个书签位置。可以注意到前缀#。在Web页面上会显示文本Location Title。第二个例子打开位于mypage.com的Web页面index.html,并定位到#location书签。最后一个例子在当前页面内移动,因此并未给出页面位置。

插入图像

可以在Web页面中插入各种图像,包括GIF、JPEG或BMP格式。<img src="图形文件路径">标记用于将图像嵌入到Web页面中。可以向图形标记加入高度和宽度属性,以限制图像的大小。alt属性用于指定Web页面在文本格式下用于替换图像的文本,其他情况下用于提示。border属性用于指定图像周围边界的宽度。这里有个例子:

<img src="data:image/bookimg.gif" height=150 width=100 border=1

alt="Building Delphi 6 Applications">

本例向Web页面嵌入了一幅GIF图像,150像素高、100像素宽、边界宽一个像素、提示为“Building Delphi 6 Applications”。当鼠标移动到图像上时,将显示包含alt文本的提示(在较新的浏览器中)。

格式化文本

HTML支持很多种文本格式,在一定程度上依赖于浏览器对特定边界的渲染能力。通常,在大多数浏览器中可以使用较为简单的标记。<i></i>标记表示标记之间的文本应该是斜体。与此相似,<b></b>标记表示其中的文本显示为黑体;<u></u>对其中的文本添加下划线;而<s></s>标记向其中的文本添加删除线。这里是一些例子:

<b>bold</b> yields bold

<i>italics</i> yields italics

<u>underline</u> yields underline

<s>strikethrough</s> yields strikethrough

文本格式化标记可以嵌入到其他种类的标记中,从而形成形式非常丰富的Web文档。

创建表格视图

有些浏览器支持帧。帧可以对Web文档进行嵌套,看起来像是单一的文档,这里并不讨论该机制。尽管许多浏览器支持帧,但许多Web站点是使用表格而不是帧制作的。<TABLE>标记可以将页面的空间组织起来并划分为区域。表格标记基本上支持对页面空间进行三维划分。<TABLE></TABLE>标记定义了表格的边界。<TR></TR>标记定义了表格中的一行,而<TD></TD>标记则定义了表格中一个单元。另外,您还可以指定表格头,它表示每一列的标题信息;同时,表格还以进行n维嵌套。可以想像到,这样的HTML文档看起来可能会复杂一些。关于Web页面设计有许多出色的书籍,我们在这里只介绍表格标记的基本应用。

定义表格  将HTML文档中的表格等价于电子表格或数据库表是有道理的。HTML表格标记可以将页面划分为统一分割的区域。一种用法是用来代表基于表格的数据,另外还可以用于划分页面的区域和数据。表格以<TABLE>标记开始,</TABLE>标记结束。表格也可以嵌套。例如,表格中的一个单元还可以包含另一个嵌套的表格。

表格还支持一些属性标记,用于定义表格的显示方式。包括背景颜色、表格宽度、单元内部占位空间、单元距离以及边界宽度等。宽度可以用像素数目或全部可用宽度的百分比来表示。下面列出的代码(其页面显示如图17.3所示)示范了如何在表格标记中使用各种属性标记。

注意:本小节只强调了下面代码中的表格标记。代码的其他方面将在其后的三个小节中讨论,我们将继续引用该代码。

<html>

<body>

<Table BGCOLOR=#88EE88" WIDTH="75%" CellPadding=10 CellSpacing=1

BORDER=1>

<Caption>Sample Table</Caption>

<TR><TH>Column1</TH><TH>Column 2</TH><TH>Column 3</TH></TR>

<TR><TD ALIGN=center>Col 1, Row 1</TD><TD ALIGN=center>Col 2,

Row 1</TD><TD ALIGN=center>Col 3, Row 1</TD>

<TR><TD ALIGN=center>Col 1, Row 2</TD><TD ALIGN=center>Col 2,

Row 2</TD><TD ALIGN=center>Col 3, Row 2</TD>

</Table>

</body>

</html>

表格标记的bgcolor属性表示表格所显示的背景颜色。前面提到过,颜色属性可以用一个三元组表示,其中红、绿、蓝各16比特;也可用颜色名称表示,如red。width属性表示表格在页面的水平宽度所占的百分比或像素数目。如果使用百分比表达,则表格的宽度可根据显示区域动态变化。CellPadding属性表示每个单独的数据单元中使用的额外空间的大小。CellSpacing表示表格之间的距离大小。border属性表示表格边界的宽度。

将<CAPTION></CAPTION>标记放置在表格开始后、结束前,可以为表格添加相关联的标题。如图17.3所示,本例中与表格相关联的标题是“Sample Table”。

图17.3  使用表格标记和相关属性进行格式化的文档

添加表格头  表格头标记<TH>表示HTML表格的列标题。请注意上面的代码,其中分别向表格的三列添加了列标题Column1、Column2、Column3。

定义表格行  表格的每一行数据都使用<TR></TR>标记表示。即使在HTML源文件中标记之间包括了许多行文本也是如此,该标记指示浏览器将标记之间的所有东西都作为表格的一行数据来显示。

向表格添加数据  单独的表格元素使用<TD></TD>标记表示,如代码所示。由于单一的数据元素可以包含简单的文本或嵌套表,因此其工作方式非常简单。请注意,<TD>标记可能会包含一些额外的属性,如代码中所示的ALIGN属性。

提示:为建立有趣的Web页面,一个好方法是复制已存在的页面,并进行定制。

很明显,单独的标记是易于理解的。HTML的潜在威力正在于这种简单性。如同任何语言的语法,我们进行通信的能力受对语言的理解的限制。虽然HTML是简单的,考虑到26个拉丁字母——其可能性确实是令人吃惊的,正像World Wide Web一样。如果需要创建高级的Web页面,还需要进一步学习,本节只是提供了进阶的基础。

17.1.3  将可替换参数标记与WebBroker一同使用

专业水准的Web页面需要更多的努力,并广泛地使用HTML,其内容远远超过这里所介绍的。除了需要创建基本的Web页面之外,还需要表示在动态Web页面中可替换的标记;这些标记将使用由Web服务器传送的动态数据替换。可替换参数标记与书签标记看起来很相似。

可替换参数在Delphi的Web服务器页面中使用<#标记名>表示。对于并不表示有效的超文本标记的< >对,HTML惟一的行为是忽略掉这些标记。但Delphi组件可以查找一些特定格式的文本。

注意:TPageProducers的TagString行为与Format函数行为非常相似。当遇到特定的标记时,就使用格式化文本替换该标记。Format函数使用字符串参数,以%开头,后接特殊的格式化字符;而HTML文档中遇到<#标记名>时,则触发OnHTMLTag事件。显然,一旦该类型事件发生,即可使用必要的代码进行合适的替换。

当为Web服务器设计页面时,当需要向WebBroker页面插入数据时,只要找到<#标记名>参数标记即可。TPageProducer组件的OnHTMLTag事件会传递一个TagString参数,可以将其与预先确定的值进行比较,然后根据TagString的值采取合适的动作。

下面一节示范了WebBroker组件一些基本的方法、特性和事件,可用于建立Web服务器。

17.2  使用WebBroker组件

Delphi的WebBroker套件定义了一些组件,能够对URL请求进行响应;这些URL请求是由浏览器或HTTP服务器应用程序向基于WebBroker套件建立的服务器发出的。有一些基本的组件可以支持这种行为特征,而服务器的其余部分则是由您业已熟悉的组件构成的。

WebBroker支持建立ISAPI、NSAPI、CGI和Apache服务器。ISAPI和NSAPI服务器分别是由Microsoft和Netscape的Internet服务器支持的。而Apache服务器则是一种流行的Internet服务器;Delphi 6的WebBroker组件也支持建立Apache服务器。Delphi的WebBroker支持CGI服务器,可建立为单独的控制台程序或Windows程序,将请求返回到标准输入/输出设备。大多数流行的浏览器都支持CGI。

所有的服务器程序都以WebDispatcher组件开始,我们在讨论WebBroker组件时也从这里开始。

17.2.1  WebDispatcher组件

所有的Web服务器程序都包含了一个单独的WebDispatcher组件,当使用New Items对话框中的Web Server Application向导启动新的工程时,该组件将自动添加到TWebModule组件中;也可以手工从组件面板的Internet属性页向已有的TDataModule组件添加。特别是,TWebDispatcher组件是TCustomWebDispatcher组件的后代,子类化TDataModule并实现了IWebAppDispatcher和IWebDispatcherAccess接口。TWebDispatcher组件、IWebAppDispatcher和IWebDispatcherAccess接口支持添加动作项与函数,动作项用于响应Web请求,而函数用于返回TWebResponse和TWebRequest对象。TWebResponse和TWebRequest对象用于交换所请求的数据和返回到Web服务器的数据。当向数据模块添加TWebDispatcher组件时,与定义TWebModule对象在效果上是相同的。

TWebDispatcher包含一个TWebActionItems的集合。这些项可用于为URL响应和请求定义路径信息。如果使用Web Server Application向导,则Delphi将自动创建一个TWebModule对象,该组件实现了TWebDispatcher的接口。每个Web服务器只需一个TWebDispatch对象:可以向TDataModule组件添加TWebDispatcher组件,也可创建TWebModule组件。

TWebModule组件

TWebModule由TDataModule子类化而来,并支持添加和管理TWebActionItems以及非可视化控件,这些组件对于使用WebBroker套件创建Web服务器是必要的。除了TWebModule,还需要添加其他VCL控件,如TPageProducer,该组件以URL路径信息和查询数据的形式对发送到Web服务器的请求进行响应(请回忆在本章开始讨论的URL的各个部分)。

TWebActionItem

用右键单击TDataModule中的TWebDispatcher组件或TWebModule中的任意点,然后从上下文菜单中选择Action Editor菜单项,即可启动WebAction编辑器。WebAction编辑器(如图17.4所示)可以为Web服务器定义路径信息。

在动作项中包括了动作的名字、触发该动作的URL路径信息、该动作项是否可用、某个特定的动作是否是默认的动作以及可选的页面生成器。最低程度也需要一个默认动作,而且可能只有一个。默认动作是某个特定的请求不存在匹配路径时触发的动作。在图17.4中,该动作项名为Root。路径为/root。该动作是可用的,并且是默认动作。当触发该动作时,使用PageProducer1组件来生成相应的页面。

图17.4  WebAction集合编辑器可以定义路径信息,

用于浏览器或其他来源发出的URL请求中

注意:当某个动作是默认动作时,忽略Enable特性。默认动作总是可用的。

假定图中所示的Root动作是为驻留在LocalHost计算机上的ISAPI服务器server.dll定义的。下面的两个URL都会返回由PageProducer1组件所定义的响应。

http://LocalHost/scripts/server.dll

http://LocalHost/scripts/server.dll/root

除了WebAction编辑器中所显示的信息之外,还可以指定MethodType。默认的MethodType是mtAny。默认的MethodType必须对任何请求进行响应,其他可能的MethodType包括mtGet、mtHead、mtPost和mtPut。mtGet类型请求用于获取与URL关联的信息。mtPut类型用于替换与URL关联的信息的内容。mtPost用于将请求的内容发送到服务器。最后,mtHead类型只返回请求的头信息。

当Web服务器从HTTP服务器接收到路径和查询信息时,首先从动作项列表中查找匹配的路径和MethodType。如果没有匹配的动作,则使用默认动作。另一方面,找到匹配动作后,则调用OnAction事件。OnAction事件处理程序的参数包括:调用对象的引用、TWebRequest对象、TWebResponse对象和一个布尔类型的变量参数Handled。TWebRequest对象Request包含了发送到HTTP服务器的请求的所有信息。TWebResponse对象Response可用于将内容返回到服务器。Content特性可用于返回简单的文本,而ContentStream和ContentType特性可用于返回较为复杂的二进制数据如图像或大块的文本。变量参数Handled用于表示事件处理程序是否完全处理了该请求。将Handled设置为False表示还需要其他处理程序来完成对请求的处理。

procedure TDataModule2.WebDispatcher1GetHeadAction(Sender:

TObject; Request: TWebRequest; Response: TWebResponse;

var Handled: Boolean);

const

sContent = '%s %s <BR> Connection: %s <BR> User-Agent: %s

<BR>' + 'Host: %s <BR> Accept: %s <BR>';

begin

with Request do

Response.Content := Format(sContent, [Method, URL,

Connection, UserAgent, Host, Accept]);

end;

在这个简明的例子中,一个名为GetHead的动作的OnAction事件处理程序返回了所有请求的头信息。在下面列出了输出。

GET /scripts/GetHead.dll

Connection: Keep-Alive

User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0;

COM+ 1.0.2204)

Host: LocalHost

Accept: */*

请求的头信息包含了请求的方法类型和URL。从例子中可以知道,Get请求是由浏览器发出的,本例中的Web服务器是GetHead.dll。Connection状态表示该连接不应关闭。User-Agent表示了发出请求的程序的有关信息。在本例中,通过该信息可以确定该程序是Internet Explorer 5.5,运行在Windows 2000系统上。Host是服务器所在的站点。从列出的信息可以看出,Web服务器是运行在LocalHost;即Internet服务器、Web服务器和浏览器都运行在同一台计算机上。Accept表示构成有效响应的媒体类型。其值为*/*,表示所有类型的数据都可以作为响应的内容。

还有一些示范OnAction以及使用响应和请求对象的例子,在本章稍后有关创建Web服务器的章节中将会涉及到。

17.2.2  TPageProducer组件

TPageProducer组件可以指定HTML文档模板,存储在TStrings类型特性HTMLDoc中,或者通过HTMLFile特性来引用。HTMLFile和HTMLDoc特性是互斥的。HTML模板可以由完整的HTML文档构成,其中也可包括对HTML透明的标记,在请求该文档时使用特定的数据进行替换。如果将TPageProducer组件与TWebActionItem的Producer特性关联起来,那么在URL中包括相关联的路径时,将返回TPageProducer组件的HTML文档。从图17.4可知,GetHead.dll服务器的默认路径Root将返回PageProducer1组件的HTMLDoc特性。

如果TPageProducer组件包含需要替换的透明标记,则需要实现TPageProducer.OnHTMLTag事件处理程序对标记进行动态替换。如果页面生成器包含了完整的HTML文档,就不需要OnHTMLTag事件处理程序。下面的HTML文档示范了文档体中透明标记的使用。

<html>

<body>

<#CURRENTTIME>

</body>

</html>

假定上面的HTML与例子中的默认URL路径Root相关联,那么在请求默认响应时,将对文档中的每个标记调用相关联的PageProducer1的OnHTMLTag事件处理程序。在例子中,只有一个HTML透明标记:<#CURRENTTIME>。

将TagString特性与已知的标记进行比较,可以对遇到的每个标记都提供替换文本。下面列出的代码将TagString参数与CURRENTTIME标记进行比较。当遇到CURRENTTIME标记时,即可如下例所示使用系统时间对其进行替换。

procedure TDataModule2.PageProducer1HTMLTag(Sender: TObject;

Tag: TTag; const TagString: String; TagParams: TStrings;

var ReplaceText: String);

begin

if( CompareText(TagString, 'CURRENTTIME') = 0 ) then

ReplaceText := TimeToStr(Now);

end;

提示:在比较标记字符串时,尖括弧和#符号被忽略。<#和>的作用是使得标记对HTML阅读器透明。

页面生成器的默认行为是使用空字符串替换不匹配的标记。在TTag参数中含有枚举值,表示了发现该标记的上下文。也可以向透明标记添加标记属性。标记属性添加到TStrings类型参数TagParams,在OnHTMLTag事件处理程序中进行处理。为示范TagParams参数的用法,下面分别对HTML文档和事件处理程序进行了修订:

<#CURRENTTIME DateTimeFormat=hh:nn:ssa/p>

修订的标记添加了一个参数,在本例中将作为标记的格式化规则。修改后的事件处理程序考虑了标记参数的可能性。

procedure TDataModule2.PageProducer1HTMLTag(Sender: TObject; Tag:

TTag; const TagString: String; TagParams: TStrings; var

ReplaceText: String);

begin

if( CompareText(TagString, 'CURRENTTIME') = 0 ) then

if( TagParams.Values['DateTimeFormat'] <> '' ) then

ReplaceText := FormatDateTime(

TagParams.Values['DateTimeFormat'], Now)

else

ReplaceText := TimeToStr(Now);

end;

修订后的代码检查是否存在DateTimeFormat参数。如果该参数非空,则对时间值使用格式化规则;否则默认行为就是使用TimeToStr将时间转换为字符串,并返回该字符串。

17.2.3  TDataSetPageProducer组件

TDataSetPageProducer可以将TDataSet组件关联到TDataSetPageProducer组件。当请求TDataSetPageProducer的内容时,页面生成器将把HTML透明标记自动与字段名进行匹配,并使用数据集中的匹配字段替换标记的当前值。考虑下面的HTMLDoc特性值。

<HTML>

<HEAD>

<TITLE>Customer Orders</TITLE>

</HEAD>

<BODY>

<TABLE BORDER=0>

<TR><TH ALIGN=LEFT>Customer No.:</TH><TD><#CustNo></TD></TR>

<TR><TH ALIGN=LEFT>Order No.:</TH><TD><#OrderNo></TD></TR>

<TR><TH ALIGN=LEFT>Date of Sale:</TH><TD><#SaleDate></TD></TR>

<TR><TH ALIGN=LEFT>Amount:</TH><TD><#AmountPaid></TD></TR>

<TR><TH ALIGN=LEFT>Ship Date:</TH><TD><#ShipDate></TD></TR>

</TABLE>

</BODY>

</HTML>

上面的HTML文档中包含了五个HTML透明标记:<#CustNo>、<#OrderNo>、<#SaleDate>、<#AmountPaid>和<#ShipDate>。根据设计,这些都是存储在Orders.db表中的字段。下面的OnAction事件将Orders.Content特性赋值给Response.Content特性。

procedure TWebModule1.WebModule1OrdersAction(Sender: TObject;

Request: TWebRequest; Response: TWebResponse; var Handled:

Boolean);

begin

TableOrders.Open;

try

Response.Content := Orders.Content;

finally

TableOrders.Close;

end;

end;

Orders是一个TDataSetPageProducer对象,其中包含上述的HTML文档。Orders.DataSet与一个TTable组件相关联,该组件表示Orders.db表(Orders.db与Delphi一同发布,可通过BDE别名DBDEMOS引用)。Orders对象将把标记自动地替换为与标记名匹配的字段值。由于代码只是把表打开,因此字段的值将是表中第一行所包含的值。

对Orders页面生成器进行微小的修改,即可使用浏览器的刷新按钮看到所有的订单。在TDataSetPageProducer.OnCreate事件处理程序中打开相应的表。在TDataSetPageProducer.OnDestroy事件中关闭表,在每次触发WebAction时将当前记录指针向前移动一个记录。下面列出了修订后的代码。

procedure TWebModule1.WebModule1OrdersAction(Sender: TObject;

Request: TWebRequest; Response: TWebResponse; var Handled:

Boolean);

begin

Response.Content := Orders.Content;

if Not TableOrders.Eof then TableOrders.Next

else TableOrders.First;

end;

procedure TWebModule1.OrdersCreate(Sender: TObject);

begin

TableOrders.Open;

end;

procedure TWebModule1.OrdersDestroy(Sender: TObject);

begin

TableOrders.Close;

end;

尽管这对于浏览所有的记录而言是一种呆板的方法,但它是可用的,而且可以作为更高级技术的基础。看到多条记录的更为方便的方法是使用下一节中示范的TDataSetTableProducer组件。

17.2.4  查看表数据

TDataSetTableProducer组件用于返回多行数据。它可以定义数据的表格视图(见图17.5),并在请求该路径时显示多行数据。返回的实际行数由TDataSetTableProducer.MaxRows特性决定。TDataSetTableProducer组件包括了许多用于描述和添加页面内容的特性,如Footer、Header、Caption和RowAttributes等。如果只是简单地把TTable或TQuery对象赋值给TDataSetTableProducer.DataSet特性,则页面上会显示数据集中所有的列。可以选择使用列编辑器,只返回列的特定子集。

图17.5  HTML表格,其内容来自于ORDERS.DB数据,

是通过TDataSetTableProducer组件得到的

单击TDataSetTableProducer组件上下文菜单中的Response Editor菜单项,即可运行Columns编辑器(如图17.6所示)。该编辑器与TDBGrid的Columns编辑器非常相似。但TDataSetTableProducer的Columns编辑器并不是把列值返回到VCL控件,而是生成必要的HTML文档,将其与根据选定的列得到的静态字段值合并起来。

图17.6  要创建如图17.5所示的HTML文档,可在Columns编辑器中如图配置

图17.5中所显示的示例页面在本书CD-ROM的MastApp.dll中定义。通过在TDataSetTableProducer.OnFormatCell事件中修改背景颜色,即可产生该页面的效果。OnFormatCell事件的代码如下所示:

procedure TWebModule1.ManyOrdersFormatCell(Sender: TObject;

CellRow, CellColumn: Integer; var BgColor: THTMLBgColor;

var Align: THTMLAlign;

var VAlign: THTMLVAlign; var CustomAttrs, CellData: String);

begin

if CellRow = 0 then BgColor := 'Gray'

else if CellRow mod 2 = 0 then BgColor := 'Silver';

end;

要创建所示的页面,可按照下列步骤进行。

1.单击File | New | Other命令,创建新的Web服务器程序,并创建ISAPI Web Server Application。

2.从组件模板的Internet属性页添加一个TDataSetTableProducer组件,从Data Access属性页添加一个TTable组件。

3.将TTable.DatabaseName特性设置为DBDEMOS。

4.将TTable.TableName特性设置为orders.db表。

5.将TDataSetTableProducer.DataSet特性设置为TTable组件。

6.将上面列出代码添加到TDataSetTableProducer.OnFormatCell事件处理程序。

7.在TWebModule上单击右键,然后在TWebModule上下文菜单中单击Action Editor菜单项。

8.添加动作项ManyOrders。

9.对动作项的PathInfo特性输入文本/ManyOrders。确认该动作是可用的,并将Producer特性设置为TDataSetTableProducer组件。确认Enabled特性设置为True(可以在Object Inspector中进行所有的改动)。

10.将工程保存为MastApp。并建立该应用程序。

编译该ISAPI服务器并将其复制到Web服务器脚本目录(请记住,如果在计算机上你有Peer Web Services或在Windows NT下有IIS,即可测试该服务器)。假定您是在Windows 2000系统上运行IIS,默认目录为inetpub,可以通过输入地址http://localhost/scripts/MastApp.dll/ManyOrders 来运行服务器。

下一节示范了TQueryTableProducer组件的用法以及用于响应URL中查询的TWebRequest对象。

17.2.5  TQueryTableProducer组件

TQueryTableProducer组件用于为TWebRequest.QueryFields特性中的GET请求查询字段信息;POST方法的信息通过TWebRequest.ContentFields特性传递到服务器。上述两个特性均为TStrings。可以使用TStrings的Names和Values刷新得到传递给服务器的数据。

为演示TQueryTableProducer组件,我们需要看一下位于$(DELPHI)/Demos/Internet WebServ/IIS文件夹下的iserver.dpr工程。iserver.dpr例子通过对DBDEMOS数据库中的Customer.db表进行迭代,使用TPageProducer组件生成顾客列表。WebAction定义在main.pas中,以返回表示每个顾客的超链接列表。

procedure TCustomerInfoModule.CustomerListHTMLTag(Sender:

TObject; Tag: TTag; const TagString: String;

TagParams: TStrings; var ReplaceText: String);

var

Customers: String;

begin

Customers := '';

if CompareText(TagString, 'CUSTLIST') = 0 then

begin

Customer.Open;

try

while not Customer.Eof do

begin

Customers := Customers +

Format(

'<A HREF="/scripts/%s/runquery?CustNo=%d">%s</A><BR>',

[ScriptName, CustomerCustNo.AsInteger,

CustomerCompany.AsString]);

Customer.Next;

end;

finally

Customer.Close;

end;

end;

ReplaceText := Customers;

end;

TPageProducer类型的组件CustomerList将<#CUSTLIST>用每个顾客的超链接进行替换。有一行特定的HTML语句是使用上面的事件处理程序中的Format语句生成的。

<A HREF="/scripts/%s/runquery?CustNo=%d">%s</A><BR>

%s参数是Web服务器程序的名字。可以对该值进行硬编码,在数据模块创建时使用API过程GetModuleFileName来得到这个值就更为灵活。

procedure TCustomerInfoModule.DataModule1Create(Sender: TObject);

var

FN: array[0..MAX_PATH- 1] of char;

begin

SetString(ScriptName, FN, GetModuleFileName(hInstance, FN,

SizeOf(FN)));

ScriptName := ExtractFileName(ScriptName);

end;

注意:请记住,可以使用TTable或TQuery的字段编辑器在设计时生成TField组件。

路径信息/runquery代表了一个动作项。查询是?CustNo=%d,其中%d表示整型参数,将使用CustomerCustNo.AsInteger来替换,并向用户提供超链接以及从TField对象CustomerCompany读出顾客名。在指向每个顾客的超链接组装好以后,将使用这些链接替换ReplaceText变量参数。

当TPageProducer对象CustomerList使用顾客列表进行响应时,单击任意的顾客超链接都会把CustNo发送到QueryAction项。QueryAction项是动作项的名字,由/runquery路径表示。发送到/runquery动作项的请求出现在浏览器的地址栏中,其形式如下(假定我们选中了Action Club链接):

http://localhost/scripts/iserver.dll/runquery?CustNo=1645

当向Web服务器提供路径时,将调用QueryAction.OnAction事件,并照原TQueryTableProducer对象CustomOrders的内容。在事件处理程序的实现中,TQuery的Locate方法用于只返回与每个特定顾客相关联的订单。

procedure TCustomerInfoModule.WebModule1QueryActionAction(Sender:

TObject; Request: TWebRequest; Response: TWebResponse;

var Handled: Boolean);

begin

Customer.Open;

try

if Customer.Locate('CustNo',

Request.QueryFields.Values['CustNo'], []) then

begin

CustomerOrders.Header.Clear;

CustomerOrders.Header.Add(

'The following table was produced using a'+

TQueryTableProducer.<P>');

CustomerOrders.Header.Add(

'Orders for: ' + CustomerCompany.AsString);

Response.Content := CustomerOrders.Content;

end

else

Response.Content := Format(

'<html><body><b>Customer: %s not found</b></body></html>',

[Request.QueryFields.Values['CustNo']]);

finally

Customer.Close;

end;

end;

传递到Web服务器的查询信息存储在Response.QueryFields特性中。从上面的代码可知,响应的内容是直接由TQueryTableProducer组件CustomerOrders返回的。

17.3  使用Cookie

Cookie是一些小块的数据,存储在运行浏览器的客户端PC上。Cookie用于保存浏览器会话的持久信息,即对状态进行持久化。TWebRequest组件包含一个未分析的cookie特性,用于表示cookie头,以及一个CookieFields特性,把cookie字段按逐字段的名-值对存储在TStrings对象中。TWebRequest.Cookie特性与HTTP请求一同发送的cookie头。TWebResponse.Cookies特性类型为TCookieCollection,包含了对请求进行响应所需的所有cookie头。

TCookieCollection包含单独的TCookie对象,该对象含有Domain、Expires、HeaderValue、Name、Path、Secure和Value特性。Domain特性限制了接收者的域——即cookie发送到哪个站点。Expires特性表示什么时候不再发送Cookie。HeaderValue包含了cookie的名字和值,以及Domain、Expires和Secure字段的字符串值。Name特性用于通过名字标识cookie,而Value特性则包含了cookie所存储的值。Path特性将cookie接收者限制到特定的路径。如果Secure特性为True,则cookie只发送到使用安全socket的连接(安全socket使用HTTPS协议)。

提示:由CGI服务器cookie.exe所返回的Web内容演示了如何使用嵌套表格来划分HTML文档。通过设置边界,使得可以看到页面被划分。将边界宽度设置为零来隐藏表格的边界,这样无需使用帧即可生成专业水准的页面。

通过对iserver.dpr进行改编,下面的代码演示了如何存储cookie来跟踪访问某一页面的次数(参见图17.7)。下面列出的代码是OnHTMLTag的处理程序,示范了如何对工作进行划分,使实际的事件处理程序保持比较简单。

图17.7  显示的Web页面是由CGI服务器程序生成的,该程序

存储了一个cookie,用于标识用户的刷新或访问次数

procedure TWebModule1.RootHTMLTag(Sender: TObject; Tag: TTag;

const TagString: String; TagParams: TStrings;

var ReplaceText: String);

begin

if( TagString = 'COUNT' ) then

ReplaceText := GetCountText( Request, Response )

else if( TagString = 'COOKIEDATA' ) then

ReplaceText := GetCookieText( Request, Response );

end;

如果遇到<#COUNT>标记,则调用GetCountText;如果TagString是<#COOKIEDATA>,则调用GetCookieText(参见图17.8的例子,在cookie文件中包含cookie计数值)。GetCookieText的实现从TWebRequest.CookieFields中读出所要求的cookie的名字和值。

图17.8  cookie相应的文本文件包含了cookie的信息。

图中cookie的当前计数值是高亮显示的

function TWebModule1.GetCookieText( Request : TWebRequest;

Response : TWebResponse ) : String;

begin

result := 'Before increment:'+Request.CookieFields[0];

end;

GetCountText向cookie集合添加一个cookie,分配cookie和名字,在cookie存在的情况下读出其值。在把cookie转换为整数再逆向转换时使用了异常处理块,以防在第一次访问时cookie值未初始化的情况。该cookie在一天后过期,就不是安全的cookie,而且只作为对任意域的响应发送,而且路径限制为/scripts/Cookie.exe/。

function TWebModule1.GetCountText( Request : TWebRequest;

Response : TWebResponse ) : String;

begin

with Response.Cookies.Add do

begin

Name := 'Count';

Value := Request.CookieFields.Values['Count'];

try

Value := IntToStr(StrToInt(Value) + 1);

except

Value := '1';

end;

Secure := False;

Path := '/scripts/Cookies.exe/';

Expires := Now + 1; // this cookie expires in one day

result := Value;

end;

end;

现在可以发现,Web编程还是有许多事情要做的。显然需要掌握HTML编程,包括向Web页面添加输入域和其他可视化控件。请记住,在选择Web内容时有几种可能性。Web服务器可以返回包含JavaScript或VBScript的ASP,也可以是XML、WML。每个协议都有其自身的语法和要求,但都可以创建内容丰富的Web站点。采用工具来方便文档的创建,和使用页面生成器的HTMLFile特性,都是好主意。使用工具可以减轻学习不同协议的负担,而HTMLFile特性可以将外部的HTML模板与页面生成器关联起来。通过使用外部HTML模板,可以修改Web内容而无需重建Web服务器程序。

17.4  小  结

本章对HTML进行了综述。超文本标记语言可以创建丰富的Web页面,而无需多少真正的聪明。对于创建Web的内容来说,较好的起步策略是模仿您所喜欢的站点。可以看到大多数Web站点的源代码,并将其保存到本地计算机。HTML包含的标记还有很多,可用于指示浏览器如何显示信息,这已经超出了本章的范围,但您可以使用类似FrontPage的工具来跳过复杂标记的学习。

WebBroker套件支持使用HTML透明标记,当浏览器请求Web页面时可以对这些标记进行动态替换。WebBroker有一些工具,如TPageProducer可以响应特定的URL路径和查询信息,并根据HTML模板、HTML透明标记和从数据库得到的信息进行动态的信息替换。

本章中您已经学到了WebBroker的一些知识。更多的信息请阅读附录D。附录D使用WebBroker来返回供无线连接使用的WML(Wireless Markup Language,无线标记语言)的内容,例如与因特网蜂窝电话一起使用的那些连接。

第17章 使用WebBroker组件创建Web服务器相关推荐

  1. ENSP如何开启服务器的http_如何使用HTTP模块在Node.js中创建Web服务器(上)

    当你在浏览器中查看网页时,其实是在向互联网上的另一台计算机发出请求,然后它会将网页提供给你作为响应.你通过互联网与之交谈的那台计算机就是Web服务器,Web服务器从客户端(例如你的浏览器)接收HTTP ...

  2. 应用Nodejs创建web服务器

    //创建web服务器,设置端口,根据浏览器的URL做出响应 /index    响应'<h2>这是首页</h2>'              /list         响应文 ...

  3. ESP32利用SPIFFS(闪存文件系统)创建 Web服务器实现引脚控制

    ESP32利用SPIFFS(闪存文件系统)创建 Web服务器实现引脚控制 在本教程中,我们将向您展示如何构建一个web服务器,以提供存储在ESP32文件系统中的HTML和CSS文件.我们将创建分离的H ...

  4. 两步使用Express快速创建web服务器

    一:首先需要给大家介绍一下Express~~~ Express 是一个基于 Node.js 平台,快速.开放.极简的 web 开发框架. 框架:是一个半成品,用来快速解决一类问题:库就是工具集,使用非 ...

  5. nodejs没有net模块_Node.js实战16:用http模块创建web服务器

    Nodejs的http模块,是基于net.server,经过c++二次封装,也是nodejs的核心模块. 功能比net.server更强,可解析和操作更多细节内容,如值.content-length. ...

  6. 一文读懂tomcat组件--一个web服务器的架构演化史

    1. tomcat是谁? 2. tomcat可以做什么? tomcat是一个web容器,可以将web应用部署到tomcat,由它提供web服务,一个web容器中可以部署多个web应用,这些web应用可 ...

  7. 《图解HTTP》读书笔记--第5章与HTTP协作的Web服务器

    写在前面:本文仅供个人学习使用,如有侵权,请联系删除.文章中所用图片绝大多数来源于<图解HTTP>,请读者支持原版. 文章目录 5.1 用单台虚拟主机实现多个域名 5.2 通信数据转发程序 ...

  8. dotnet core使用mvc创建web服务器

    Kerstrel服务器 dotnet core自带轻量级的kerstrel服务器,可以实现iis.tomcat.ngnix.apache等功能 dotnet core mvc dotnet new m ...

  9. 创建web服务器||HTTP协议的概念||报文||HTTP请求与响应处理——未完待续

    HTTP协议的概念 超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)规定了如何从网站服务器传输超文本到本地浏览器, 它基于客户端服务器架构工作,是客户端( ...

最新文章

  1. Javascript函数之深入浅出递归思想,附案例与代码!
  2. html 甘特图_甘特图怎么画?甘特图基础教程,小白快速入门简单易懂
  3. nagios安装配置(一)
  4. vs2015第二次装安装不能选择路径问题解决方法
  5. 帝国cms百度小程序接入自然搜索的两种方式看看哪个更适合你
  6. “std::invoke”: 未找到匹配的重载函数
  7. python自学入门教程-Python基础教程,Python入门教程(非常详细)
  8. SD-WAN应用可见性的流量对称
  9. HDU4633(Polya计数)
  10. CentOS7入门_安装并配置mysql5.7.18
  11. java实现物体下落效果_手撸一个物体下落的控件,实现雪花飘落效果
  12. C# 基础补遗(未完待续)
  13. 搭建个人论坛网站图文教程
  14. 简单 PS CS6蒙版抠图技巧
  15. STM32F103ZET6---【硬件篇】定时器
  16. 10月北京二手房交易量强势反弹 房价环比上涨5.2%
  17. 记忆宫殿--清华一日游
  18. python小白进阶之路三——七段数码管的绘制+做一个酷炫的倒计时(函数的复用)
  19. 小象学院人工智能机器学习全新升级版
  20. 亚马逊显示在售商品为0怎么办?亚马逊新品货还没到就在售了

热门文章

  1. 微搭使用笔记(五) 通过数据源API写入数据并展示到页面
  2. Silverlight开发工具汇总
  3. 哺恩养老获资本战略投资,“传统养老机构”如何走上发展快车道?
  4. mtp java_Android之 MTP框架和流程分析
  5. 荧光标记氨基酸:FITC标记D-精氨酸;FITC-D-Arginine
  6. 物联网将如何在智慧城市交通管理中发挥重要作用
  7. win10控制台(cmd)中文乱码解决
  8. 购买浏览器认证的证书
  9. 在VMware workstation上使用win_server_2012(Hyper-v)搭建云桌面教程
  10. 如何使用Xcode Configuration 管理 iOS Apps 不同构建版本