1. Trang chủ
  2. » Công Nghệ Thông Tin

MASTERING DELPHI 6 phần 10 pps

99 281 1

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Web Programming with WebBroker and WebSnap
Trường học Sybex, Inc.
Chuyên ngành Web Programming
Thể loại sách
Năm xuất bản 2001
Thành phố Alameda
Định dạng
Số trang 99
Dung lượng 757,18 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The WebSearch program on the companion CD, available as aCGI application or a Web App Debugger executable, has an action that simply returns theHTML retrieved by the search engine and a

Trang 1

database table, we’ll need to update the element list automatically As a final solution, we candesign the ISAPI DLL to produce a form on-the-fly, and we can fill the selection controlswith the available elements.

We’ll generate the HTML for this page in the /formaction, which we’ve connected to aPageProducer component The PageProducer contains the following HTML text, whichembeds two special tags:

<h4>Customer QueryProducer Search Form</h4>

<form action=”CustQueP.dll/search” method=”POST”>

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

const TagString: String; TagParams: TStrings; var ReplaceText: String);

Trang 2

This method used a second Query component, which I manually placed on the form andconnected to the DBDEMOS database, and it produces the output shown in Figure 22.6.

Finally, this Web server extension, like many others we’ve built, allows the user to view thedetails of a specific record As in the last example, we can accomplish this by customizing theoutput of the first column (column zero), which is generated by the QueryTableProducercomponent:

procedure TWebModule1.QueryTableProducer1FormatCell(

Sender: TObject; CellRow, CellColumn: Integer;

var BgColor: THTMLBgColor; var Align: THTMLAlign;

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

begin

if (CellColumn = 0) and (CellRow <> 0) then

CellData := ‘<a href=”’ + Request.ScriptName + ‘/record?’ + CellData +

‘“>’ + CellData + ‘</a>’#13;

if CellData = ‘’ then

CellData := ‘&nbsp;’;

end;

TIP When you have an empty cell in an HTML table, most browsers render it without the border.

For this reason, I’ve added a “nonbreaking space” symbol (&nbsp;) into each empty cell This

is something you’ll have to do in each HTML table generated with Delphi’s table producers.

Trang 3

The action for this link is /record, and we’ll pass a specific element after the ?parameter(without the parameter name, which is slightly nonstandard) The code we use to producethe HTML tables for the records doesn’t use the producer components as we’ve been doing;instead, it is very similar to the code of an early ISAPI example:

procedure TWebModule1.RecordAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);

Query2.SQL.Add (‘select * from customer ‘ +

‘where Company=”’ + Request.QueryFields[0] + ‘“‘);

Response.Content := Response.Content + ‘</table><hr>’#13 +

// pointer to the query form

‘<a href=”’ + Request.ScriptName + ‘/form”>’ +

‘ Next Query </a>’#13 + ‘</body></html>’#13;

end;

end;

Debugging with the Web App Debugger

Debugging Web applications written in Delphi is often quite difficult In fact, you cannotsimply run the program and set breakpoints in it, but should convince the Web server to runyour CGI program or library within the Delphi debugger This can be accomplished byindicating a Host application in Delphi’s Run Parameters dialog box, but it implies lettingDelphi run the Web server (which is often a Windows service, not a stand-alone program)

To solve all of these issues, Borland has added to Delphi 6 a specific Web App Debuggerprogram This tool, activated by the corresponding item of the Tools menu, is a Web server,which 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, using COM-based techniques This

Trang 4

means you can run the Web server application from within the Delphi IDE, set all the points you need, and then (when the program is activated through the Web App Debugger)debug the program as you’ll do for a plain executable file.

break-The Web App Debugger does also a good job in logging all the received requests and theactual responses returned to the browser, as you can see in Figure 22.7 The program also has

a Statistics page, which interestingly tracks the time required for each response, allowing you

to test the efficiency of an application in different conditions

By using the corresponding option of the New Web Server Application dialog, you caneasily create a new application compatible with the debugger This defines a standard project,which creates both a main form and a data module The (useless) form includes code for reg-istering the application as an OLE automation server, as:

The log of the Web

App Debugger with its

LogDetail window

Trang 5

only those running In fact, the use of COM Automation accounts for the automatic tion of a server Not that this is a good idea, though, as running and terminating the programeach time will make the process much slower Again, the idea is to run the program withinthe Delphi IDE, to be able to debug it easily Notice, though that the list can be expandedwith the detailed view, which includes a list of the actual executable files and many otherdetails.

activa-The data module for this type of project has some initialization code as well:

if WebRequestHandler <> nil then

WebRequestHandler.WebModuleClass := // Web module class

F I G U R E 2 2 8 :

A list of applications

registered with the Web

App Debugger is displayed

when you hook to its

home page.

Trang 6

WARNING By doing this for the CustQueP example (it is the CustQueDebug project), I realized that some

of the Web request settings are different So instead of using the ScriptName property of the request (set to empty for a Web debug application), you have to use the InternalScript- Name property.

There are other two interesting elements in the use of the Web App Debugger The first isthat you can test your programs without having a Web server installed and without having totweak its settings In other words, you don’t have to deploy your programs to test them—yousimply try them out right away Another advantage is that, contrary to doing early development

of the applications as CGI, you can start experimenting with a multithreaded architecture rightaway, without having to deal with the loading and unloading of libraries, which often impliesshutting down the Web server and possibly even the computer

NOTE If your aim is to build an ISAPI application, you can also use a specific ISAPI DLL debugging

