WebSnap and Web Services hand-in-hand

By: Other Guy

Abstract: Here's a guide to combining WebSnap and Web Services to create SOAP client web pages. By Daniel Polistchuck.

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:

  1. 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
  2. Building WebSnap applications. Ditto.
  3. 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:

  1. 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).

  2. 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.

Property Value
WSDLLocation http://www.taragroup.com/bin/AreaCode.exe/wsdl/iGetArea
Service IGetAreaservice (use the property drop down)
Port IGetAreaPort (use the property drop down)

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


Server Response from: ETNASC03