To create a new Web Service use the Web Connection Management Console by typing DO CONSOLE and select Create Web Service.
Enter the filename of the new Web Service and point it to a virtual directory on your Web site. Web Services are executed as PRG files, but are run dynamically through scriptmapping via the wwSOAP extension.
The scripted Web Service process consists of a small header program that is responsible for calling your actual implementation methods and a set of functions that make up the functionality of your Web Service. A basic Web Service with a Helloworld method looks like this:
*** DO NOT REMOVE - CALL WRAPPER
PARAMETERS lcMethod, lcParmString, lvResult
PRIVATE _loServer
_loServer = CREATEOBJECT("SoapService")
lvResult = Eval("_loServer."+ lcMethod + "(" + lcParmString+ ")" )
RETURN lvResult
ENDFUNC
*** DO NOT REMOVE - CALL WRAPPER
*** Class is not used OLEPUBLIC - OLEPUBLIC only used for SDL generation
DEFINE CLASS SoapService as RELATION OLEPUBLIC
FUNCTION helloworld(lcName as String) as String
ltTime = DATETIME()
RETURN "Hello World, " + lcName + ". Good day " + TRANSFORM(ltTime) + "."
ENDFUNC
ENDDEFINE
Note: This example uses VFP7 and data types for use in the type library if built into a COM object (not required). For VFP6 leave out the AS TYPE declarations. If left out or in VFP6 all parameters will be forced to string.
Note that to add functionality you simply add new methods with input parameters and a return value.
The above code implements a single function in the Web Service called HelloWorld. To call this method from a wwSOAP client use the following code:
oSoap = CREATEOBJECT("wwSoap")
oSoap.lIncludeDataTypes = .T.
oSoap.cServerUrl = "http://localhost/soap/soapservice.wwsoap"
oSoap.AddParameter("lcName","Rick Strahl")
lvResult = oSOAP.CallMethod("HelloWorld")
? lvResult
? oSOap.cErrorMsg
* ? oSoap.cRequestXML && Debug
* ? oSoap.cResponseXML && Debug
If you're using wwSOAP on the client you can also pass typed parameters and receive typed results. Lets add a method to the Web Service. Just open the .wwSOAP file and add a new method to the class:
FUNCTION addnumbers(lnValue as Integer, lnvalue2 as Integer) as Integer RETURN lnValue + lnValue2
You can immediately call this new method in your Web Service without recompiling or stopping the server. Note that the parameters and return value are numeric in this example:
oSoap = CREATEOBJECT("wwSoap")
oSoap.lIncludeDataTypes = .T.
oSoap.cServerUrl = "http://localhost/soap/soapservice.wwsoap"
oSoap.AddParameter(lcNumber1,10)
oSoap.AddParameter(lcNumber2,11)
lvResult = oSOAP.CallMethod("addnumbers")
? lvResult && 21.00
? VARTYPE(lvResult)
? oSOap.cErrorMsg
lvResult in this case comes back as a numeric value. Typed input is valid per SOAP spec, but be aware that MS SOAP does not use embedded type information instead relying on the external SDL file to get type information. Web Connection's Web Services support either embedded types or using an external SDL file if the types are not embedded.
Microsoft's Rope Client and SDL
If you're using Microsoft's ROPE client you'll need to do a little more work. Microsoft requires that you use an SDL file that describes the service. This version of Web Connection's Web Serivces doesn't create SDL files so you'll have to manually create them. There are two options for doing so:
http://www.west-wind.com/presentations/soap
Tip:
In your tools directory is a small utility called WebServiceToDLL.prg. You can run this program to create a project from the Web Service and build the file quickly into a DLL. When you compile the project you may run into a number of compilation errors due to missing dependencies - Ignore All and continue to build the DLL. Once the DLL has been built use the MS Soap Toolkit Wizard to create an SDL file from your Web Service. Once the XML file has been created edit it and change the reference of <yourservice>.asp to <yourservice>.wwSoap in the <address> portion of the SDL.
In future versions of Web Connection you'll be able to create service descriptions directly. But in light of lack of a standard in this area we won't duplicate work that was already done for a non-standard such as MS's SDL. Once a real Service description standard exists it will be provided.
Once you have set up the required SDL file for using the ROPE client you can now call the Web Service easily:
oWire=CREATEOBJECT("Rope.WireTransfer")
lcXML = oWire.GetPageByURI("http://localhost/soap/soapservice.xml")
oProxy = CREATEOBJECT("Rope.Proxy")
? oProxy.LoadServicesDescription(2, lcXML) && .T./1
lvResult = oProxy.helloworld("rick")
*lvResult = oProxy.addnumbers(10,11) && Result: 21 as a string
? VARTYPE(lvResult)
How scripted Web Services work
Web Services are compiled scripts and run as native VFP code. Depending on the wwServer::nScriptMode setting the scripts are either run pre-compiled (the FXP must exist and is never recompiled at runtime) or checked for the latest date and re-compiled on the fly if changed. Pre-Compiled scripts don't detect any changes made to the Web Service script code while the server is running - the files would have to manually compiled. Because .wwSOAP files are really just PRG files with a different extension you can simply do:
COMPILE \inetput\wwwrroot\wconnect\soapservice.wwSOAP
to compile it. You can also use the Admin page's Compile scripts option to compile *.wwsoap pages in a given directory via the HTML interface remotely.
When nScriptMode is set to Interpreted scripts the Web Serivce goes out to disk and compares dates between the FXP and the .wwSoap file. If the FXP doesn't exist or is older than the .wwSOAP file it recompiles it at runtime. This mode is obviously more flexible as it allows you to make changes and immediately see the changes online.
Debugging scripts
Because scripts are real VFP programs you can debug them if running your Web Connection server inside of the VFP IDE. For example, you can simply SET STEP ON like this:
FUNCTION helloworld(lcName as String) as String ltTime = DATETIME() SET STEP ON RETURN "Hello World, " + lcName + ". Good day " + TRANSFORM(ltTime) + "." ENDFUNC
and the debugger will pop up in the middle of your Web Service.
Error Handling
When the #DEFINE DEBUGMODE flag in wconnect.h is set to .T. scripts will stop on errors and bring up the code VFP editor to allow you to fix your code. With DEBUGMODE .F. (which you should always do with live servers!!!) the error will be passed back to the SOAP error handler which will create the appropriate SOAP faultcode XML response. Using this approach you can basically debug your Web services like any other VFP program.
************************************************************************
* wwWebService :: wwWebService
****************************************
*** Function: Web Service Process Loader. Loads teh default
************************************************************************
LPARAMETER loServer
LOCAL loProcess
#INCLUDE WCONNECT.H
loProcess=CREATE("wwYourWebService",loServer)
*** Call the Process Method that handles the request
loProcess.Process()
RETURN
*************************************************************
DEFINE CLASS wwYourWebService AS WWC_WEBSERVICE
*************************************************************
* Sample WebService Method - just add regular methods
FUNCTION TestWebService(lcName)
RETURN "Hello " + lcName + ". Time is: " + TRANSFORM(DATETIME())
ENDDEFINE
*EOC wwDemoWebService
DO FORM .\tools\soapdemo
This brings up an interactive form that allows you to specify a URL for a Web Service (any SOAP compliant URL really) a method and up to three parameters that you can visually add.
For example to access the demo Web Service provided try:
http://localhost/wconnect/soap/soapservice.wwsoap
helloworld
lcName Rick
To explore the demo Web Service click on the arrow and enter:
http://localhost/wconnect/soap/soapservice.xml
into the SDL text box, then click Go. This should load all the methods of the Web Service and show them in the listbox. Select any item and the rest of the form will fill with the service URL, method name and the parameters and their types. You can run each request and check out the SOAP messages passed between client and server.