Invoking a Web Service


Working with and invoking web services from ColdFusion is amazingly simple. In many ways, it's like calling a custom tag or CFC in which you pass a method, parameters, and point to a specific URI.

Let's just jump into an example, and we will explain it line by line. Take a look at Listing 21.1.

Listing 21.1 getTemp.cfm
 <cfinvoke webservice="http://www.xmethods.net/sd/2001/TemperatureService.wsdl"   method="getTemp"   zipcode="80526"   returnVariable="theTemp">  </cfinvoke>  <cfoutput>#theTemp#</cfoutput> 

This listing calls a web service that provides temperature information for a specific zip code. The first thing you will notice is that we use CFINVOKE with an attribute called webservice with a value that's a URI to a WSDL file. WSDL files are XML descriptions of the API for a specific service or portion of a service, and they are published by various providers so that developers like us know how to interact with their service.

The WSDL file usually provides you with the following:

  • Methods/operations of a web service

  • Input parameters that you pass to each operation

  • Return values from an operation

The WSDL file is very important. First, you need to have the URL to the WSDL of whatever service you want to use. Second, inside the WSDL file is important information that we need to help us figure out what parameters and methods we need to pass to the service.

First let's look at the Temperature Service WSDL file. Go ahead and use your IE browser and look at www.xmethods.net/sd/2001/TemperatureService.wsdl. You should see something like Listing 21.2.

Listing 21.2 TemperatureService.wsdl
 <?xml version="1.0" ?>   <definitions name="TemperatureService"  targetNamespace=http://www.xmethods.net/sd/TemperatureService.wsdl  xmlns:tns=http://www.xmethods.net/sd/TemperatureService.wsdl  xmlns:xsd=http://www.w3.org/2001/XMLSchema  xmlns:soap=http://schemas.xmlsoap.org/wsdl/soap/  xmlns="http://schemas.xmlsoap.org/wsdl/">    <message name="getTempRequest">      <part name="zipcode" type="xsd:string" />    </message>    <message name="getTempResponse">      <part name="return" type="xsd:float" />    </message>    <portType name="TemperaturePortType">      <operation name="getTemp">         <input message="tns:getTempRequest" />         <output message="tns:getTempResponse" />      </operation>    </portType>    <binding name="TemperatureBinding" type="tns:TemperaturePortType">      <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />      <operation name="getTemp">        <soap:operation soapAction="" />        <input>           <soap:body use="encoded" namespace="urn:xmethods-Temperature"  encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />        </input>        <output>         <soap:body use="encoded" namespace="urn:xmethods-Temperature"  encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />        </output>      </operation>    </binding>    <service name="TemperatureService">      <documentation>Returns current temperature in a given U.S. zipcode</documentation>      <port name="TemperaturePort" binding="tns:TemperatureBinding">        <soap:address location="http://services.xmethods.net:80/soap/servlet/rpcrouter"  />      </port>    </service>  </definitions> 

At first glance, the WSDL file might be a little much to read, but it's actually very straightforward. The WSDL has several major elements with the message element being of greatest concern to us, but let's step through the file first.

The root element of the WSDL file is definitions that basically hold various namespaces to avoid naming conflicts with public services. The next major element in a WSDL file is types that define data types for the services. The next element that is of greatest importance to us is the message element. The message element defines the name and data types for the input parameters as well as the return values, and this is where we got the information for these two lines

 method="getTemp"  zipcode="80526" 

in our CFINVOKE tag from the WSDL file:

 <message name="getTempRequest">    <part name="zipcode" type="xsd:string" />  </message>  <message name="getTempResponse">    <part name="return" type="xsd:float" />  </message> 

This tells us that we can call a method "getTempRequest" by passing it a zip code as a string, and it will return the temperature as a floating-point number. This section is very important in that it tells you not only what to provide the web service but what to expect back and how. Oftentimes, your web service will return a data type such as an array or another complex data type that ColdFusion deals with as a structure. The conversions between ColdFusion and web service data types are shown in Table 21.1.

Table 21.1. Data Type Relations Between ColdFusion and WSDL

ColdFusion Data Type

WSDL Data Type

numeric

SOAP-ENC:double

boolean

SOAP-ENC:boolean

string

SOAP-ENC:string

array

SOAP-ENC:Array

binary

xsd:base64Binary

date

xsd:dateTime

struct

complex type

The next major element is the porttype, which tells us what operations are provided by this web service. After that, we have the operation element that defines the service that can be remotely called. Then we have the elements input and output that define the parameters needed for inputs and outputs.

