|
Mailing Lists
|
Home /
Groups /
ColdFusion Talk (CF-Talk)
Consume NET web service with complex arguments
Author: Kris Jones
Short Link: http://www.houseoffusion.com/groups/cf-talk/thread.cfm/threadid:52942#287940
> did you ever get this figured out? I am coming across the same thing
Hi Shane,
I did find an approach that works for my situation. Here are some
abbreviated notes I put together for our team:
Note, you'll need to access the WSDL file, and soap descriptions for
your webservice calls.
Cheers,
Kris
-----------------------------------------
ColdFusion has the ability to consume (e.g., call) a webservice using
it's CFINVOKE and CFINVOKEARGUMENT tags:
<cfinvoke
webservice="webservice wsdl address"
method="ws method name"
returnvariable="myreturnvar">
<cfinvokeargument name="ws parameter name 1" value="my param value"
/>
<cfinvokeargument name="ws parameter name 2" value="my param value"
/>
</cfinvoke>
You can also wrap the arguments up in the cfinvoke tag itself, like this:
<cfinvoke
webservice="webservice wsdl address"
method="ws method name"
parametername1="my param value"
parametername2="my param value"
returnvariable="myreturnvar" />
Be aware that ColdFusion has, in it's CFInvoke tag, parameters called
username and password. If the web service you are calling has
attributes with these same names, your web service call will not work
if you are using the simple form of CFInvoke like this:
<cfinvoke
webservice="webservice wsdl address"
method="ws method name"
username="myusername"
password="mypassword"
attributeN="value"
returnvariable="myreturnvar" />
You must create a structure to hold your attributes, and pass that in
the attributeCollection parameter instead:
<cfset stArgs=structNew() />
<cfset stArgs.username="myusername" />
<cfset stArgs.password="mypassword" />
<cfset stArgs.attributeN="value" />
<cfinvoke
webservice="webservice wsdl address"
method="ws method name"
attributeCollection="#stArgs#"
returnvariable="myreturnvar" />
In general, when you are dealing with complex inputs to a webservice,
you are probably better off just constructing the soap packet
manually, and calling the webservice via cfhttp, rather than
attempting to use cfinvoke on a webservice object, for example:
<cfsavecontent variable="soap_packet"><cfoutput><?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:Header>
<HeaderName xmlns="name_space_address">
<headerparam1>#variables.sessionkey#</headerparam1>
<headerparam2>ImpExpTestClient</headerparam2>
<headerparam3>xmlservices</headerparam3>
</HeaderName>
</soap:Header>
<soap:Body>
<PN_SET_VENDORCOMBO xmlns="name_space_address">
<webserviceparam1>#variables.xmlstring#</webserviceparam1>
<webserviceparam2>#request.integration_package_id#</webserviceparam2>
</PN_SET_VENDORCOMBO>
</soap:Body>
</soap:Envelope>
</cfoutput>
</cfsavecontent>
<cfhttp url="webservice_wsdl_address" method="POST">
<cfhttpparam type="HEADER" name="Content-Type" value="text/xml;
charset=utf-8">
<cfhttpparam type="HEADER" name="Accept" value="application/soap+xml,
application/dime, multipart/related, text/*">
<cfhttpparam type="HEADER" name="User-Agent" value="Axis/1.1">
<cfhttpparam type="HEADER" name="Host"
value="dotted_notation_host_address">
<cfhttpparam type="HEADER" name="Cache-Control" value="no-cache">
<cfhttpparam type="HEADER" name="Pragma" value="no-cache">
<cfhttpparam type="HEADER" name="SOAPAction" value="URL TO
WEBSERVICEorMETHOD">
<cfhttpparam type="HEADER" name="Content-Length"
value="#len(trim(soap_packet))#">
<cfhttpparam type="BODY" value="#trim(soap_packet)#">
</cfhttp>
In the cfhttp call above, note the URL, Host, SOAPAction,
Content-Length, and BODY are dependent on your call. Everything else
can remain static as shown.
Also, when receiving complex structures back from a webservice call,
remember that CFDUMP is your friend. When using CFINVOKE, very often
the return value is inside an object returned by the web service. With
our webservices, usually, you'll need to call the get_any() function
on the object to get the actual return value. Which function is to be
called is dependent on the type of value being returned, and certainly
can be webservice method specific. The results from the get_any() (or
whatever) call, will usually be an array--but not always--so again,
CFDUMP is your friend.
Finally, here are decent articles on consuming web services from/to .Net:
http://coldfusion.sys-con.com/read/47199.htm
Long forum discussion on consuming complex web services that require
specific header and property attributes:
http://www.adobe.com/cfusion/webforums/forum/messageview.cfm?catid=7&threadid=781179
And finally, here is an article on the nitty-gritty behind how
ColdFusion utilizes Java to interface with most other web services,
specifically having to do with complex web services, and how we get
our type-less values to be typed correctly for the web-service being
called. It's not inherently necessary to read it, but it is good
information:
When having to pass complex structures into a .Net webservice, read
this excellent article, which you'll have to get from the WayBack
machine, because it's no longer available on the authors' website:
http://web.archive.org/web/20070309173903/http://hcc.musc.edu/research/shared_resources/xml_complex_types_to_cf_structure_notes.cfm
And, while this article gives some approaches for dealing with complex
calls, I still found that just writing the soap packet manually got me
the results (and more easily).
-----------------------------------------
Author: Shane Trahan
Short Link: http://www.houseoffusion.com/groups/cf-talk/thread.cfm/threadid:52942#287903
did you ever get this figured out? I am coming across the same thing
Author: Kris Jones
Short Link: http://www.houseoffusion.com/groups/cf-talk/thread.cfm/threadid:52942#286109
Hi all,
I'm trying to consume a .NET web service that has 2 arguments, one of
which is complex. The webservice is expecting an xml type and an int
type. I've looked at a lot of resources regarding this, and am still
coming up broke. I've run the wsdl against the WSDL2Java tool, and
looked at the .java file for the ws method I'm calling, and not
getting any particularly good information out of it (it wants an
argument of type _any, which from what I've read is the default .NET
signature).
From the wsdl:
<s:element name="SOMEMETHODNAME">
−<s:complexType>
−<s:sequence>
−<s:element minOccurs="0" maxOccurs="1" name="somearg1">
−<s:complexType mixed="true">
−<s:sequence>
<s:any/>
</s:sequence>
</s:complexType>
</s:element>
<s:element minOccurs="1" maxOccurs="1" name="somearg2" type="s:int"/>
</s:sequence>
</s:complexType>
</s:element>
Additionally, the call requires a header:
<s:element name="SecurityHeader" type="tns:SecurityHeader"/>
−<s:complexType name="SecurityHeader">
−<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="SessionKey"
type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="ClientName"
type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="UserName"
type="s:string"/>
</s:sequence>
</s:complexType>
So, I've created a struct with an array for the first argument, and a
simple key for the second argument, something like so:
<cfset mystruct=structNew() />
<cfset mystruct.somearg1=arrayNew(1) />
<cfoutput query="myquery">
<cfset mystruct.somearg1[currentrow]=structNew() />
<cfset mystruct.somearg1[currentrow].EL1="#EL1#" />
<cfset mystruct.somearg1[currentrow].EL2="#EL2#" />
<cfset mystruct.somearg1[currentrow].EL3="#EL3#" />
</cfoutput>
<cfscript>
// Create the web service object.
ws = CreateObject("webservice", "http://www.domain.com/webservice.asmx?WSDL");
// Setup the SOAP header
addSOAPRequestHeader(ws, "", "SessionKey", "#variables.sessionkey#", false);
addSOAPRequestHeader(ws, "", "ClientName", "MyClientName", false);
addSOAPRequestHeader(ws, "", "UserName", "MyUserName", false);
// Invoke the web service operation.
my_result = ws.SOMEMETHODNAME(mystruct,1);
</cfscript>
I'm getting the error:
Web service operation "SOMEMETHODNAME" with parameters
{{somearg1={[{EL1={4760000-11513503999},EL2={111-222-4444},EL3={111-222-4445}...
could not be found
In the code above, I'm sending the 2nd argument via the call,
straight-up. I've also tried adding that as an key/value pair in the
struct, as in
<cfset mystruct.somearg2=1 />
and then the call would be:
my_result = ws.SOMEMETHODNAME(mystruct);
Previously I'd tried passing in the XML string, or a CF XML object as
well. I've verified that the XML format I was/am using is valid.
What am I missing? Any suggestions for fixes? A better or easier way
to approach this?
Thanks,
Kris
|
May 24, 2012
|
Latest Fusion Authority Articles
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||