ColdFusion and ASP.NET Web Service Interoperability (or Lack Thereof), Part II

Yesterday I was talking about some of the problems I had getting a service provider's ASP.NET client to work with my company's ColdFusion web service. Once we had that issue resolved the next step was to get our CFMX 6.1 application working with their ASP.NET web services.

Here is a sample SOAP 1.1 request and response for one of their web service methods. (This is from the ASP.NET auto generated documentation):

POST /service.asmx HTTP/1.1
Host: their.service.com
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://tempuri.org/Company_Add"

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Company_Add xmlns="http://tempuri.org/">
<txtCompanyName>string</txtCompanyName>
<txtCompanyAddress1>string</txtCompanyAddress1>
<txtCompanyAddress2>string</txtCompanyAddress2>
<txtCompanyZip>string</txtCompanyZip>
<txtCompanyFax>string</txtCompanyFax>
</Company_Add>
</soap:Body>
</soap:Envelope>

----------------

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Company_AddResponse xmlns="http://tempuri.org/">
<Company_AddResult>int</Company_AddResult>
<txtErrorMessage>string</txtErrorMessage>
</Company_AddResponse>
</soap:Body>
</soap:Envelope>

I originally tried to call this service using <cfinvoke> however I kept getting the following error:

Web service operation "Company_Add" with parameters {txtCompanyName={Test Company}...} could not be found.

I got this error no matter how I tried passing in the parameters so what I ended up doing is taking the brute force approach. I created a cfc which just wrapped <cfhttp>calls to the webservice. Below is an example of what I did.

<cfcomponent>

<cffunction access="public" name="int" output="false" returntype="mywebservice">
<cfargument name="URL" type="string" required="true">
<cfset instance = StructNew() />
<cfset instance.URL = arguments.URL />
<cfreturn this />
</cffunction>

<cffunction access="public" name="companyAdd" output="false" returntype="struct">
<cfargument name="companyName" type="string" required="true" />
<cfargument name="companyAddress1" type="string" required="false" default="" />
<cfargument name="companyAddress2" type="string" required="false" default="" />
<cfargument name="companyZip" type="string" required="false" default="" />
<cfargument name="companyFax" type="string" required="false" default="" />

<cfset var requestXML = "" />

<cfsavecontent variable="requestXML">
<Company_Add xmlns="http://tempuri.org/">
<txtCompanyName>#arguments.companyName#</txtCompanyName>
<txtCompanyAddress1>#arguments.companyAddress1#</txtCompanyAddress1>
<txtCompanyAddress2>#arguments.companyAddress2#</txtCompanyAddress2>
<txtCompanyZip>#arguments.companyZip#</txtCompanyZip>
<txtCompanyFax>#arguments.companyFax#</txtCompanyFax>
</Company_Add>
</cfsavecontent>

<cfreturn makeRequest("Company_Add",requestXML) />

</cffunction>

<cffunction access="private" name="makeRequest" output="false" returntype="struct">
<cfargument name="requestType" type="string" required="true" />
<cfargument name="requestXML" type="string" required="true" />

<cfset var httpXML = "" />
<cfset var cfhttp = "" />
<cfset var resultXML = "" />
<cfset var resultStruct = StructNew() />

<cfsavecontent variable="httpXML">
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>#arguments.requestXML#</soap:Body>
</soap:Envelope>
</cfsavecontent>

<cfhttp method="post" url="#instance.URL#">
<cfhttpparam type="header" name="SOAPAction" value="http://tempuri.org/#arguments.requestType#">
<cfhttpparam type="XML" value="#httpXML#">
</cfhttp>

<cfset resultXML = XMLParse(cfhttp.FileContent) />
<cfset resultStruct.result = resultXML.Envelope.Body['#arguments.requestType#Response']['#arguments.requestType#Result']['XmlText'] />
<cfset resultStruct.errorMessage = resultXML.Envelope.Body['#arguments.requestType#Response']['txtErrorMessage']['XmlText'] />

<cfreturn resultStruct />

</cffunction>

</cfcomponent>

I'm definitely not a SOAP expert and I don't know a whole lot about ColdFusion's underlying Apache Axis web service engine, so if anyone has other suggestions on how to handle this, or any insight whatsoever as to what the issue is I'd be glad to hear it.

Comments
Phil Duba's Gravatar Nathan, did you try creating a structure called Company_Add with each one of the items another struct? I remember doing this for one of the assessment providers at VE and they were a .NET webservice.
# Posted By Phil Duba | 2/7/07 11:10 PM
Phil Duba's Gravatar Here is the url of the article I used to help myself out while doing that work (sorry for the double comment): http://hcc.musc.edu/research/shared_resources/xml_...
# Posted By Phil Duba | 2/7/07 11:12 PM
Nathan Mische's Gravatar Hey Phil, thanks for that article, it was a huge help. I think I now have the issue figured out. I'll post what I've learned in a new entry.
# Posted By Nathan Mische | 2/8/07 11:22 AM
Tyson's Gravatar I had the exact same problem about 5 months ago. The ASP.NET web service was returning 2 values. I talked to the other programmer and he changed it to just return the result code, not the error message string and it worked. So in your example, just Company_AddResult needed to be returned, not txtErrorMessage. Hope that helps a little.
# Posted By Tyson | 2/8/07 11:42 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.