The WSDL file might also have a fault element that defines an error message that should be thrown in the case of an exception. Next we have the element binding, which tells us what protocols we can use to access this web service. The binding element is very important in that ColdFusion can only consume web services that:

  • Use RPC as the soap binding style.

  • Use encoding as the encodingStyle.

If you look at the WSDL for this service, you will see that it supports both:

 <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />      <operation name="getTemp">        <soap:operation soapAction="" />        <input>           <soap:body use="encoded" namespace="urn:xmethods-Temperature"  encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />        </input>        <output>          <soap:body use="encoded" namespace="urn:xmethods-Temperature"  encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />        </output>      </operation>    </binding> 

We also have the element service, which defines any related service and, in some cases, provides documentation for the web service. Finally, we have the element port, which defines the address for the web service. So, with this basic knowledge, you can parse a WSDL file and figure out what information you need to invoke a web service.

In general, when calling or consuming a web service, you will end up using the following general form:

 <cfinvoke    webservice = "URLtoWSDL"    method = "operationName"    inputParam1 = "val1"    inputParam2 = "val2"    ...    returnVariable = "anyVariableName"  /> 

CFINVOKE has several attributes that you need to provide when invoking a web service. The first is webservice, which specifies the URL to the WSDL file for the web service. The next attribute, method, specifies the operation of the web service to invoke, which in our example was getTemp. Next, if your webservice calls for it, you need to define parameters that need to be passed, which could be one or many. In our example, we just had zipcode="". Finally, you need to define returnVariable, which is a ColdFusion variable you define that will hold the return results from your web service if there are any.

Sometimes, if you have to pass many parameters, you will want to use the CFARGUMENT tag, which is nested inside the CFINVOKE tag as follows:

 <cfinvoke    webservice = "URLtoWSDL"    method = "operationName"    returnVariable = "anyVariableName"  />  <cfinvokeargument name="someparam" value="1">  <cfinvokeargument name=" someparam" value="2">  </cfinvoke> 

This is especially useful if you are creating web services that need to build the parameters dynamically. If you are trying to pass a complex array of parameters, you can do something like this:

 <cfscript>    stParameters = structNew();    stParameters.parameter1 = "1";    stParameters.parameter2 = "2";    stParameters.parameter3 = "3";    stParameters.parameter4 = "4";  </cfscript>  <cfinvoke    webservice = "URL"    method     = "methodname"    argumentCollection = "#stParameters#"    returnVariable = "varName"    >  <cfoutput>#varName#</cfoutput> 

So, as you can see with this example, we can build a structure of our parameters and pass that to the web service.

We can also consume web services using CFSCRIPT and the function CreateObject. Listing 21.3 provides an example.

Listing 21.3 getTemp2.cfm
 <cfscript>    ws = CreateObject("webservice",          "http://www.xmethods.net/sd/2001/TemperatureService.wsdl");    resultVar = ws.getTemp("80526");    writeoutput(resultVar);  </cfscript> 

In CFScript, you use the CreateObject function to connect to the web service. After connecting, you can make requests to the service. The general form for calling a web service with CFScript is as follows:

 webServiceName = CreateObject("webservice", "URLtoWSDL") 

After you have created the object, you can invoke the web service via dot notation:

 webServiceName.operationName(inputVal1, inputVal2, ... ) 

The input values are the names of the parameters you want to pass. You can also name the parameters as follows:

 ws.getTemp(zipcode="80526"); 

Or in the general form:

 webServiceName.operationName(parametername="inputVal1", parametername="inputVal2", ... ) 

Using this name-value-pair approach is probably the better coding practice in that it's much easier to understand when inspecting the code.

To deal with the return value, you just assign the web service to a ColdFusion variable:

 resultVar = webServiceName.operationName(inputVal1, inputVal2, ... ); 

Or you can directly deal with them by assigning them to a function, as follows:

 writeoutput(webServiceName.operationName(inputVal1, inputVal2, ...) ); 

You might find it annoying to have to cut and paste or type the WSDL URLs, especially if you are creating a series of templates or functions that perform different requests on the same service. ColdFusion enables you to map a name to a web service in the Administrator so that you can refer to a web service by that name instead of the URL to the WSDL file. In fact, every time you invoke a web service for the first time, ColdFusion automatically registers it for you in the Administrator, and you can just go in and edit it to assign it a mapping. This enables you to change an invocation from this

 <cfscript>    ws = CreateObject("webservice",          "http://www.xmethods.net/sd/2001/TemperatureService.wsdl");    resultVar = ws.getTemp("80526");    writeoutput(resultVar);  </cfscript> 

to this:

 <cfscript>    ws = CreateObject("webservice", "TemperatureService");    resultVar = ws.getTemp("80526");    writeoutput(resultVar);  </cfscript> 

