Consuming XML Web Services in VFP 8
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.
Consuming XML Web Services in VFP 8
Doug Hennig
XML Web Services are becoming very important for application development, and VFP 8 makes it easier than ever to use them. This month, Doug Hennig explores consuming XML Web Services in VFP applications, including how to use the new XML Web Service Builder to bind controls to XML Web Services results with very little code.
Microsoft's vision of software development strongly embraces XML Web Services. A Web Service is nothing more than a component that sits on a Web server somewhere. A client calls a function of the component, using SOAP (Simple Object Access Protocol) as the transport mechanism for the function call, parameters, and return value, and does something with the results, which are usually in the form of XML. The reason Web Services are becoming more important for application development is that they can form the building blocks of distributed applications.
In my June 2002 FoxTalk article, "Searching With Web Services," I discussed using Web Services in VFP 7 to provide a means of searching various Web sites for information. This month, I'll revisit this topic, but using the new tools that come with VFP 8. I won't discuss the plumbing involved in Web Services, such as SOAP, WSDL, and UDDI. There are numerous white papers available, including many on the MSDN Web site (https://msdn.microsoft.com), which go into great detail on that topic.
Registering a Web Service
The first step to using a Web Service is to register it with VFP. Although this isn't strictly necessary, it's easier to work with a Web Service if you do. To register a Web Service, launch the XML Web Services Registration dialog (shown in Figure 1) by clicking on the Web Services button on the Types page of the IntelliSense Manager (this was the only way to launch this dialog in VFP 7), clicking on the Register link in the My XML Web Services category of the new Toolbox, choosing the Register an XML Web Service link in the XML Web Services pane of the new Task Pane Manager, or selecting the New button in the XML Web Services Manager, which is also available from the XML Web Services pane of the Task Pane Manager.
Enter the URL, including the http:// prefix, for the WSDL of the Web Service you want to register, and click on the Register button. If you don't know the URL, click on the UDDI Search button. In the UDDI Search dialog, type a name and click the Search button. The combo box is populated with a list of matching Web Services from the UDDI database, and the edit box below it shows information about the selected Web Service.
For the examples in this article, we'll work with the Web Service that provides searching on the FoxWiki (a great FoxPro resource created by Steven Black—http://fox.wikis.com), so register the following WSDL file: http://fox.wikis.com/wikiwebservice.wsdl.
Using Web Services in VFP 8
The way you use registered Web Services in VFP 8 is totally different than it was in VFP 7. Rather than selecting a Web Service from IntelliSense when you type LOCAL SomeVariable AS, you drag a Web Service from the Toolbox to either a code window—which generates code—or a container (such as a form) in the Form or Class Designers—which adds an object to the container. Let's look at both of these mechanisms.
Using Web Services programmatically
Here's the code generated when you drag the FoxWiki Web Service from the Toolbox to a code window:
LOCAL lowikiwebservice AS "MSSOAP.SoapClient30" LOCAL lowikiwebservice AS "XML Web Service"
- Do not remove or alter following line. It is used to
- support IntelliSense for your XML Web Service. *VFPWSDef: lowikiwebservice = ;
- http://fox.wikis.com/wikiwebservice.wsdl ,
- wikiwebservice , wikiwebserviceSoapPort LOCAL loException, lcErrorMsg, loWSHandler TRY loWSHandler = NEWOBJECT("WSHandler", ; IIF(VERSION(2) = 0, "", ; HOME() + "FFC") + "_ws3client.vcx") lowikiwebservice = loWSHandler.SetupClient( ; "http://fox.wikis.com/wikiwebservice.wsdl", ; "wikiwebservice", ; "wikiwebserviceSoapPort")
- Call your XML Web Service here. ex: leResult
- = lowikiwebservice.SomeMethod()
CATCH TO loException
lcErrorMsg = "Error: " + ;
TRANSFORM(loException.Errorno) + " - " + ;
loException.Message
DO CASE
CASE VARTYPE(lowikiwebservice)#"O"
* Handle SOAP error connecting to Web Service
CASE !EMPTY(lowikiwebservice.FaultCode)
* Handle SOAP error calling method
lcErrorMsg = lcErrorMsg + CHR(13) + ;
lowikiwebservice.Detail
OTHERWISE
* Handle other error
ENDCASE
- Use for debugging purposes MESSAGEBOX(lcErrorMsg) FINALLY ENDTRY
This code uses the new structured error handling in VFP 8 (see my January 2003 article, "CATCH Me If You Can," for details on this wonderful new feature), so it handles errors gracefully. Also, if you used Web Services in VFP 7, you'll notice this code uses a different class, WSHandler in _WS3Client.VCX rather than WSClient in _WebServices.VCX, to act as the Web Service proxy.
Add the following three lines of code to the code generated by the Toolbox (just above the CATCH statement), or run TestWikiWebService.PRG, which is included in the Download file:
This code searches the FoxWiki for all occurrences of "Stonefield" (substitute your favorite search string) and displays them in a browse window, complete with title and URL for the full text. Not bad for three lines of code. But wait until you see what we can do with just two lines!
Using Web Services visually
Dragging a Web Service from the Toolbox to the Form or Class Designer adds an instance of the same WSHandler class to the container and fills in several properties with information about the selected Web Service. The great thing about this class is that VFP provides a builder for it that allows you to configure the object so it not only calls a particular method of the Web Service, but also obtains any parameters needed for the method in a variety of ways (such as prompting the user or from controls in the form) and binds the results to other controls.
Let's build a form that allows us to search the FoxWiki and displays the results in a grid. Create a form called WikiSearch.SCX. (This form is included in the Download file if you want to see the finished results.) Drop a label, a text box, a command button, and a grid on it, laid out as shown in Figure 2, and set the properties of these controls as shown in Table 1.
****
Table 1. Set the properties of the controls in the form to these values.
Control |
Property |
Value |
Form |
AutoCenter |
.T. |
Label |
Name |
lblSearch |
TextBox |
Name |
txtSearch |
CommandButton |
Name |
cmdSearch |
Grid |
Name |
grdResults |
Bring up the Toolbox from the Tools menu or the VFP toolbar, click on the My XML Web Services category, and drag the icon for the WikiWebService to the form. That will add a WSHandler object to the form and automatically launch the XML Web Service Builder.
The Operations page of the builder is automatically selected because the Web Service to use has already been defined in the object. (The Services page allows you to define which Web Service you're calling.) The Operations page allows you to define the operations (which methods of the Web Service are called) and which clients (other controls) will consume the results of each operation. Figure 3 shows this page.
To define an operation, click on the Add button. There are several properties you can set for an operation in the Operation Detail dialog (shown in Figure 4), including a friendly name and description. To select the Web Service method to call, choose it from the methods combo box; for this example, choose the GetTextSearch method. If you turn on the "Allow Web Service calls to be cached offline" option, WSHandler will cache the results of a Web Service call and use those results when the Web Service isn't available for a future call.
Use the Set parameter(s) combo box to specify how the values for the parameters passed to it are obtained. The choices are "Input values now," "Programmatically set at runtime," and "Prompt at runtime." "Programmatically set at runtime" means you'll set the value in code. Use "Prompt at runtime" if you want VFP to display a dialog in which the user can enter the values; this is probably more useful for developer tools than user applications. The term "Input values now" is somewhat misleading, since it doesn't necessarily mean you enter hard-coded values (although you certainly can do that). When you select that choice, the Set button is enabled. Click on it to display the Parameter Values dialog (see Figure 5).
To specify a value for a parameter, select it in the Select parameter list, choose Value (expression) as the parameter type, and enter the value into the edit box below this option. To indicate the value is an expression that should be evaluated at runtime, precede it with "=". To obtain the parameter value from another control, choose Control/Property, put a checkmark in front of the appropriate control, and select the property that holds the value from the combo box below the control list. For the WikiWebService, we'll bind the tcString parameter to the Value property of the txtSearch text box. That way, the Web Service will automatically search for whatever we type into the text box.
To define a client for an operation, click on the Add button beside the Clients list in the Operations page. Use the Client Detail dialog, shown in Figure 6, to specify which control in the container is the client for the operation. You can then indicate which property of the control receives the result, and whether the property is bound to a parameter or the return value of the Web Service method.
If you turn on the "Invoke at startup" option, the operation executes when the container instantiates. Otherwise, you'll have to invoke it manually. If the client object is a CursorAdapter and you turn on "Invoke at startup," you're warned that you should set the form's BindControls property to False to prevent any problem with controls bound to the CursorAdapter. WSHandler automatically sets BindControls to True after the CursorAdapter has been filled with data from the Web Service so data binding can occur.
If you turn on the "Always call Web Service" option, the client will always call the Web Service method. Otherwise, WSHandler will reuse the results when it detects that multiple clients call the same operation at the same time (such as when the container instantiates).
WSHandler adds the property you specify in the Name of binding property option (the default is "BindMe" but you can also choose any custom property or method of the client control) to the client control at runtime if it doesn't already exist. The property acts like a switch: WSHandler uses BINDEVENTS() (a new function in VFP 8 that provides event binding between VFP objects) so it's notified when the property's value changes. Setting its value to .T. causes the WSHandler object to call the Web Service method and put the results into the client control.
If the Web Service you want to call returns complex XML—such as objects serialized as XML, ADO.NET DataSets, or VFP cursors converted to XML via CURSORTOXML()—click on the Complex Types button. In the Complex Types dialog, click on the Query button to call the Web Service method immediately; the builder will then examine the return value and fill in the rest of the controls. For example, if the Web Service returns an ADO.NET DataSet, the Dataset option is selected, the Dataset tables combo box is filled with the names of the tables in the DataSet, and the Table fields combo box shows the fields in the selected table. (In the case of a grid client, Table fields is ignored and the grid is bound to the selected table.) If the Web Service returns an XML string that can be converted into a VFP cursor (as is the case with the WikiWebService used in these examples), you'll be informed of that and asked if you want to view the cursor's schema. If you choose Yes, the Dataset tables and Table fields combo boxes are filled in. The Attach button is enabled if the client object is a CursorAdapter and you click on Query to query the Web Service. In that case, if you click on the Attach button and choose Yes in the subsequent dialog, the builder will update the CursorSchema property of the CursorAdapter to match the schema of the selected table.
For our example form, choose grdResults in the Client Detail page so the results will be displayed in a grid. The WSHandler object will automatically convert the XML returned from the GetTextSearch method of the Web Service into a cursor and bind the grid to that cursor.
That's it for the builder, so choose OK in each open dialog until the builder is closed. The last thing we have to do is tell the WSHandler object when to call the Web Service. That should happen when we click on the Search button, so put the following code into the Click method of that button:
Setting the BindMe property of grdResults (remember, the WSHandler object will add this property to the grid when the form is instantiated) to .T. starts the operation because that was the property specified in the builder. The WSHandler object calls the GetTextSearch method of the FoxWiki Web Service, passing it the contents of the Value property of the txtSearch text box, creates a cursor from the returned XML, and puts the name of that cursor into the RecordSource property of the grdResults grid. Calling the grid's AutoFit method ensures the grid columns are adjusted to fit the received data.
Run the form, enter a search string, and click on the Search button. After a few seconds, the search results are shown in a grid that auto-fits the columns to its contents and provides hyperlinks to the Wiki articles; Ctrl-Click on the hyperlink to bring up the document in your browser. See Figure 2 for an example of what this form looks like. Not bad for just two lines of code!
Note: If you didn't build this form from scratch but rather want to use the one that's provided in the Download file, instead of running this form directly, run WikiSearch.PRG—it ensures the path to the WSHandler object in WikiSearch.SCX is correct for your system.
Summary
XML Web Services are easier to use than ever in VFP 8. The WSHandler class allows you to consume XML Web Services both programmatically and visually, including handling the wiring of form controls to a Web Service. Start considering what role XML Web Services will take in your distributed applications.
Download 07DHENSC.exe
To find out more about FoxTalk and Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57
Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.
This article is reproduced from the July 2003 issue of FoxTalk. Copyright 2003, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.