tool One such tool, called IntraBob, has been built by Bob Swart and is available on his Web site (www.drbob42.com) as freeware.

Working with Apache

If you plan on using Apache instead of IIS or another Web server, you can certainly takeadvantage of the common CGI technology to deploy your applications on almost any Webserver However, using CGI means some reduced speed and some trouble handling stateinformation (as you cannot keep any data in memory) This is a good reason for writing anISAPI application or a dynamic Apache module Using Delphi’s WebBroker technology, youcan also easily compile the same code for both technologies, so that moving your program to

a different Web platform becomes much simpler Finally, you can also recompile a CGI gram or a dynamic Apache module with Kylix and deploy it on a Linux server

pro-As I’ve mentioned, Apache can run traditional CGI applications but has also a specifictechnology for keeping the server extension program loaded in memory at all times for fasterresponse To build such a program in Delphi 6, you can simply use the Apache Shared Mod-ule option of the New Web Server Application dialog box You end up with a library havingthis type of source code for its project:

Trang 7

configu-ModuleName := ‘Apache1_module’;

ContentType:= ‘Apache1-handler’;

If you don’t set them, Delphi will assign them some default values, which are built adding

the _module and -handler strings to the project name, ending up with the two names I’ve used

above

An Apache module is generally not deployed within a script folder, but within the modulessubfolder of the server itself (by default, c:\Program Files\Apache\modules) Then you have

to edit the http.conffile, adding a line to load the module, as:

LoadModule apache1_module modules/apache1.dll

Finally, you have to indicate when the module is invoked The handler defined by the ule can be associated with a given file extension (so that your module will process all of thefiles having a given extension) or with a physical or virtual folder In the latter case, the folderdoesn’t exist, but Apache pretends it is there This is how you can set up a virtual folder for thesimple Apache1 module:

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

As Apache is inherently case sensitive (because of its Linux heritage), you might also 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 Agreat advantage of using virtual folder in Apache is that a user doesn’t really distinguish betweenthe physical and dynamic portions of your site, as we’ll better see in the next example

Because the development of Apache modules with WebBroker is almost identical to thedevelopment of other types of programs, instead of building an actual application (besidesthe over-simplistic Apache1 example) I’ve created a new version of the BrokDemo example,already available as a CGI or ISAPI program To do this, I’ve taken the project file of an

Trang 8

Apache module from that example, added the local Web modules to it, and modified the project source code to reflect the proper module name and handler I’ve actually definedthem differently than the default, as the following code excerpt demonstrates:

http://localhost/scripts/brokcgi.exe/table

http://localhost/brokdemo/table

Not only is the latter URL simpler, but it hides the fact that we are running an applicationwith a /tableparameter In fact, it seems we are accessing a specific folder of the server Actu-ally, the Apache configuration file can be modified to also invoke CGI applications throughvirtual folders, which explains why CGI applications have a path-like command prefixing therequest Another related explanation is that Linux CGI applications, like any other executablefile, have no extension whatsoever, so their names still seem to be part of a path

Practical Examples

After this general introduction to the core idea of the development of server-side applicationswith WebBroker, let me end this part of the chapter with two simple practical examples Thefirst is a classic Web counter The second is an extension of the WebFind program presented

in the preceding chapter to produce a dynamic page instead of filling a list box

A Web Hit Counter

The server-side applications we’ve built up to now were based only on text Of course, youcan easily add references to existing graphics files What’s more interesting, however, is tobuild server-side programs capable of generating graphics that change over time

A typical example is a page hit counter To write a Web counter, we save the current number

of hits to a file and then read and increase the value every time the counter program is called.How do we return this information? If all we need is some HTML text with the number ofhits, the code is straightforward:

procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;

Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);

Trang 9

// read if the file exists

if FileExists (LogFileName) then begin

Reset (LogFile);

Readln (LogFile, nHit);

Inc (nHit);

end else

WARNING This simple file handling does not scale When multiple visitors hit the page at the same time,

this code may return false results or fail with a file I/O error because a request in another thread has the file open for reading while this thread tries to open the file for writing To sup- port a similar scenario, you’ll need to use a mutex (or a critical section in a multithreaded pro- gram) to let each subsequent thread wait until the thread currently using the file has completed its task.

What’s a little more interesting is to create a graphical counter that can be easily embeddedinto any HTML page There are basically two approaches for building a graphical counter: youcan prepare a bitmap for each digit up front and then combine them in the program, or you cansimply let the program draw over a memory bitmap to produce the graphic you want toreturn In the WebCount program, I’ve chosen this second approach

Basically, we can create an Image component that holds a memory bitmap, which we canpaint on with the usual methods of the TCanvasclass Then we can attach this bitmap to aTJpegImageobject Accessing the bitmap through the JpegImage component converts the

Trang 10

image to the JPEG format At this point, we can save the JPEG data to a stream and return

it As you can see, there are many steps, but the code is not really complex:

// create a bitmap in memory

FormatFloat (‘###,###,###’, Int (nHit)));

// convert to JPEG and output

You can see the effect of this program in Figure 22.9 To obtain it, I’ve added the followingcode to an HTML page:

<img src=”http://localhost/scripts/webcount.exe” border=0 alt=”hit counter”>

Trang 11

Searching with a Web Search Engine

In Chapter 21, I discussed the use of the Indy HTTP client component to retrieve the result

of a search on the Google Web site Now I’m going to extend the example a little, turning itinto a server-side application The WebSearch program on the companion CD, available as aCGI application or a Web App Debugger executable, has an action that simply returns theHTML retrieved by the search engine and a second action that fills a client data set compo-nent, then hooked to a table page producer This is the code of this second action:

const

strSearch = ‘http://www.google.com/search?as_q=borland+delphi&num=100’;

procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;

Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);

