We've read many articles here in the Community pages
about WebSnap and Web Services, but not anything yet about their integration.
What about generating custom dynamic content in your elegant WebSnap-based
portal through a Web Service? Isn't that what it is all about? In this
article you will learn one way to perform that integration.
First things first
Before we start this informative, erudite, and highly entertaining article, let's get acquainted with
some techniques and concepts:
- Building a GUI-based Web consumer. We can find various articles here about
his subject. All of them are clearly and beautifully written, so I won't get into
any details here
- Building WebSnap applications. Ditto.
- Working with adapters. Adapters are components especially created to help
us expose Object Pascal classes and other OP functionality to the WebSnap
infrastructure. Which means that other WebSnap components (like the
AdapterPageProducer) can extract and manipulate the adapter's related
fields and actions. Fields and Actions are two collection properties available
in the TAdapter class which let you expose data (Fields) and functionality
(Actions) to your ActiveScript-addicted devteam fellows and WebSnap components.
This will become a little clearer when you read through this article.
Second things second...
Let's explain things using an advanced form of step-by-step method:
Step one (um, uno, ein, ekhad -- those are steps for our international Community
members; I promise, it's the
one and only joke in this article <fingers crossed>): Creating a WebSnap
application
Start by selecting File | New | Other | WebSnap | WebSnap Application.
1.1 - In the New WebSnap Application Wizard, select the Server
Type you like best. (I used CGI because I wouldn't want to restart IIS every
time I
want to update my ISAPI DLL.)
1.2 - Still in the New WebSnap Application Wizard, set the Page
Name to "Home" and press OK.
1.3 - Look at the created PageModule...beautiful, eh? Save
All. Unit1 is HomeU.pas and Project1 is WebServiceSnap.dpr.
Step 2: Import the WSDL into your WebSnap application
We must now choose the Web Service we will be calling in our
WebSnap Application. I will use
the AreaCode Web Service, which can be found and invoked through the following
WSDL, written by Dave Bathia:
http://www.taragroup.com/bin/AreaCode.exe/wsdl/iGetArea
Here is how we do it:
2.1 - Select File | New | Other | WebServices | Web Service
Importer.
2.2 - In the WSDL Schema edit box, paste the WSDL for Dave Bathia's
AreaCode Web Service from above.
2.3 - Press "Generate" and wait a couple of
seconds while the IDE fetches the WSDL from its Web site, parses it, and creates
a unit that reflects its structure.
2.4 - Save the created unit as AreaCodeIntf.pas:
Unit AreaCodeIntf;
interface
uses Types, XSBuiltIns;
type
IGetArea = interface(IInvokable)
['{8D2937CE-62F8-46BA-8B47-52199394FB02}']
function GetUS(const iAreaCode: Integer): WideString; stdcall;
function GetInternational(const iCountryCode: Integer): WideString; stdcall;
end;
implementation
uses InvokeRegistry;
initialization
InvRegistry.RegisterInterface(TypeInfo(IGetArea), 'urn:GetAreaIntf-IGetArea', '');
end.
Step 3: Create a WebDataModule with a TAdapter
Why will we be using an adapter? Because we want to pass
parameters to our Web Service methods and display the result through the
WebSnap framework standard components. And why use a WebDataModule? Because this
Adapter will be referenced throughout your application and you will be able to
reuse it in every other WebSnap Application you write.
3.1 - Create a WebDataModule by selecting File | New | Other
| WebSnap | WebSnap Data Module.
3.2 - Accept the default settings in the New WebSnap Data Module
Wizard and set the name of the new WebDataModule to "wdmAreaCode."
The two parameters of the New WebSnap Data Module Wizard are:
-
Creation: can be "On Demand" (only when referenced:
memory-friendly, slower) or "Always" (created at every
request, whether referenced or not: slow startup, faster references).
-
Caching: can be "Cache Instance" (leave the
DataModule instance created even with no references: fast) or "Destroy
Instance" (destroy the DataModule without references: memory-friendly,
slow).
3.3 - Save the WebDataModule unit as AreaCodeWDM.pas.
3.4 - Drop a TAdapter into your wdmAreaCode WebDataModule and name
it adpAreaCode.
Step 4: Configure the adpAreaCode TAdapter
Now we must create the fields and actions that are necessary to
make our adapter adapt itself to our Web Service.
4.1 - If you take a close look (Hey, not so close or you
will be seeing pixels! Whoops. Sorry, another joke. Last one, I
swear.)
Let me start over:
4.1 - If you take a not-too-close look at the AdapterIntf unit,
you will see that each functios in the interface receives one Integer parameter (the Area Code or Country Code) and returns a WideString containing the result
of the Area Code or Country Code lookup. Let's mirror these in the adpAreaCode
fields. Double-click the adpAreaCode adapter and use the toolbar's New Item button
to add four fields like the following (select AdapterField and click
"OK" in the popup 4 times):
| Name |
DisplayLabel |
ReadOnly |
| adfAreaCode |
Area Code: |
False (default) |
| adfCountryCode |
Country Code: |
False (default) |
| adfAreaCodeResult |
Area Code Lookup Result: |
True |
| adfCountryCodeResult |
Country Code Lookup Result: |
True |
4.2 - Now let's configure the OnGetValue event of the
adfAreaCode and adfCountryCode fields:
procedure TwdmAreaCode.adfAreaCodeGetValue(Sender: TObject;
var Value: Variant);
begin
//the value will be passed by the AdapterForm that renders this
//adapter's fields and actions
Value := Request.ContentFields.Values['adfAreaCode'];
end;
procedure TwdmAreaCode.adfCountryCodeGetValue(Sender: TObject;
var Value: Variant);
begin
//the value will be passed by the AdapterForm that renders this
//adapter's fields and actions
Value := Request.ContentFields.Values['adfCountryCode'];
end;
The comments will be clearer later in this article when we get to the AdapterForm that will render the HTML
form from the adpAreaCode adapter. The OnGetValue event is triggered in the WebSnap framework whenever
some code requests the value of a TAdapter's Field.
4.3 - To configure the OnGetValue event for the other two fields (adfAreaCodeResult and adfCountryCodeResult), we must first declare two
private attributes in our wdmAreaCode class: GetUSResult and
GETInternationalResult, both strings:
type
TwdmAreaCode = class(TWebDataModule)
adpAreaCode: TAdapter;
adfAreaCode: TAdapterField;
adfCountryCode: TAdapterField;
adaCountryCode: TAdapterFields;
adfAreaCodeResult: TAdapterField;
adfCountryCodeResult: TAdapterField;
procedure adfAreaCodeGetValue(Sender: TObject; var Value: Variant);
procedure adfCountryCodeGetValue(Sender: TObject; var Value: Variant);
private
{ Private declarations }
//The two following fields will hold the Web Service
//methods return values to be used in the OnGetValue
//of their corresponding AdapterFields
GetUSResult : String;
GetInternationalResult : String;
public
{ Public declarations }
end;
The values of these attributes will be set later when we
implement our adapter's actions.
4.4 - Now we can configure the OnGetValue events of our remaining
AdapterFields:
procedure TwdmAreaCode.adfAreaCodeResultGetValue(Sender: TObject;
var Value: Variant);
begin
Value := GetUSResult;
end;
procedure TwdmAreaCode.adfCountryCodeResultGetValue(Sender: TObject;
var Value: Variant);
begin
Value := GetInternationalResult;
end;
4.5 - To create the adapter's actions, which will call the Web Service for
us, we must first drop a THTTPRIO component (found in the Web Services component
palette page) into our wdmAreaCode WebDataModule and
configure it. Press F12 to switch to the designer.
4.6 - Now we create and implement our adpAreaCode adapter actions by
clicking on the ellipsis button of the adpAreaCode actions property in the
object inspector:
| Name |
DisplayLabel |
| adaGetUS |
Get US Area Code |
| adaGetInternational |
Get International Country Code |
4.5 - Use the AreaCodeIntf unit in your AreaCodeWDM (the current) unit.
4.6 - The actions OnExecute events are coded as a normal Web Service Consumer code:
procedure TwdmAreaCode.adaGetUSExecute(Sender: TObject; Params: TStrings);
var
Srv : IGetArea;
begin
Srv := HTTPRIO1 as IGetArea;
try
//the GetUSResult below is the same private attribute
//previously returned bt the OnGetValue of adfAreaCode
GetUSResult:= Srv.GetUS(adfAreaCode.Value);
finally
Srv := nil;
end;
end;
procedure TwdmAreaCode.adaGetInternationalExecute(Sender: TObject;
Params: TStrings);
var
Srv : IGetArea;
begin
Srv := HTTPRIO1 as IGetArea;
try
//the GetInternationalResult below is the same private attribute
//previously returned bt the OnGetValue of adfCountryCode
GetInternationalResult:= Srv.GetInternational(adfCountryCode.Value);
finally
Srv := nil;
end;
end;
The private attributes previously declared and used in the OnGetValue implementation
for the adfAreaCodeResult and adfCountryCodeResult fields are used
as the receivers of the return value of the corresponding methods of the
AreaCode Web Service (step 4.4).
4.7 - Save everything.
Step 5: Building the HTML Front-End
Now that our adapter is completely configured, lets create the adapter form
that will be used to supply the values for the parameters of our Web Service.
5.1 - Select File | New | Other | WebSnap | WebSnap Page Module.
5.2 - On the New WebSnap Page Module Wizard set the Producer Type to
AdapterPageProducer and the Page Name and Title to CallWebService. Press the OK
button. You are presented with a new Unit/WebSnap Page Module with an
AdapterPageProducer.
5.3 - Use the AreaCodeWDM WebDataModule unit. Save your PageModule unit as
CallWS.pas.
5.4 - Double-click the AdapterPageProducer. You are presented with the
CallWebService.AdapterPageProducer editor.
5.5 - Using the New Item button in the AdapterPageProducer editor toolbar,
create an AdapterForm.
5.6 - With the AdapterForm selected, add an AdapterFieldGroup using the same
button as in 5.5.
5.7 -A warning appears in the "Browser" tab saying something like
"Hey, where's the adapter for the AdapterForm?" This is how
Delphi tells you that every AdapterFieldGroup should be linked to an adapter
so its fields are displayed. Set the Adapter property of the
AdapterFieldGroup to wdmAreaCode.adpAreaCode.
5.8 - Oh nooooo! Not all the fields...pleeeeease! OK...calm down...let's
correct that. Right-click the AdapterFieldGroup1 and select "Add
Field." In the Add Field pop-up window, select the adfAreaCode field and
press the OK button. Better now, isn't it?
5.9 - Select the AdapterForm component and press the New Item button in the
toolbar. Select AdapterCommandGroup and press OK. Set the
AdapterCommandGroup DisplayComponent property to the AdapterFieldGroup created
in 5.6. (If you don't do that, you will see a warning in the Browser tab with something like
"There is no DisplayComponent for that AdapterCommandGroup of
yours!")
5.10 - Again, a button is created for each of the adapter's actions. We are interested here
only in the adaGetUS action, so right-click your
AdapterCommandGroup and select "Add Commands." In the pop-up window,
select adaGetUS and press OK. Now we have a single button for our desired
action.
5.11 - With your CmdadaGetUS command selected, go to the Object Inspector and
type "AreaCodeResult" in its
PageName property. This will ensure that after executing the action, the next
page to appear will be a still-to-be-created "AreaCodeResult"
WebSnap Page Module. Write "AreaCodeResult" on a scrap of paper and
save your work.
5.12 - Repeat the steps above (from 5.6 to 5.11), selecting the corresponding
fields and actions for the International Country Code lookup (adfCountryCode and
adaGetInternational). The PageName property for the created
Command will be "InternationalResult" -- write it on the scrap of
paper.
If you did everything correctly, you will end up with the following:
Step 6: Creating the result PageModules
We must now create two PageModules to display the results. So take out that
scrap of paper...what, you can't find it?
Hey! Look -- my dog just ate it! What will I do? Either I'll have to use
my super-ultra mental data retrieval powers or...scroll this article up in the
browser and look up their names in steps 5.11 and 5.22.
Oh wait, here's the paper after all.
6.1 - Select File | New | Other | WebSnap | WebSnap Page Module.
6.2 - On the New WebSnap Page Module Wizard, set the Producer
Type to AdapterPageProducer and the Page Name and Title to "AreaCodeResult"
and uncheck the Published check box. Press the OK button. You are presented with
a new Unit/WebSnap Page Module with an AdapterPageProducer. Save it as
AreaCodeResU.pas.
6.3 - Double click the AdapterPageProducer and add an
AdapterForm using the New Item button in the Toolbar.
6.4 - My dog ate this step...maybe there was nothing
interesting here. Ah, yes: Use the AreaCodeWDM unit.
6.5 - Using the same techniques as in Step 5 above, create an
AdapterFieldGroup that points to the adpAreaCode Adapter with two fields:
| Field |
ViewMode |
| adfAreaCode |
vmDisplay |
| adfAreaCodeResult |
vmDisplay |
The resulting AdapterPageProducer looks like this:
6.6 - Create another WebSnap Page Module by repeating the steps 6.1-6.6 above with the adfCountryCode and
adfCountryCodeResult adapter fields. Name the WebSnap Page Module you create
"InternationalResult."
6.7 - Save all (naming the last created unit InternationalResU.pas)
and compile.
Step 7: Wrapping things up
Now, we must deploy our files to our preferred Web
server. If you are using IIS, for example, you will have to copy the
WebServiceSnap.exe file and the html files generated for our PageModules to your
"scripts" directory or an equivalent one.
Step 8: Running our WebSnap/WebService client application
Make sure your Web server is started, point your browser to http://localhost/scripts/WebServiceSnap.exe,
and click on the CallWebService link. If you did everything correctly, it'll
work beautifully. Example values are:
-
Area Code: 213
-
Country Code: 55
If you have any trouble running the application using IIS, you
can try to download the WSDL file directly to your scripts folder. Point your
browser to http://www.taragroup.com/bin/AreaCode.exe/wsdl/iGetArea.
Select File | Save As, find your Inetpubscripts dir and save iGetArea.xml
there. Now, change your HTTPRIO WSDLLocation property to C:InetpubscriptsiGetArea.wsdl
(or wherever you saved it).
Building WebSnap applications with Web Services Consumer functionality
may appear a little complicated. Nevertheless, when you understand the adapter idea and its integration with the other WebSnap
components, things will become clearer and, with some experience, you'll be able
to build Web Applications in a...er, snap!
Good luck, have fun and beware of the dog!
Daniel Polistchuck
IT Director
QualTech IT - Brazil
daniel@qualtech.com.br