gkubu - 2012-06-22 19:08:46 This page contains examples of how to use the „Web services for Tcl “ client side package. The package was created and is maintained by Gerald Lester, who also patiently provides support for ignorant users.
The client parses so-called wsdl files [L1 ]. From this file you get most of the information you need:
You may also get the information from the documentation of the service, or by a tool like soapUI, or by the CreateStubs command of the package (see below).
The wsdl file contains the information in tags like
<wsdl:service name="country"> <wsdl:operation name="GetCountryByCountryCode"> <s:element name="GetCountryByCountryCode"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="CountryCode" type="s:string"/> </s:sequence> </s:complexType> </s:element>
(The style here is "document/literal", refered to as "document/literal wrapped" in [L2 ]).
Even those not familiar with wsdl can certainly identify the elements in this example (from the wsdl file "http://www.webservicex.net/country.asmx?WSDL ")
lm 2012/08/21 : Note that
set ret [::WS::Client::GetAndParseWsdl "http://www.webservicex.net/MortgageIndex.asmx?WSDL"]
returns a dictionary containing all the information describing the service. This dictionnary contains the keys name, operList (list of available operations) and operation (description of individual operations), t.
package require Tcl 8.5 package require WS::Client ::WS::Client::GetAndParseWsdl "http://www.webservicex.net/MortgageIndex.asmx?WSDL" # DoCall: yields result as a nested dict set monthlyIndex [ ::WS::Client::DoCall MortgageIndex GetCurrentMortgageIndexMonthly {} ] set key [ dict keys $monthlyIndex ] flush stdout puts "Key: $key" foreach { i v } [ dict get $monthlyIndex $key ] { puts "$i $v" }
should return something like
Key: GetCurrentMortgageIndexMonthlyResult IndexDate 7/1/2004 OneYearConstantMaturityTreasury 2.1 ThreeYearConstantMaturityTreasury 3.05 FiveYearConstantMaturityTreasury 3.69 ThreeMonthTreasuryBill 1.36 SixMonthTreasuryBill 1.69 ThreeMonthSecondaryMarketCD 1.4625 SixMonthSecondaryMarketCD - EleventhDistrictCOFI 1.4929 CostOfSavingsIndex 1.6945 OneMonthLIBOR 1.9857 ThreeMonthLIBOR 2.4632 SixMonthLIBOR 1.1617 OneYearLIBOR 1.91 CostOfDepositsIndex 1.57 TwelveMonthTreasuryAverage 1.85
DoRawCall returns the xml response of the web service
::WS::Client::DoRawCall MortgageIndex GetCurrentMortgageIndexMonthly {} <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body><GetCurrentMortgageIndexMonthlyResponse xmlns="http://www.webserviceX.NET/"> <GetCurrentMortgageIndexMonthlyResult> <IndexDate>7/1/2004</IndexDate> <OneYearConstantMaturityTreasury>2.1</OneYearConstantMaturityTreasury> <ThreeYearConstantMaturityTreasury>3.05</ThreeYearConstantMaturityTreasury> <FiveYearConstantMaturityTreasury>3.69</FiveYearConstantMaturityTreasury> <ThreeMonthTreasuryBill>1.36</ThreeMonthTreasuryBill> <SixMonthTreasuryBill>1.69</SixMonthTreasuryBill> <ThreeMonthSecondaryMarketCD>1.4625</ThreeMonthSecondaryMarketCD> <SixMonthSecondaryMarketCD>-</SixMonthSecondaryMarketCD> <EleventhDistrictCOFI>1.4929</EleventhDistrictCOFI> <CostOfSavingsIndex>1.6945</CostOfSavingsIndex> <OneMonthLIBOR>1.9857</OneMonthLIBOR> <ThreeMonthLIBOR>2.4632</ThreeMonthLIBOR> <SixMonthLIBOR>1.1617</SixMonthLIBOR> <OneYearLIBOR>1.91</OneYearLIBOR> <CostOfDepositsIndex>1.57</CostOfDepositsIndex> <TwelveMonthTreasuryAverage>1.85</TwelveMonthTreasuryAverage> </GetCurrentMortgageIndexMonthlyResult> </GetCurrentMortgageIndexMonthlyResponse> </soap:Body> </soap:Envelope>
If you don't want to look up the name of the web service, you can provide your own. However, if you want to discuss the service, it might be advantageous to use its proper name.
package require WS::Client ::WS::Client::GetAndParseWsdl "http://www.webservicex.net/MortgageIndex.asmx?WSDL" {} mogaIxAlias set monthlyIndex [ ::WS::Client::DoCall mogaIxAlias GetCurrentMortgageIndexMonthly {} ] puts [ dict get $monthlyIndex GetCurrentMortgageIndexMonthlyResult TwelveMonthTreasuryAverage ] 1.85
As mentioned above, the CreateStubs command returns operations and parameters
package require WS::Client ::WS::Client::CreateStubs MortgageIndex ;# || ::WS::Client::CreateStubs mogaIxAlias ::MortgageIndex::GetCurrentMortgageIndexMonthly {} ::MortgageIndex::GetCurrentMortgageIndexByWeekly {} ::MortgageIndex::GetMortgageIndexByMonth {Month Year} ::MortgageIndex::GetMortgageIndexByWeek {Day Month Year}
The stubs are actually commands, which can be used instead of DoCall (a stub is a wrapper for DoCall), but they are missing the flexibility of the latter.
::MortgageIndex::GetMortgageIndexByMonth 5 2004
Some web services require additional information in http headers, e.g. for authentification. This is shown for a call to a simple eBay web service. You will need an eBay developer account, if you want to reproduce the example [L3 ]. Information on eBay web services is available on [L4 ].
The eBay web service GeteBayTime requires an application ID, an API call name, a site ID and the API version as http header fields. WS::Client takes them as a key-value list.
The http headers are supplied as the fourth parameter to the DoCall and DoRawCall commands. Stubs cannot take such a parameter.
package require Tcl 8.5 package require WS::Client 2.2.7 dict set httpParm X-EBAY-API-APP-ID abcdefgh-1a23-45b6-789c-d012345ef6a7 ;# invalid ID - for demonstration only dict set httpParm X-EBAY-API-SITE-ID 77 dict set httpParm X-EBAY-API-CALL-NAME GeteBayTime dict set httpParm X-EBAY-API-VERSION 775 ::WS::Client::GetAndParseWsdl "http://developer.ebay.com/webservices/latest/ShoppingService.wsdl" ::WS::Client::DoRawCall Shopping GeteBayTime {} $httpParm
DoCall needs another option, as will be demonstrated below. DoRawCall yields
<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Body> <GeteBayTimeResponse xmlns="urn:ebay:apis:eBLBaseComponents"> <Timestamp>2012-06-23T18:02:51.683Z</Timestamp> <Ack>Success</Ack> <Build>E777_CORE_BUNDLED_14948543_R1</Build> <Version>775</Version> </GeteBayTimeResponse> </soapenv:Body> </soapenv:Envelope>
The wsdl includes the URI of the web service. You may want to change it, e.g. for test purposes. The ::WS::Client::Config command provides for that:
::WS::Client::GetAndParseWsdl "file://localhost:P:/edv/programmierung/tcl.tk/div/ebayShopping.wsdl" ::WS::Client::Config Shopping location http://open.api.sandbox.ebay.com/shopping dict set httpParm X-EBAY-API-APP-ID abcdefgh-1a23-45b6-789c-d012345ef6a7 dict set httpParm X-EBAY-API-SITE-ID 77 dict set httpParm X-EBAY-API-CALL-NAME GeteBayTime dict set httpParm X-EBAY-API-VERSION 775 ::WS::Client::DoRawCall Shopping GeteBayTime {} $httpParm
Now let's try DoCall:
::WS::Client::DoCall Shopping GeteBayTime {} $httpParm # no response, therefore ... ::WS::Client::Config Shopping skipLevelWhenActionPresent 1 ::WS::Client::DoCall Shopping GeteBayTime {} $httpParm GeteBayTimeResponse {Ack Failure Errors {{ShortMessage {Ungültige Eingabedaten.} LongMessage {Die Eingabedaten für diesen Tag sind ungültig oder fehlen. Bitte lesen Sie die API-Dokumentation.} ErrorCode 1.22 SeverityCode Error ErrorParameters {{Value {Body not found.}}} ErrorClassification RequestError}} Build E777_CORE_BUNDLED_14948543_R1 Version 777}
We got a response, but this is not what we wanted. The error message says "Value {Body not found.}". There are two ways to overcome this:
# supply the SOAP body dict set body GeteBayTimeRequest "" # note the parameter "$body" ::WS::Client::DoCall Shopping GeteBayTime $body $httpParm GeteBayTimeResponse {Timestamp 2012-06-24T14:19:08.092Z Ack Success Errors {{} {} {} {}} Build E777_CORE_BUNDLED_14948543_R1 Version 777} # the stub handles this alike: ::Shopping::GeteBayTime GeteBayTimeRequest # Alternative (WS::Client version 2.2.8 required) ::WS::Client::Config Shopping skipLevelWhenActionPresent 0 ::WS::Client::Config Shopping skipLevelOnReply 1 ::WS::Client::DoCall Shopping GeteBayTime {} $httpParm GeteBayTimeResponse {Timestamp 2012-06-24T14:24:46.699Z Ack Success Errors {{} {} {} {}} Build E777_CORE_BUNDLED_14948543_R1 Version 777}
When creating the dictionary for the result from the <body> part of the SOAP response, skipLevelonReply causes the routine to skip one level of the DOM tree hierarchy, skipLevelWhenActionPresent does so if the result of the first level is empty. A look at proc ::WS::Client::parseResults in ClientSide.tcl should make this clear.
Should you want to use stubs, you will have to prepare your environment because of the need for http header fields.
package require WS::Client 2.2.8 set preparsed [ ::WS::Client::GetAndParseWsdl "http://developer.ebay.com/webservices/latest/ShoppingService.wsdl" ] flush stdout dict set httpParm X-EBAY-API-APP-ID abcdefgh-1a23-45b6-789c-d012345ef6a7 dict set httpParm X-EBAY-API-SITE-ID 77 dict set httpParm X-EBAY-API-CALL-NAME GeteBayTime dict set httpParm X-EBAY-API-VERSION 775 set serviceName [ WS::Client::LoadParsedWsdl $preparsed $httpParm ] ::WS::Client::Config $serviceName location "http://open.api.sandbox.ebay.com/shopping" ::WS::Client::Config $serviceName skipLevelOnReply 1 ::WS::Client::CreateStubs $serviceName # $serviceName is "Shopping", of course ::${serviceName}::GeteBayTime GeteBayTimeRequest GeteBayTimeResponse {Timestamp 2012-06-27T20:52:19.092Z Ack Success Errors {{} {} {} {}} Build E779_CORE_BUNDLED_14991381_R1 Version 779}
While for the stub the parameters were submitted by position (see example 1), for DoCall we need a list or a dict.
package require Tcl 8.5 package require WS::Client ::WS::Client::GetAndParseWsdl "http://www.webservicex.net/MortgageIndex.asmx?WSDL" # remember the definition: GetMortgageIndexByMonth {Month Year} dict set monthYear Month 4 dict set monthYear Year 2004 ::WS::Client::DoCall MortgageIndex GetMortgageIndexByMonth $monthYear # Alternative ::WS::Client::DoCall MortgageIndex GetMortgageIndexByMonth {Month 4 Year 2004} # also ::WS::Client::DoCall MortgageIndex GetMortgageIndexByMonth {Year 2004 Month 4 }
package require WS::Client set secrets "somewhereOnYourSystem" ::WS::Client::GetAndParseWsdl "file://$secrets/mortgageIndex.wsdl" # Alternative package require WS::Client set secrets "somewhereOnYourSystem" set fdWsdl [ open [ file join $secrets MortgageIndex.wsdl ] r ] set wsdl [ read $fdWsdl ] close $fdWsdl ::WS::Client::ParseWsdl $wsdl
An input transform like the following is possible after application of this one-line bugfix which seems to already be in trunk .
# # extending the MortgageIndex example above to insert a (here, useless) soap header # package require WS::Client set preparsed [::WS::Client::GetAndParseWsdl "http://www.webservicex.net/MortgageIndex.asmx?WSDL"] set svcName [WS::Client::LoadParsedWsdl $preparsed {}] WS::Client::CreateStubs $svcName # # a quick and very dirty way to inject SOAP-ENV:Header # proc xformInAddHeader {svcName opName tType xml {url {}} {argList {}}} { set hdr {<SOAP-ENV:Header><!-- stuff here --></SOAP-ENV:Header>} if {[string equal $tType "REQUEST"]} { set xml [string map [list <SOAP-ENV:Body> $hdr<SOAP-ENV:Body>] $xml] } return $xml } WS::Client::SetServiceTransforms $svcName xformInAddHeader puts [${svcName}::GetCurrentMortgageIndexMonthly]
http://core.tcl.tk/tclws/wiki?name=WSClient+Client+Side
TclSOAP (note the comment of Pat Thoyts regarding maintenance for this package)
tommycat - 2012-08-19 18:59:07
Where do we get tclws 2.2.8 as referenced in the examples above? The highest I'm seeing is 2.2.7
gkubu - 2012-08-20 17:15:27
2.2.8 is the trunk currently [L5 ]. It's needed for the skipLevelOnReply option. Accidentally I've just noticed that ActiveStates teapot archive includes 2.2.8 already.
ALX 2016-03-17 10:18:00
Simple HTTP Authentication Wrapper for http::geturl RFC 2617
HaO: Alex, if you have put the link here, could you also add information how to use it with tclws client ? Please remove this remark if wrong or done. Thanks.
You may also include your solution to the manual solution on page TCLWS (Web Services for Tcl) as SAP Client.