Trang 12

The GrabHtmlmethod is identical to the WebFind example, while the HtmlStringToCdsmethod is similar to corresponding method (which adds the items to a list box) and adds theaddresses and their textual descriptions by calling:

cds.InsertRecord ([0, strAddr, strText]);

The ClientDataSet component, in fact, is set up with three fields: the two strings plus aline counter This extra empty field is used to have the extra column in the table producer.The code fills the column in the cell-formatting event, which also adds the hyperlink:

procedure TWebModule1.DataSetTableProducer1FormatCell(Sender: TObject; CellRow, CellColumn: Integer; var BgColor: THTMLBgColor; var Align: THTMLAlign;

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

begin

if CellRow <> 0 then

case CellColumn of

0: CellData := IntToStr (CellRow);

1: CellData := ‘<a href=”’ + CellData + ‘“>’ + SplitLong(CellData) + ‘</a>’;

2: CellData := SplitLong (CellData);

end;

end;

The call to SplitLongis used to add some extra spaces within the output text, to avoid ing grid columns that are too large, as the browser won’t split the text on multiple linesunless it contains spaces or other special characters The result of this program is a ratherslow application (because of the multiple HTTP requests it must forward) producing outputlike Figure 22.10

hav-F I G U R E 2 2 1 0 :

The WebSearch program

shows the result of the

multiple searches done

on Google.

Trang 13

Active Server Pages

Another approach to the development of server-side applications is the use of scripting Beforelooking at the scripting technology embedded in the WebSnap framework, let me shortly dis-cuss Microsoft’s Active Server Pages (ASP) technology and how you can use Delphi to support

it The idea behind ASP is to add scripts to the HTML code, so that part of the text on a Webpage is directly available while other information can be added at run time on the server Theclient receives a plain HTML file The difference between this approach and ISAPI is thatyou don’t need to recompile a program on the server to see a change; you simply update thescript ASP offers a complex model, where you can attach persistent data to a session (forexample, a user moving from page to page of a section of your Web site) and to the entireapplication (the section of the Web site, regardless of the user)

ASP is quite a complex technology, and here I can only discuss it in relation to Delphi gramming One of the features of ASP is that it allows you to create COM objects within ascript, and you can write those COM objects in Delphi The Delphi IDE even provides spe-cific support classes and a wizard to help you build ASP objects Compared to ISAPI or CGI,one of the advantages is that your ASP object built in Delphi can get access to session andapplication information, exactly as an ASP script does This means we automatically get extrafeatures such as persistent user data built into our server-side object By building a compiledASP object, we can also increase the speed of complex server-side code (ASP scripts are notalways the best solution in term of performance.) But, again, I don’t want to discuss ASP indetail, only focus on Delphi support

pro-To try this out, simply create a new ActiveX library, and then start the Active Server ObjectWizard (from the ActiveX page of the File ➢ New dialog box) As you can see in Figure 22.11,the wizard has a couple of options You can build an object integrated with the ASP script byselecting the Page-Level Event Methods radio button, or an internal object (which can beinstalled as an MTS object) by using the Object Context option Only in the first case doesthe object automatically handle the OnStartPagemethod, which receives as parameter the

scripting context In both cases, however, the VCL classes you inherit from (TASPObjectandTASPMTSObject, respectively) have properties to access the Request, Response, Session, Server, and ApplicationASP objects

Trang 14

Once you’ve created the ASP object with the wizard (I’ve used the Page-Level EventMethods option for the AspTest example), Delphi will bring up the Type Library editor,where you can prepare a list of properties and methods for your ASP object Simply add thefeatures you need, and then write their code For example, you can write the followingsimple test method:

I’ve written the code used to set the property and the method invocation one after the

other, but they can even be in different pages This new dynamic property (Microsoft’s term

F I G U R E 2 2 1 1 :

The new Active Server

Object wizard

Trang 15

for these values added to an object) is saved in the session, so it depends on the current user.The Hellomethod can use the username to welcome them:

procedure Tasptest.Hello;

var

strName: string;

begin

strName := Session [‘UserName’];

Response.Write (‘<h3>Hello, ‘ + strName + ‘</h3>’);

Response.Write (‘<p>Page started at ‘ + TimeToStr (StartTime) + ‘</p>’);

end;

You can see the result of this and the previous method combined in Figure 22.12 The lastline of the method uses a variable that’s set when the page is first loaded, in the OnStartPagemethod (despite the name, this is not an event handler, but a method the ASP engine will call

as the page containing the object is activated):

procedure Tasptest.OnStartPage(const AScriptingContext: IUnknown);

The Web page generated by

the AspTest object I’ve built

with Delphi

Trang 16

Technically, this method retrieves the scripting context The TASPObjectbase class uses themethod to initialize all the ASP objects (including the two, Responseand Session, I use inthe code), surfacing them as properties.

To generate more complex HTML from the Delphi ASP object, you can use Producercomponents, optionally connecting them to a dataset In the AspTest example, I’ve added aTable component and a DataSetTableProducer, connected them as usual, and written the fol-lowing code to activate it:

to limit their expense to the Professional version of Delphi 6, which includes WebBroker).WebSnap has a few definitive advantages over the plain WebBroker, such as allowing formultiple pages, integrating server-side scripting, and XSL and Delphi 5 Internet Expresstechnology (these last two elements will be covered in the next chapter) Moreover, there aremany ready-to-use components for handling common tasks, such as users’ login, sessionmanagement, and so on Instead of listing all the features of WebSnap right away, though,I’ve decided to cover them in a sequence of simple and focused applications All of theseapplications have been built using the Web App Debugger, for testing purposes, but you’ll beable to easily deploy them using one of the other available technologies