Another advantage of doing things this way is that you can change the URL to the WSDL file in the ColdFusion mapping at any time without having to change all the code you've written that calls those WSDL files.

Consuming your own ColdFusion web services is actually even easier than working with web services that have not been created using ColdFusion. To call ColdFusion web services, you follow the same procedure as you would with a regular web service. Let's make one right now. First create a file called square.cfc and save it under your webroot, as shown in Listing 21.4.

Listing 21.4 square.cfc
 <cfcomponent>    <cffunction name="square"                returnType="string"                access="remote">      <cfargument name="num1" type="string">      <cfset sum = arguments.num1 * arguments.num1>      <cfreturn sum>    </cffunction>  </cfcomponent> 

Now open up IE and type in the URL to your CFC, but after square.cfc, type ?WSDL so that you have the following:

 http://localhost:8100/xml/webservice/square.cfc?wsdl 

Then view the WSDL:

   <?xml version="1.0" encoding="UTF-8" ?>   <wsdl:definitions targetNamespace="http://webservice.xml"  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"  xmlns:xsd="http://www.w3.org/2001/XMLSchema"  xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"  xmlns:intf="http://webservice.xml" xmlns:impl="http://webservice.xml-impl" xmlns:SOAP- ENC="http://schemas.xmlsoap.org/soap/encoding/"  xmlns="http://schemas.xmlsoap.org/wsdl/">    <wsdl:message name="CFCInvocationException" />   <wsdl:message name="squareResponse">   <wsdl:part name="return" type="SOAP-ENC:string" />    </wsdl:message>   <wsdl:message name="squareRequest">    <wsdl:part name="num1" type="SOAP-ENC:string" />    </wsdl:message>   <wsdl:portType name="square">   <wsdl:operation name="square" parameterOrder="num1">    <wsdl:input message="intf:squareRequest" />    <wsdl:output message="intf:squareResponse" />    <wsdl:fault name="CFCInvocationException" message="intf:CFCInvocationException" />    </wsdl:operation>    </wsdl:portType>   <wsdl:binding name="square.cfcSoapBinding" type="intf:square">    <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />   <wsdl:operation name="square">    <wsdlsoap:operation soapAction="" />   <wsdl:input>    <wsdlsoap:body use="encoded"  encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"  namespace="http://webservice.xml" />    </wsdl:input>   <wsdl:output>    <wsdlsoap:body use="encoded"  encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"  namespace="http://webservice.xml" />    </wsdl:output>    </wsdl:operation>    </wsdl:binding>   <wsdl:service name="squareService">   <wsdl:port name="square.cfc" binding="intf:square.cfcSoapBinding">    <wsdlsoap:address location="http://localhost:8100/xml/webservice/square.cfc" />    </wsdl:port>    </wsdl:service>    </wsdl:definitions> 

Whenever you create a CFC with the access set to remote and then request the web service, ColdFusion automatically creates a WSDL file for you so that you don't have to bother with it.

To call our web service, we do the same thing as before: We provide it with the URL to the WSDL file, the method (which is square), and a parameter (which is NUM1). See Listing 21.5.

Listing 21.5 getSquare
 <cfinvoke webservice="http://localhost:8100/xml/webservice/square.cfc?wsdl"   method="square"   num1="5"   returnVariable="var1">  </cfinvoke>  <cfdump var="#var1#"> 

If you call this service in your browser, you will get 25 as the result.

Sometimes, though, you are going to want to try to work with a web service that handles what are called "complex types of data."

 <cfscript>    stExch = structNew();    stExch.currency = "DEM";  stExch.amount = 100;  stExch.toCurrency = "EUR";    ws = createObject("webservice",  "http://www.shinkatech.com/interop/CurrencyConverter.wsdl");    ws.calculateExchangeRate(stExch);  </cfscript> 

This web service calls a service that converts currencies but expects to receive its parameters as a complex data type. If you look at the WSDL file that can be found at http://www.shinkatech.com/interop/CurrencyConverter.wsdl, you will notice that the web service expects three parameters like this:

 <complexType name="ConvertMessage">   <all>    <element name="currency" type="CurrencySchema:CurrencySymbol" />    <element name="amount" type="double" />    <element name="toCurrency" type="CurrencySchema:CurrencySymbol" />    </all>    </complexType> 

In the specific case in which applications are requesting a complex data type, you can do what we have done in the example: build a ColdFusion structure that models the complex data type and then pass the structure to the web service.



Inside ColdFusion MX
Inside Coldfusion MX
ISBN: 0735713049
EAN: 2147483647
Year: 2005
Pages: 579

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net