ColdFusion vs. ASP.NET

So this week I had an experience with ColdFusion and ASP.NET that reminded me of why I love ColdFusion. I'm currently working on a project which has a pretty aggressive time line, the goal of which is to migrate some legacy data into a new ColdFusion system. The system has some fairly complex business logic so we decided that the quickest way to get the job done was to write an routine which submits data to the new system as a user would entering data into the application's forms. That way we wouldn't have to reverse engineer any business logic and incorporate it into SQL migration scripts. We also decided to try to leverage some of our .NET resources to help with this task.

[More]

Understanding .NET

With the exception of the ColdFusion application I support, the company I currently work for is a Microsoft shop. I've recently been asked to help out with maintenance on some of our .Net applications; the only problem is I have next to zero .NET experience. For what I've had to do so far this hasn't been a huge problem, but I've decided to take this opportunity to really dig into .NET and see if I can't learn anything useful. I decided to start out with a book I've had on my bookshelf for awhile, Understanding .NET, A Tutorial and Analysis by David Chappell. The book is a little outdated so it covers some topics that are no longer relevant (.NET My Services anyone?), but overall I've found it pretty helpful. If you are looking for a hands-on book to teach you programming in .NET this is not the book for you. This is more of an introduction with a strong focus on the underlying technologies and design decisions behind the .NET framework. The first part of the book gives a general overview of .NET, followed by introductions to both web services and the CLR. The latter part of the book has some example code with one chapter on the different .NET languages and another on the .NET Framework Class Library followed by chapters focusing on ADO.NET and ASP.NET specifically. The author is obviously a Microsoft technologist, but he at least makes an attempt to be objective. One quote I particularly liked was the following:

The people who create software tools often forget that they're almost always much better software developers than the people who will use those tools. As a result, they tend to create tools that they themselves would like to use, tools that are too complex for many of their potential customers.

While the author is talking about VB.NET in this quote, it definitely rings true for developers of software products in general, not just software tools in particular.

While the book does have some good content, I probably wouldn't recommend it to anyone looking to get started in with .NET, mainly because it is a little outdated. I'm probably going to be reading a couple more .NET books over the next few months so maybe I'll have a recommendation soon. Also if any of my reader have any recommendations of good hands-on books for web application development with C# I'd like to hear them. (A co-worker lent me Teach Yourself C# in 24 Hours from Sams Publishing. I've never really been a big fan of that series of books, but it has been awhile since I read one so I may give this one a try.)

ColdFusion and ASP.NET Web Service Interoperability, Part III

Yesterday I described some of the problems I was having getting ColdFusion to talk to an ASP.NET web service as well as a brute force solution to the issue. Well my former co-worker Phil Duba left a comment on that post pointing to the follwing article: Notes on Interfacing ColdFusion MX to External Web Services Requiring Complex-within-Complex XML Documents as Input. This article turned out to be the key to my getting this web service call to work using <cfinvoke>.

[More]

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.

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

For the past few weeks I've been doing some work with a third party service provider trying to integrate their offerings into an in-house application via web services. Their application is written in ASP.NET and ours is written in ColdFusion which has lead to some interesting challenges.

The first issue we had was getting their .NET application to talk to our ColdFusion web service. At first the provider reported that they were seeing the following error when calling our web service:

The underlying connection was closed: An unexpected error occurred on a send.

They were attempting to call our web service every ten minutes, however after looking in our web server logs I was able to determine that they had successfully connected to the web service only once. After doing some research I found a MS Knowledge Base Article which lead me to believe this error was due to a connection timeout. Our server's keep-alive setting was set at 5 minutes. If they were only calling our web service every 10 minutes the connection the client originally opened was being closed by our server. When I pointed out this article and described the issue the provider agreed to have their developers rewrite the calling code in ASP.NET 2.0, which apparently handles the keep-alive properly.

However once the client had been re-written, they reported a new issue. They were now seeing the following error:

The request failed with an empty response.

Just to confirm that the issue wasn't on our end I wrote a quick ColdFusion client which was able to call the web service with no problem. I looked at our logs again and could see that they were not even getting to our web service. This lead me to believe the issue was still in the calling code, but unlike the previous error google didn't turn up anything that seemed to point directly to the issue. At this point I decided to download VB 2005 Express and build a .NET client myself. Having done zero .NET development it took me a few minutes to figure out how to connect to a web service via VB .NET, but once I did it was fairly easy.

In Visual Studio .NET you create a Web Reference to work with web services. Adding a Web Reference is a lot like adding a COM, DLL, or ActiveX reference to you project; basically you just click on Project -> Add Web Reference and type in the URL of the web service you want to work with in the Web Reference window and you are all set. Well almost. Our web service need to be called overSSL so the URL I entered for our Web Reference looked something like "https://our.service.com/component.cfc?wsdl." However whenever I tried to call the web service in my VB client I was getting the same "empty response" error our service provider was reporting. So, I fired up a network sniffer (Ethereal) to see if I could figure out what was going wrong. It turns out that even though I was typing in "https://our.service.com/component.cfc?wsdl" for my Web Reference the VB.NET client was calling "http://our.service.com/component.cfc?wsdl." Security in our application was then trying to redirect the client to the secure URL via <cflocation>, hence the empty response. Now that I knew what the issue was I just had to figure out how to fix it. Turns out it was pretty simple. The VB.NET project has an app.config file which holds, you guessed it, application configuration. One of the entries in this file was the web service URL and upon inspection I found that it was listed as "http://our.service.com/component.cfc?wsdl." I manually edited this entry to use https instead of http and that did the trick. I then notified the provider and they made the corresponding change on their end an we were in business.

This has turned into a bit of a .NET post, but I thought it was worth sharing in case you have others trying to consume your ColdFusion web services via .NET. Up next, what we had to do to get ColdFusion to talk to the service providers .NET web services.

BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.