Trang 17

The starting point of the development of a WebSnap application is a dialog box that youcan invoke either in the WebSnap page of the New items dialog box (File ➢ New ➢ Other) orusing the new Internet toolbar of the IDE The resulting dialog box, shown in Figure 22.13,allows you to choose the type of application (like in a WebBroker application) and to cus-tomize the initial application components (but you’ll be able to add more later on) The bot-tom portion of the dialog determines the behavior of the first page, usually the default orhome page of the program A similar dialog box is displayed also for subsequent pages.

If you go ahead, choosing the defaults and typing in a name for the home page, the dialogbox will create a project and open up a TWebAppPageModulefor you This module contains thecomponents you’ve chosen, by default:

• A WebAppComponents component is a container of all of the centralized services ofthe WebSnap application, such as the user list, core dispatcher, session services, and so

on Not all of its properties must be available, as an application might not need all ofthe available services

• One of these core services is offered by the PageDispatcher component, which ically) holds a list of the available pages of the application and defines the default one

(automat-• Another core service is given by the AdapterDispatcher component, which handlesHTML form submissions and image requests

The ApplicationAdapter is the first component we encounter of the adapters family.

These components offer fields and actions to the server-side scripts evaluated by the

F I G U R E 2 2 1 3 :

The options offered by the

New WebSnap Application

dialog box include the type

of server and a button for

the selection of the core

application components.

Trang 18

program Specifically, the ApplicationAdapter is a fields adapter that exposes the value

of its own ApplicationTitleproperty By entering a value for this property, it will bemade available to the scripts

• Finally, the module hosts a PageProducer that includes the HTML code of the page—

in this case, the default page of the program Unlike WebBroker applications, theHTML for this component is not stored inside its HTMLDocstring list property or refer-enced by its HTMLFileproperty The HTML file is an external file, stored by default inthe folder hosting the source code of the project and referenced from the applicationusing a statement similar to a resource include statement: {*.html}

Because the HTML file included by the PageProducer is kept as a separate file (theLocateFileService component will eventually help you for its deployment), you can edit it tochange the output of a page of your program without having to recompile the application.These possible changes relate not only to the fixed portion of the HTML file but also tosome of its dynamic content, thanks to the support for server-side scripting The defaultHTML file, based on a standard template, actually already has some scripting in it

The HTML file is visible within the Delphi editor with reasonably good syntax ing, simply by selecting the corresponding lower tab, such as WSnapDM.html in my simpleexample, shown in Figure 22.14 The editor also has other pages for a WebSnap module,including by default an HTML Result page, where you can see the HTML generated afterevaluating the scripts, and a Preview page hosting what a user will see inside a browser

highlight-F I G U R E 2 2 1 4 :

The Delphi 6 editor for a

WebSnap module includes

a simple HTML editor and a

preview of its output.

Trang 19

TIP If you prefer editing the HTML of your Web application with another more sophisticated

edi-tor, you can set up your choice in the Internet page of the Environment Options dialog box Within this page, you can see a list of file extensions Selecting the Edit button for one of these groups of extensions, you can choose an external editor to use for these files At this point, the External Editor button of the Internet toolbar will become active.

The standard HTML template used by WebSnap adds to any page of the program its titleand the application title, using simple script lines such as:

<h1><%= Application.Title %></h1>

<h2><%= Page.Title %></h2>

We’ll get back to the scripting in a while But let me start the development of the WSnap1example by simply creating a program with multiple pages Before I do this, let me finish thisoverview by showing you the extra source code of a sample Web page module:

uses WebReq, WebCntxt, WebFact, Variants;

function home: Thome;

of the page and its behavior

Trang 20

Managing Multiple Pages

The first notable difference between WebSnap and WebBroker is that, instead of having a singledata module with multiple actions eventually connected to producer components, WebSnaphas multiple data modules, each corresponding to an action and having a producer componentwith an HTML file attached to it Actually, you can still add multiple actions to a page/mod-ule, but the idea is that you structure applications around pages and not around actions Likeactions, the name of the page is indicated in the request path

As an example, I’ve added to the WebSnap application, built with default settings, twomore pages For the first, in the New WebSnap Page Module dialog (see Figure 22.15), I’ve

chosen a standard page producer and given to it the name date For the second, I’ve gone with a DataSetPageProducer and given it the name country After saving the files, you can

start testing the application Thanks to some of the scripting I’ll discuss later, each page listsall of the available pages (unless you’ve unchecked the Published check box in the New Web-Snap Page Module dialog)

All of the pages will be rather empty, but at least we have the structure in place To plete the home page, I’ve simply edited its linked HTML file directly For the date page, I’ve

com-F I G U R E 2 2 1 5 :

The New WebSnap Page

Module dialog box

Trang 21

employed the same approach as a WebBroker application I’ve added to the HTML textsome custom tags, as in:

<p>The time at this site is <#time>.</p>

and I’ve added some code to the OnTagevent handler of the producer component to replacethis tag with the current time

For the third page, the country page, I’ve modified the HTML to include tags for the ous fields of the country table, as in:

Table1.Open;

Table1.First;

The fact that a WebSnap page can be very similar to a portion of a WebBroker application(basically an action tied to a producer) is quite important, in case you want to port existing Web-Broker code to this new architecture You can even port your existing DataSetTableProducercomponents to the new architecture Technically, you can generate a new page, remove its pro-ducer component, replace it with a DataSetTableProducer, and hook this component to thePageProducerproperty of the Web page module In practice, this approach would cut out theHTML file of the page and its scripts

In the WSnap1 program, I’ve used a better technique I’ve added a custom tag

(<#htmltable>) to the HTML file and used the OnTagevent of the page producer to add tothe HTML the result of the data set table:

if TagString = ‘htmltable’ then

ReplaceText := DataSetTableProducer1.Content;

Trang 22

Server-Side Scripts

If having multiple pages in a server-side program, each associated with a different page ule, changes the way you write a program, having the server-side scripts at hand offers aneven more powerful approach For example, the standard scripts of the WSnap1 exampleaccount for the application and page titles, and for the index of the pages This is generated

mod-by an enumerator, the technique used to scan a list from within a WebSnap script code Let’shave a look at it:

<table cellspacing=”0” cellpadding=”0”><td>

<% e = new Enumerator(Pages)

s = ‘’

c = 0 for (; !e.atEnd(); e.moveNext()) {

if (e.item().Published) {

} }

if (c>1) Response.Write(s)

%>

</td></table>

NOTE Typically, WebSnap scripts are written in JavaScript, an object-based language very common

for Internet programming because it is the only scripting language generally available in browsers (on the client side) JavaScript, technically indicated as ECMAScript, borrows the core syntax of the C language and has almost nothing to do with Java Actually, WebSnap uses Microsoft’s ActiveScripting engine, which supports both JScript (a variation of JavaScript) and VBScript.

Inside the single cell of this table (which, oddly enough, has no rows), the script outputs astring with the Reponse.Writecommand This string is built with a forloop over an enumer-ator of the pages of the application, stored in the Pagesglobal entity The title of each page isadded to the string, only if the page is published and using an hyperlink only for pages differ-ent than the current one Having this code in a script, instead of hard-coded into a Delphicomponent, allows you to pass it over to a good Web designer to turn it into something a littlemore visually appealing

Trang 23

TIP To publish or unpublish a page, don’t look for a property in the Web page module This status

is controlled by a flag of the AddWebModuleFactory method called in the Web page module initialization code Simply comment or uncomment this flag to obtain the desired effect.

As a sample of what you can do with scripting, I’ve added to the WSnap2 example (an

exten-sion of the WSnap1 example) a demoscript page The script of this page can generate a full table

of multiplied values with the following scripting code (see Figure 22.16 for its output):

<table border=1 cellspacing=0>

The WSnap2 example has a

custom menu stored in an

included file reference by

each page.

Trang 24

In this script, the <%=symbol replaces the longer Response.Writecommand Anotherimportant feature of server-side scripting is the inclusion of pages within other pages Forexample, if you plan on modifying the menu, you can include the related HTML and script

in a single file, instead of changing it and maintaining it in multiple pages File inclusion isgenerally done with a statement like:

<! #include file=”menu.html” >

In Listing 22.1, you can find the complete source code of the include file for the menu, erenced by all the other HTML files of the project In Figure 22.16, you can see an example

ref-of this menu, across the top ref-of the page with the table generation script mentioned earlier

Listing 22.1: The menu.html file included in each page of the WSnap2 example

else Response.Write (‘<td>’ + e.item().Title + ‘</td>’) }

Trang 25

Besides these global objects, within a script you can access all the adapters available in thecorresponding Web page module (Adapters in other modules, including shared Web datamodules, must be referenced by prefixing their name with the Modulesobject and the corre-sponding module.) The idea is that adapters allow you to pass information from your com-piled Delphi code to the interpreted script, providing a scriptable interface to your Delphiapplication Adapters contain fields that represent data and host actions that represent com-mands The server-side scripts can access these values and issue these commands, passingspecific parameters to them

NOTE Technically, adapters implement an IDispatch interface that can be accessed by the script

through an Active Scripting engine language, such as JavaScript The page producer nent is responsible for invoking the Active Scripting engine and has a property indicating the language of the script Because of this, you’ll have to register two type libraries (and deploy the corresponding DLLs) to make this work on a machine where Delphi is not installed: Web- BrokerScript.tlb and stdvcl40.dll As the first is a type library, it must be installed with Delphi’s TRegSvr utility (available in the bin subfolder) rather than Microsoft’s RegSvr32 pro- gram Of course, the server computer must also have Microsoft Active Scripting Engine installed in order to work.

compo-Adapter Fields

For simple customizations, you can simply add new fields to the specific adapters For instance,

in the WSnap2 example, I’ve added a custom field to the application adapter After selectingthis component, you can either open up its Fields editor (accessible via its local menu) or simplywork within the Object TreeView After adding a new field (called Countin the example), youcan assign a value to it in its OnGetValueevent As I want to count the hits (or requests) on anypage of the Web application, I’ve also handled the OnBeforePageDispatchevent of the global

PageDispatcher component Here is the code of the two methods:

procedure Thome.PageDispatcherBeforeDispatchPage(Sender: TObject;

const PageName: String; var Handled: Boolean);

Trang 26

Of course, I could have used the page name to also count hits on each specific page (and Icould have added some support for persistency, as the count is reset every time you run a newinstance of the application) Now that I’ve added a custom field to an existing adapter (corre-sponding to the Applicationscript object), I can access it from within any script, like this:

<p>Application hits since last activation:

<%= Application.Count.Value %></p>

Adapter Components

In the same way, you can also add custom adapters to specific pages If you need to pass along

a few fields, use the generic Adapter component Other custom adapters (besides the globalApplicationAdapter we’ve already used) include these:

• The PagedAdapter component has built-in support for showing its content over multiplepages

• The DataSetAdapter component is used to access a Delphi dataset from a script and iscovered in the next section

• The StringValuesList holds a list of name/value pairs, like a string list, and can be useddirectly or to provide a list of values to an adapter field The inherited DataSetValues-List adapter has the same role but grabs the list of name/value pairs from a dataset,providing support for lookups and other selections

• User-related adapters, such as the EndUser, EndUserSession, and LoginForm

adapters, are used to access user and session information and to build a login form forthe application, automatically tied to the users list I’ll cover these adapters in the sec-tion “Sessions, Users, and Permissions” later in this chapter

Using the AdapterPageProducer

Most of these components are used in conjunction with an AdapterPageProducer nent The AdapterPageProducer, in fact, can generate portions of script after you visually

compo-design the desired result As an example, I’ve added to the WSnap2 application the inout

page, which has an adapter with two fields, one standard and one Boolean:

object Adapter1: TAdapter

Trang 27

object TAdapterFields

object Text: TAdapterField

OnGetValue = TextGetValue

end object Auto: TAdapterBooleanField

OnGetValue = AutoGetValue

end end

end

The adapter has also a couple of actions, used to post the current user input and to add aplus sign to the text The same plus sign is added anyway when the Autofield is enabled.Developing the user interface for this form, and the related scripting, would take some timeusing plain HTML But the AdapterPageProducer component (used in this page) has anintegrated HTML designer, which Borland calls Web Surface Designer Using this tool, youcan visually add a form to the HTML page and add an AdapterFieldGroup to it Connectthis field group to the adapter to have editors for the two fields automatically displayed.Then you can add an AdapterCommandGroup and connect it to the AdapterFieldGroup, tohave buttons for all of the actions of the adapter You can see an example of this designer inFigure 22.17

F I G U R E 2 2 1 7 :

The Web Surface Designer

of Delphi 6 for the inout

page of the WSnap2

example, at design time

Trang 28

To be more precise, the fields and buttons are automatically displayed if the Fieldsand AddDefaultCommandsproperties of the field group and command group are set.The effect of the visual operations I’ve done to build this form are summarized in the follow-ing DFM snippet:

AddDefault-object AdapterPageProducer: TAdapterPageProducer

object AdapterForm1: TAdapterForm

object AdapterFieldGroup1: TAdapterFieldGroup

of the buttons is pressed, we have to retrieve the text passed by the user, which is not matically copied into the corresponding adapter field You can obtain this effect by looking atthe ActionValueproperty of these fields, which is set only if something was entered (for thisreason, when nothing is entered we set the Boolean field to False) To avoid repeating thiscode for both actions, I’ve placed it in the OnBeforeExecuteActionevent of the Web pagemodule:

auto-procedure Tinout.Adapter1BeforeExecuteAction(Sender, Action: TObject;

Params: TStrings; var Handled: Boolean);

Trang 29

Notice that each action can have multiple values (in case of components allowing multipleselections); but this is not the case, so we can simply grab the first element Finally, I’ve writ-ten the code for the OnExecuteevents of the two actions:

procedure Tinout.AddPlusExecute(Sender: TObject; Params: TStrings);

NOTE The AdapterPageProducer component has specific support for cascading style sheets (CSS).

You can define the CSS for a page using either the StylesFile property or Styles string list Any element of the editor of the items of the producer, at this point, can define a specific style

or choose one of the styles of the attached CSS This last operation (which is the suggested approach) is accomplished using the StyleRule property.

Scripts Rather Than Code?

Even this simple example of the combined use of an adapter and an adapter page producer,with its visual designer, shows the power of this architecture However, this approach also has

a big drawback By letting the components generate the script (in the HTML, you have onlythe <#SERVERSCRIPT>tag), you save a lot of development time, but at the same time you end

up mixing the script with the code, so that changes to the user interface will require updatingthe program The division of responsibilities between the Delphi application developer and theHTML/script designer is lost And, ironically, we end up having to run a script to accom-plish something the Delphi program could have done right away, possibly even much faster!

So my opinion is that this is a very powerful architecture and a huge step forward fromWebBroker, but it has to be used with some care, to avoid misusing some of these technolo-gies just because they are simple and powerful (and they are indeed) For example, it might

be worth using the designer of the AdapterPageProducer to generate the first version of apage, then grabbing the generated script and copying to the HTML of a plain PageProducer,

so that a Web designer can modify the script with a specific tool

Trang 30

For nontrivial applications, I tend to prefer the possibilities offered by XML and XSL,which are available within this architecture even if they don’t have a central role More onthis specific topic in the next chapter.

WebSnap and Databases

One of the areas where Delphi has always shined is database programming For this reason,

it is not surprising to see a lot of support for handling datasets within the WebSnap work Specifically, you can use the DataSetAdapter component to connect to a dataset anddisplay its values in a form or a table using the visual editor of the AdapterPageProducercomponent

frame-A WebSnap Data Module

As an example, I’ve built a new WebSnap application (called WSnapTable) with an PageProducer as its main page to display a table in a grid and another AdapterPageProducer

Adapter-in a secondary page to show a form with a sAdapter-ingle record I’ve also added to the application aWebSnap Data Module, as a container of the dataset components The data module has aClientDataSet wired to a dbExpress dataset through a provider and based on an InterBaseconnection, as shown here:

object ClientDataSet1: TClientDataSet

‘select CUST_NO, CUSTOMER, ADDRESS_LINE1, CITY, STATE_PROVINCE, ‘ +

‘ COUNTRY from CUSTOMER’

Trang 31

Delete, Edit, and Apply) You can add them explicitly to the Actionsand Fieldscollections

to exclude some of them and customize their behavior, but this is not always required.Like the PagedAdapter, the DataSetAdapter has a PageSizeproperty where you can indi-cate the number of elements to display in each page The component also has commandsthat you can use to navigate among pages This approach is particularly suitable when youwant to display a large dataset in a grid These are the adapter settings for the main page ofthe WSnapTable example:

object DataSetAdapter1: TDataSetAdapter

of numbers for the pages, so that a user can jump to each of them directly The AdapterGridcomponent has the default columns plus an extra one hosting a couple of commands, Editand Delete The bottom command group has a button used to create a new record You cansee an example of the output of the table in Figure 22.18 and the complete settings of theAdapterPageProducer in Listing 22.2

F I G U R E 2 2 1 8 :

The page shown by the

WSnapTable example at

start up includes the initial

portion of a paged table.

Trang 32

object AdapterCommandGroup1: TAdapterCommandGroup

Trang 33

end object AdapterCommandGroup2: TAdapterCommandGroup

end

In this rather long listing, there are a few things to notice First, the grid has the Modeproperty set to Browse, other possibilities being Edit, Insert, and Query This datasetdisplay mode for adapters determines the type of user interface (text or edit boxes and otherinput controls) and the visibility of other buttons (for example, Apply and Cancel buttons areonly present in the edit view, the opposite for the Edit command)

Adapter-NOTE The adapter mode can also be modified using server-side script and accessing Adapter.Mode.

Second, I’ve modified the display of the commands inside the grid, using the ctAnchorvalue for the DisplayTypeproperty instead of the default button style Similar properties areavailable in most components of this architecture to tweak the HTML code they produce

Editing the Data in a Form

Finally, some of the commands are connected to a different page, the page that is going to bedisplayed after the commands are invoked For example, the editcommand has its PageNameproperty set to formview This second page of the application has an AdapterPageProducerwith components hooked to the same DataSetAdapter of the other table, so that all of therequest will be automatically synchronized Selecting the edit command, in fact, the programwill open the secondary page displaying the data of the record corresponding to the command.Listing 22.3 shows the details of the page producer of the second page of the program Again,building the HTML form visually using the Delphi specific designer (see Figure 22.19) was avery fast operation

Trang 34

Listing 22.3: AdapterPageProducer settings for the formview page

object AdapterPageProducer: TAdapterPageProducer

object AdapterForm1: TAdapterForm

object AdapterErrorList1: TAdapterErrorList

Adapter = table.DataSetAdapter1

end object AdapterCommandGroup1: TAdapterCommandGroup

ActionName = ‘Cancel’

PageName = ‘table’

end object CmdDeleteRow: TAdapterActionButton

ActionName = ‘DeleteRow’

Caption = ‘Delete’

F I G U R E 2 2 1 9 :

The formview page shown

by the WSnapTable example

at design time, in the Web

Surface Designer (or

AdapterPageProducer

editor)

Trang 35

PageName = ‘table’

end end object AdapterFieldGroup1: TAdapterFieldGroup

DisplayWidth = 27

FieldName = ‘CUSTOMER’

end object FldADDRESS_LINE1

object FldCITY

object FldSTATE_PROVINCE

object FldCOUNTRY

end end

end

In the listing, you can see that all the operations send the user back the main page and thatthe AdapterModeis set to Edit, unless there are update errors or conflicts In this case, thesame page is displayed again, with a description of the errors obtained by adding an Adapter-ErrorList component at the top of the form

The second page is not published, because selecting it without referring to a specificrecord would make very little sense To unpublish the page, I’ve simply commented the cor-responding flag in the initialization code Finally, to make the changes to the database persis-tent, you can call the ApplyUdpatesmethod in the OnAfterPostand OnAfterDeleteevents ofthe ClientDataSet component hosted by the data module Another problem (which I haven’tfixed) relates to the fact that the SQL server assigns the ID of each customer, so that whenyou enter a new record, the data in the ClientDataSet and in the actual database are notaligned any more This can cause Record Not Found errors, a problem I’ve not fixed in theexample

Master/Detail in WebSnap

The DataSetAdapter component has specific support for master/detail relationships betweendatasets After you’ve created the relationship among the datasets, as usual, define an adapterfor each dataset and then connect the MasterAdapterproperty of the adapter of the detaildataset Setting up the master/detail relationship between the adapters makes them work in a

Trang 36

more seamless way For example, when you change the work mode of the master, or enternew records, the detail automatically enters into Edit mode or is refreshed.

In the WSnapMD example, I’ve defined such a relationship using two SQLClientDataSetcomponents connected with an InterBase database via dbExpress All these components andthe related adapters are in a Web data module, which has the structure displayed in the designview in Figure 22.20 I haven’t provided a complete listing of the details of these components,

as it shouldn’t be too difficult for you to rebuild it after looking at the example itself

The only page of this WebSnap application has an AdapterPageProducer component hooked

to both dataset adapters The form of this page, in fact, has both a field group hooked to themaster and a grid connected with the detail Unlike other examples, I’ve tried to improve theuser interface by adding custom attributes for the various elements, as you can see in the fol-lowing detailed excerpt:

object AdapterPageProducer: TAdapterPageProducer

object AdapterForm1: TAdapterForm

Custom = ‘Border=”1” CellSpacing=”0” CellPadding=”10” ‘ +

‘BgColor=”Silver” align=”center”’

object AdapterCommandGroup1: TAdapterCommandGroup

DisplayComponent = AdapterFieldGroup1

F I G U R E 2 2 2 0 :

The design view of the

Web data module of the

WSnapMD example.

Both the datasets and

the adapters have a

master/detail relationship.

Trang 37

ActionName = ‘PrevRow’

Caption = ‘ Previous ‘

end object CmdNextRow: TAdapterActionButton

ActionName = ‘NextRow’

Caption = ‘ Next ‘

end object CmdLastRow: TAdapterActionButton

ActionName = ‘LastRow’

Caption = ‘ Last ‘

end end object AdapterFieldGroup1: TAdapterFieldGroup

Custom = ‘BgColor=”Silver”’

Adapter = WDataMod.dsaDepartment

AdapterMode = ‘Browse’

end object AdapterGrid1: TAdapterGrid

TableAttributes.BgColor = ‘Silver’

TableAttributes.CellSpacing = 0 TableAttributes.CellPadding = 3

HeadingAttributes.BgColor = ‘Gray’

Adapter = WDataMod.dsaEmployee

AdapterMode = ‘Browse’

object ColEMP_NO: TAdapterDisplayColumn

object ColFIRST_NAME: TAdapterDisplayColumn

object ColLAST_NAME: TAdapterDisplayColumn

object ColDEPT_NO: TAdapterDisplayColumn

object ColJOB_CODE: TAdapterDisplayColumn

object ColJOB_COUNTRY: TAdapterDisplayColumn

object ColSALARY: TAdapterDisplayColumn

end end

end

I’ve used a gray background, displayed some of the grid borders (HTML grids are usedvery often by the Web surface designer), centered most of the elements, and added somespacing Notice that I’ve added some extra spaces to the button captions, to avoid them beingtoo small The effect of these settings (and the master/detail structure) is visible at run time

in Figure 22.21

Trang 38

Sessions, Users, and Permissions

Another very interesting area of the WebSnap architecture is its support for sessions andusers Sessions are supported using a classic approach: temporary cookies These cookies aresent to the browser, so that following requests from the same user can be acknowledged bythe system By adding data to a session instead of an application adapter, you can have datathat depends on the specific session or user (although a user can possibly run multiple ses-sions by opening multiple browser windows on the same computer) For supporting sessions,the application keeps data in memory, so this feature is not available in case of CGI pro-grams

Using Sessions

To underline the importance of this type of support, I’ve built a WebSnap application with asingle page showing both the total number of hits and the total number of hits for each ses-sion The program has a SessionService component with default values for its MaxSessionsand DefaultTimeoutproperties For every new request, the program increases both an nHitsprivate field of the page module and the SessionHitsvalue of the current session:

procedure TSessionDemo.WebAppPageModuleBeforeDispatchPage(Sender: TObject;

Trang 39

const PageName: String; var Handled: Boolean);

NOTE The WebContext object (of type TWebContext) is a thread variable, created by WebSnap for

each request, which provides thread-safe access to other global variables used by program.The associated HTML displays status information both by using some custom tags evalu-ated by the OnTagevent of the page producer and some script, evaluated by the engine Here

is the core portion of the HTML file:

<h3>Plain Tags</h3>

<p>Session id: <#SessionID>

<br>Session hits: <#SessionHits></p>

<h3>Script</h3>

<p>Session hits (via application): <%=Application.SessionHits.Value%>

<br>Application hits: <%=Application.Hits.Value%></p>

The parameters of the output are provided by the OnTagevent handler and the OnGetValueevents of the fields:

procedure TSessionDemo.PageProducerHTMLTag(Sender: TObject; Tag: TTag;

const TagString: String; TagParams: TStrings; var ReplaceText: String); begin

if TagString = ‘SessionID’ then

ReplaceText := WebContext.Session.SessionID

else if TagString = ‘SessionHits’ then

ReplaceText := WebContext.Session.Values [‘SessionHits’]

Trang 40

TIP In this example, I’ve voluntarily used both the traditional WebBroker tag replacement and the

newer WebSnap adapter fields and scripting, so that you can compare the two approaches and keep in mind that they are both available in a WebSnap application.

Requesting Login

Besides generic sessions, WebSnap also has specific support for users and login-based authorizedsessions You can add to an application a list of users (with the WebUserList component), eachwith a name and a password My impression is that this component is rather rudimentary in thedata it can store Instead of filling it with your list of users, however, you can keep the list in adatabase table (or in some other proprietary format) and use the events of the WebUserListcomponent to retrieve your custom users data and check the user passwords

You’ll generally also add to the application the SessionService and Adapter components At this point, you can ask the users to log in, indicating for each pagewhether it can be viewed by everyone or only by logged-in users This is accomplished bysetting the wpLoginRequired flag in the constructor of the TWebPageModuleFactoryandTWebAppPageModuleFactoryclasses in the initialization code of the Web page unit

EndUserSession-F I G U R E 2 2 2 2 :

Two instances of the

browser operate on two

different sessions of the

same WebSnap application.

Ngày đăng: 12/08/2014, 21:20

TỪ KHÓA LIÊN QUAN