My First ColdFusion Web Service
Posted December 12, 2006 at 8:30 AM
I know, I know, I'm like a decade behind everyone else, but I JUST started playing around with web services. I have just never had to invoke or provide one before and so never experimented with it. I had kind of a rocky start with this example, but after some Googling and some Adobe documentation, I got it off the ground.
Application.cfc And OnRequest() Event Method
My first hurdle was getting my application to allow ColdFusion Components (CFCs) to be called directly. My application uses the OnRequest() event method in the Application.cfc ColdFusion component and as you may or may not know, doing so blocks the use of web services and flash remoting. To get around this, first I created a directory for my web services at "/resources/webservices/". Then, I applied a dirty little trick I picked this up from Ray Camden as explained in the first issue of the Fusion Authority Quarterly; I destroyed the OnRequest() event method for web service calls:
Launch code in new window » Download code as text file »
- // Check to see if the target page is in the web services
- // directory. If it is, then we want to delete the OnRequest
- // event handler so the web service has access.
- if (ExpandPath( GetDirectoryFromPath( ARGUMENTS.TargetPage ) ) EQ APPLICATION.ServiceFactory.GetConfig().GetFullPaths().WebServices){
-
- // Delete the on request event handler.
- StructDelete( THIS, "OnRequest" );
-
- // Delete the on request end handler.
- StructDelete( THIS, "OnRequestEnd" );
-
- }
While I think Ray only recommended the OnRequest() event method removal, my OnRequestEnd() event method was causing issues (via the CFFlush tag) so I had to remove it as well. I placed this at the very end of the OnRequestStart() event method. Since that event method fires before the OnRequest() event method, it is able to successfully alter the pre-page processing event chain. Now, all calls to ColdFusion components (or any other files for that matter) located inside the "resources/webservices/" directory can be accessed directly. Thanks Ray!
BaseWebService.cfc ColdFusion Component
Then, I started my first ColdFusion component with remote access for use with web services. Stuff quickly started to break and I had no idea what was going on. I started getting errors like this:
Web service operation "GetCompliment" with parameters {Gender={}} could not be found.
But that was working a second ago! It just had a different name. Wait, how come it worked for this method and not that one? Wait, that method worked a second ago and now that I added an argument it no longer works! What gives!!!!
This was all very frustrating. Then I read that by calling the ColdFusion component build something called a "Stub" file. I am not 100% clear on how this all works, but supposedly this stub file defines what the web service does and how it works using the WSDL standard. The tricky part is that this stub file is basically only written once per file. If you go to alter the web service you will quickly find out that stuff breaks because the stub file is now out dated.
Apparently, there is something you can do in the ColdFusion Administrator to fix this (do a manual refresh or something), but I don't like going to the Admin for stuff. That is lame. I want to do it programmatically. Thanks to B. Prucell, I was able to rebuild the stub file using the ColdFusion Service Factory.
Now, since I don't really know what I am doing, I figured this is something I would want to build into the web services so that I could easily rebuild any web service stub file. To accomplish this, I created a base web service component that provides a default Initialization method and RebuildStubFile method:
Launch code in new window » Download code as text file »
- <cfcomponent
- displayname="BaseWebService"
- output="false"
- hint="This handles core web service features.">
-
- <cffunction
- name="Init"
- access="public"
- returntype="any"
- output="false"
- hint="Returns an initialized web service instance.">
-
- <!--- Return This reference. --->
- <cfreturn THIS />
- </cffunction>
-
-
- <cffunction
- name="RebuildStubFile"
- access="remote"
- returntype="void"
- output="false"
- hint="Rebuilds the WSDL file at the given url.">
-
- <!--- Define arguments. --->
- <cfargument name="Password" type="string" required="true" />
-
- <!---
- Check to make sure this user has access to this
- feature. This probably shouldn't be a remote access
- function, but for the moment, I had no idea where
- else to put it.
- --->
- <cfif NOT Compare( ARGUMENTS.Password, "sweetlegs!" )>
-
- <!---
- Rebulid this stub file for the web service using
- the ColdFusion service factory. I picked this
- tip up from:
- http://www.bpurcell.org/blog/index.cfm?mode=entry&ENTRY=965.
-
- Notice here that we are getting the requested
- URL, which will be the web service coldfusion
- component that was requested. Then we append
- the "?wsdl" web service directive to the end
- of the url.
- --->
- <cfset CreateObject( "java", "coldfusion.server.ServiceFactory" ).XmlRpcService.RefreshWebService(
- GetPageContext().GetRequest().GetRequestUrl().Append( "?wsdl" ).ToString()
- ) />
-
- </cfif>
-
- <!--- Return out. --->
- <cfreturn />
- </cffunction>
-
- </cfcomponent>
As you can see, I do some password protection; I don't want just ANYBODY rebuilding my stub files. Of course, this should probably not be remote access, but like I said, I don't really know what I am doing yet. This is my first web service experience. The thinking here though is that once I get this stub file built, I am very rarely going to change the BaseWebService component so even if the stub file is out dated, the RebuildStubFile() method will mostly likely be defined and available for use.
Fun.cfc ColdFusion Component
Ok, so now that I have my base web service ColdFusion component, I could allow my Fun web service to extend it:
Launch code in new window » Download code as text file »
- <cfcomponent
- displayname="Fun"
- extends="BaseWebService"
- output="false"
- hint="Handles web services that are fun and experimental.">
-
-
- <cffunction
- name="GetCompliment"
- access="remote"
- returntype="string"
- output="false"
- hint="This returns a random compliment.">
-
- <!--- Define arguments. --->
- <cfargument name="Gender" type="string" required="false" default="" />
-
- <cfscript>
-
- // Define the local scope.
- var LOCAL = StructNew();
-
- // Set up non-gender based compliments.
- LOCAL.Universal = ArrayNew( 1 );
- LOCAL.Universal[ 1 ] = "You are a great person.";
- LOCAL.Universal[ 2 ] = "I wish I had more friends like you.";
- LOCAL.Universal[ 3 ] = "You are too nice to people.";
- LOCAL.Universal[ 4 ] = "You are my Go-To guy!";
- LOCAL.Universal[ 5 ] = "I really appreciate what you do.";
-
- // Set up female compliments.
- LOCAL.Female = ArrayNew( 1 );
- LOCAL.Female[ 1 ] = "You're hair really looks nice today.";
- LOCAL.Female[ 2 ] = "Those are some great shoes!";
- LOCAL.Female[ 3 ] = "You are a great woman.";
-
- // Set up male compliments.
- LOCAL.Male = ArrayNew( 1 );
- LOCAL.Male[ 1 ] = "You look great! Have you been working out?";
- LOCAL.Male[ 2 ] = "Dude, that's a rockin' mustache.";
- LOCAL.Male[ 3 ] = "You are a great dude.";
-
- // Pick the type of compliment we will be doling out.
- switch ( ARGUMENTS.Gender ){
- case "M":
- LOCAL.Compliments = LOCAL.Male;
- break;
- case "F":
- LOCAL.Compliments = LOCAL.Female;
- break;
- default :
- LOCAL.Compliments = LOCAL.Universal;
- break;
- }
-
- // Return a random compliment.
- return(
- LOCAL.Compliments[
- RandRange(
- 1,
- ArrayLen( LOCAL.Compliments )
- )
- ]
- );
-
- </cfscript>
- </cffunction>
-
- </cfcomponent>
This web service component has one method, GetCompliment(). This web service takes an optional "Gender" argument and returns a gender specific or non-specific compliment. This presented a new problem. Everything worked fine and dandy when I tried to call the web service using the gender argument:
Launch code in new window » Download code as text file »
- <!--- Invoke the web service as a dude. --->
- <cfinvoke
- webservice="http://..../webservices/Fun.cfc?wsdl"
- method="GetCompliment"
- returnvariable="REQUEST.Compliment">
-
- <cfinvokeargument name="Gender" value="M" />
- </cfinvoke>
-
- <!--- Display compliment. --->
- #REQUEST.Compliment#
However, if I tried to invoke this web service without the argument:
Launch code in new window » Download code as text file »
- <cfinvoke
- webservice="http://..../webservices/Fun.cfc?wsdl"
- method="GetCompliment"
- returnvariable="REQUEST.Compliment"
- />
-
- <!--- Display compliment. --->
- #REQUEST.Compliment#
... it totally crashed and burned and give me this error:
Web service operation "GetCompliment" with parameters {} could not be found. <br>The error occurred on line 45.
After some hair pulling, some head pounding, and a good amount of Googling, I finally came across a posting by Steven Erat over at Talking-Tree. Apparently, for a web service you have to pass in optional arguments but tell the web service to omit them:
Launch code in new window » Download code as text file »
- <cfinvoke
- webservice="http://..../webservices/Fun.cfc?wsdl"
- method="GetCompliment"
- returnvariable="REQUEST.Compliment">
-
- <cfinvokeargument name="Gender" value="" omit="true" />
- </cfinvoke>
-
- <!--- Display compliment. --->
- #REQUEST.Compliment#
Notice that this time, my CFInvokeArgument tag uses the Omit attribute (and sets it to true). This invokes the web service and I guess does not pass in the argument, or I guess it passes it in (to jive with the WSDL document) but is just never used? No idea. All I know is that it works.
If you want to give it a go, the Fun.cfc web service is available at:
http://www.bennadel.com/resources/webservices/Fun.cfc?wsdl
Download Code Snippet ZIP File
Post Comment | Ask Ben | Permalink | Other Searches | Print Page
Newer Post
Import Lesson: Always Codify Change Requests From Clients
Older Post
A Small Calendar Utility For Reference
Reader Comments
Ben,
It's probably just me, because I haven't yet found a need to do a CF webservice. The one service I ever provided was in .NET, for a desktop CMS. But, that is beside the point.
What I don't understand is why you had to go through so much trouble? For instance, why did you have to delete the OnRequestXXX() methods? Why provide them in the first place? It seems like a lot to go through... (and that is what I'm most intersted in)
The other comment I had was about the need to provide an optional argument. I know its not your fault, so don't take this as jab at you =). But, that just seems quite silly. <cfinvokeargument name="Gender" value="" omit="true" /> .... I mean, all that typing/clutter to accomplish nothing except omitting the argument that shouldn't have to be there in the first place! =)
It reminds me of the Java program that does absolutely nothing (as opposed to say, the CF program that does nothing). It's quite long, but it works well! =)
A large application I work on uses web services to communicate with its CFCs which reside on a physically separate application server. Yes I am in hell. CF makes working with web services pretty easy, but there are a number of hair-pulling pieces such as using optional arguments and other issues like trying to debug errors without file names and line numbers. I ended up creating a web service wrapper that solves most of these issues for me. If you end up coming across a funky error that you can't figure out, shoot me an email - chances are I've come across it and fudged my way through it.
@Rob,
Thanks for the offer. I am sure I will hit you up with some stumps :)
@Sam,
As far as the OnRequest() and the OnRequestEnd() event method, I provide them for other parts of the application. My OnRequest() event performs some security checks. My OnRequestEnd() performs some post page processing and logging type stuff. The problem is that these are not very web-service friendly. So, instead of changing the way I architect my application, I merely delete them if it's a web service call.
And, as far as the "omit" for the optional argument, I am sorry if I was not clear on this fact. Yes, it seems silly to include an argument that you are omitting. The problem is, though, that if you simply do not send the argument (leave out the CFInvokeArgument tag itself), the web service will crash. It says it cannot find the matching web service definition or something. You have to include the "omitted" argument so that ColdFusion can match the web service request to the WSDL definition. At least, that is what I think is going on. But I agree, it seems very silly.
Ben,
Instead of destroying the onrequestend and onrequest, you might want to check out sean's post about extending the application.cfc. This might be a better approach. Let us know.
http://www.corfield.org/blog/index.cfm/do/blog.entry/entry/Extending_Your_Root_Applicationcfc
Tony,
While Sean's solution is definitely a nice, elegant one, I cannot use it. I have no application mappings in my applications and therefore cannot extend applicaition.cfc files that are in different directories.
Thanks for the link though. As I said, this is my first web service so I am looking to learn all I can.
Looks like you had a lot of fun with this! Since this article is catch all for a variety of webservice issues, here's a recent cftalk thread with suggestion for how to deal with webservers returning the WSDL in compressed format:
http://www.houseoffusion.com/groups/CF-Talk/thread.cfm/threadid:49353
Steven,
Thanks for the tip :) That's some good stuff.
I am trying to do a simple web service in cold fusion and I am getting errors? Any ideas? This is the code?
<cfobject type="JAVA"
action="Create"
name="factory"
class="coldfusion.server.ServiceFactory">
<cfset RpcService = factory.XmlRpcService>
<cfset RpcService.refreshWebService("https://www.devcallnow.com/WebService/OneCallNow.asmx?wsdl")>
<cfinvoke webservice="https://www.devcallnow.com/WebService/OneCallNow.asmx?wsdl" method="Login" returnvariable="LoginToken">
<cfinvokeargument name="Service" value="1" omit="true" >
<cfinvokeargument name="GroupKey" value="2222" omit="true" >
<cfinvokeargument name="Pin" value="3333" omit="true" >
</cfinvoke>
@Dave,
What errors are you getting?
-Ben
I kept getting not found..But thanks to the help of Paul from cf-talk this is what was discovered. I'm still not sure what all of it means.
Just to follow up on this, it seems that the webservice David was trying to consume using wsdl doesn't fit into the wsdl way of doing things in that it attempts to set one of the invoking arguments and pass that back as well as a complex object.
To get around this, I've told him to invoke the service using cfhttp instead of cfinvoke and parse the resulting XML to get the data he needs...
<cfsavecontent variable="xmlrequest"><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>
<Login xmlns="https://www.onecallnow.com/WebService/">
<Service>1</Service>
<GroupKey>1111111</GroupKey>
<Pin>1111</Pin>
<LoginToken></LoginToken>
</Login>
</soap:Body>
</soap:Envelope>
</cfsavecontent>
<cfhttp method="post"
url="http://www.devcallnow.com/WebService/OneCallNow.asmx">
<cfhttpparam type="xml" value="#xmlrequest#">
</cfhttp>
<cfset SOAPXML = XMLParse(cfhttp.Filecontent.toString())>
<cfset response =
XMLParse(toString(SOAPXML["soap:Envelope"]["soap:Body"]["XmlChildren"][1]))>
<cfdump var="#response#">
<cfoutput>#response.LoginResponse.LoginToken#</cfoutput>
Hi Ben & everyone reading this excellent article!
Once again you saved the day, Ben!
Just slightly modified the snipped to look like this:
ReFindNoCase("CMS/CFC",GetDirectoryFromPath(cgi.SCRIPT_NAME))...
(since I'm storing all my CFC's in that directory.)
Then off course, we have to keep in mind that onRequestStart won't be available to any cfc, but modifying the snippet further will probably handle this to make the method available (or not) for just some cfc's
Thank you!
# if (ExpandPath( GetDirectoryFromPath( ARGUMENTS.TargetPage ) ) EQ APPLICATION.ServiceFactory.GetConfig().GetFullPaths().WebServices){
#
# // Delete the on request event handler.
# StructDelete( THIS, "OnRequest" );
#
# // Delete the on request end handler.
# StructDelete( THIS, "OnRequestEnd" );
#
# }
Hi Ben,
where would u put this code snippet ?in onrequeststart?
-Roman
@Roman,
Put that in the OnRequest() method.
i got kind lost.
u r using StructDelete( THIS, "OnRequest" ); in onrequest?
+ if u use onrequest u will have to call explicitly file cgi.SCRIPT_NAME.
@Roman,
I am sorry, I made a mistake. Put it in the OnRequestStart() method. That way, it can delete the "OnRequest" key before it has been executed.
Thanks for this post. The note about including unused parameters with omit saved me a potential late night at work :)
@Scott,
Yeah, that will trip you up for sure.
Ben first web service: 2006
Robert's first web service: 2008
Thanks for this Ben. It helped me out quite a bit. I'm on my way. Baby steps, baby steps.
@Robert,
No problem my man!
hi Ben, i want to write a web service for an online aboratory, please how do i start?
How do you call this webservice (or any webservice) via a http-request in the browser without triggering the CFCExplorer in CFAdmin that tries to show the documentation of the webservice. I'm banging my head against the wall because the CFC is OK, the generated WSDL is OK, but calling it everytime like this (http://test.com/webservice.cfc) invokes the CFCExplorer. And as CFAdmin is not availableon production servers it throws a CF error...
HELP!
I'm on CF 8.0.1 Enterprise by the way...
@Sebastiaan You have to append the url with ?wsdl like http://test.com/webservice.cfc?wsdl
Whenever you manually request a CFC manually through a browser (which translates an HTTP GET Request with no parameters), regardless if access is remote, public, or private, the request will redirect to the CFCExplorer to provide "self-documentation" on the CFC properties and functions.
When you invoke a ColdFusion webservice programmatically, you provide it an argument for URL with the ?wsdl query string. To invoke foo.cfc on company.com, you might use the url http://www.company.com/foo.cfc?wsdl. ColdFusion will generate and return a WSDL to the invoker. The invoker will then parse the WSDL, find the ServicePort (which itself is a URL indicating where the actual service endpoint, which in CF would probably be http://www.comapny.com/foo.cfc. The invoker will then send an HTTP Post Request with a SOAP Envelope bearing a SOAP Request to the service endpoint. The service endpoint processes the request and returns a SOAP Envelope containing a SOAP Response (or SOAP Fault).
The point is that in one case you request the cfc manually as a GET Request, and in the other case the invoker POSTs a SOAP Request to the to the cfc. The former triggers the CFCExplorer, the latter does not.
@Steven and @Robert: Thanx!
It helped me some, but I'd really like to post some code here, but the CFC is a bit sensitive, so not sure howto do this! It's a simple component with an init function and a function that does something. This latter function can receive some parameters (of which some are mandatory and some are not) and the function (depending on a cftry/cfcatch-block) returns one numeric character for pass/fail. What am I overlooking, how do I tell the client how to call this webservice so that it works at his end (IE, that he can pass in the parameters and let the CFC do its thing)?
Am I making sense or just missing the really big point you've already pointed out?
@Sebastiaan,
Is your client using Coldfusion?
Nope, he's on a Filenet system just calling the webservice just like you described in your first reply to my comment. The client claims he cannot parse the arguments in the wsdl if they are of the type xsd:double (<wsdl:part name="parametername" type="xsd:double" />). He expects a xsd:numeric. In my CFC the type of the cfargument "parametername" is numeric and the returntype of the function is also numeric.
Is this built into CF8 or can I do anything about this? In other words, how can I get the wsdl:part to be of type xsd:numeric instead of xsd:double? I think this is the bottleneck...
ColdFusion uses Apache Axis under the hood to implement WebService functionality. Complex types are frequently a topic of discussion in the ColdFusion community.
Tom Jordahl is the architect behind the Webservice implementation in CF, and he's on the Webservice W3C committee. He has many articles on working with complex types in CF. Here's a couple that might be useful here:
http://tjordahl.blogspot.com/2008/07/for-some-reason-i-havent-actually-even.html
http://tjordahl.blogspot.com/2008/04/reprint-consuming-web-service-complex.html
In CF7, CF8 and in CF9 CF datatype numeric translates or is converted to xsd:double. In the CF livedocs and in the new CF9 help it nowhere states how this can be changed (http://livedocs.adobe.com/coldfusion/8/webservices_15.html and http://help.adobe.com/en_US/ColdFusion/9.0/Developing/WSc3ff6d0ea77859461172e0811cbec22c24-78b2.html). Builtin feature it seems, any ideas? Maybe save the wsdl, change the datatype and havemy client call the wsdl directly instead of the CFC? Not fun if the CFC was to change sometime in the future...
@Sebastiaan,
Hmm, that's beyond my expertise. However, I had some issues in my web service with some calls and decided to leave out the type parameter. If I did that, everything worked ok. I may get slapped by the professionls (of which I am not one) but that's what I did.
Also, a brief look at Ben Forta's MX7 book equated the cf datatype of "numeric" with the wsdl data type of "SOAP-ENC:double". Don't know if that helps or not.
-Robert
>> "Maybe save the wsdl, change the datatype and havemy client call the wsdl directly instead of the CFC?"
The wsdl does not implement the webservice, it only describes it. Changing the datatype in the wsdl will only break the webservice if the implementation (the CFC) does not operate as described by the wsdl.
@Steven: OK, that clears out that option, I guess my client will have to adapt his code to accept xsd:double...
@Robert: The datatype conversion you mention was also what I was pointing to in my two links. This hasn't changed since CF6...
Also, just wanted to throw in there that you don't have to invoke a web service using SOAP. Wasn't sure if this option was talked about. As far as invoking a CFC via the URL, you should get around the CFC Explorer if you add in the "method" parameter:
./webservice.cfc?method=someMethod
It sounds like you guys are doing SOAP, but just wanted to add those two cents.
@Ben: Thanx, I now can test m webservice properly ;-)
@All: We ended up changing the datatype from numeric to string, because a xsd:double delivers 2,0 in the call to the CFC when reading the WSDL and the CFC coughs immediately that the argument not is of type numeric. Sigh...
Anyways, we have it working now, that's the most important thing! Thanx for all the good advice and the back-and-forth on possible solutions ;-)
@Sebastiaan,
Awesome - glad you got it working.
Good reference and some great tips. Thanks!
Ben, I need some clarification. In the listing for your Fun service (Fun.cfc ColdFusion Component), the function GetCompliment has an argument named Gender, with an access attribute of "remote", but a required attribute of "false", and with a default value. According to the ColdFusion 8 docs, all arguments to a ColdFusion web service function are considered required. The attribute required="false" is ignored. Am I correct on this?
@Christopher,
When it comes to invoking web services, all arguments are required as far as the need to be defined; however, the CFInovkeArgument allows you to both define and omit an argument, letting it's default value be used.
Take a look at the last code sample - in it, I am defining the argument, but I am using the "Omit" attribute to NOT send a value.
So, yes, they all need to be defined in the invoke, but they don't need to be *passed*.
Thanks for calling my attention to that which I had missed. I now know which tack I should take with my service. Good day, sir.
@Christopher,
Good luck my friend! Drop by if you have any more questions.
Ben,
If you wanted to call a ColdFusion wsdl through the url, and the method has arguments, what would the syntax be?
Ie:
http://www.company.com/webservice.cfc?wsdl&method=someMethod&arguments=???
Ben,
I think I figured out my problem. Just needed to match up the post variables with the CFC arguments. Thanks anyways.
@Jordan,
I don't believe you can post WSDL-based (SOAP) web services using URL parameters. You have to create an actual SOAP request. If you want to use the URL-style post, you don't want to do SOAP - just call the method you want and then each argument as a name-value pair in the URL query string.
Hi Ben,
I am converting some web services that I wrote in Java with Axis 1.1 some years ago to Cold Fusion webservices.
I need to use the same WSDL that was provided to our clients. Now CFCOMPONENT has an option that allows you to specify the WSDL file:
wsdlFile = "mywsdl.wsdl"
However, when I try to invoke my web service like this I am getting the standard error that we have all seen:
Web service operation MywebService with parameters xyz cannot be found.
Are there any gotcha's when using a manually assigned wsdl file like this? Is anyone else using this technique where you are using wsdlFile="".
Would be grateful for any insights.
Khalid
Does anyone know of a way to dynamically change the endpoint of a webservice when using the cfhttp method of calling a wsdl? With createobject, I know you can do:
<cfset ws = CreateObject("webservice", "#variables.wsdlLoc#", "myPORT")/>
<cfscript>
ws._setProperty("javax.xml.rpc.service.endpoint.address", "https://newendpoint.com/test.jws");
</cfscript>
which dynamically changes the endpoint - but how is this done with cfhttp to the wsdl?
Here's a good one...
Let's say I have a basic cfc with 2 methods
setSomeValue()
This method takes 1 arguments and sets it to the variables scope.
getSomeValue()
This method returns variables.someValue (whatever was set in the previous method)
REALLY straight forward stuff here.
This works if the methods are set to PUBLIC, but switch them to REMOTE and change the invocation code and suddenly getSomeValue has nothing to return.
I can't find anywhere that states that webservice cfcs can NOT store values. Can ANYONE help me out here?
Thanks again!
@Khalid,
That's very interesting - I was not aware that there was an option to provide an already existing WSDL file. I will have to look more into that; at this time, I do not have any good advice.
@Jim,
To be honest, I am not even sure what an endpoint is. I typically make SOAP-based web service calls with raw XML and CFHTTP. Sorry I cannot be more help at this time.
@Ryan,
The problem with this is that the CFC is created fresh for every remote access method call. As such, the data from the SET method is not there when the GET method is called.
If you want to do this, you will have to cache the remote CFC instance and then wrangle the remote requests manually. Of course, if you are making SOAP-based requests, I don't think this can be done, since ColdFusion does so much SOAP-based auto-wiring for you. Typically, this kind of manual routing can only be done with URL-based method calls.
Ben - Makes total sense. I had a feeling that since CF converts the CFC to a WSDL document, you lose a lot of functionality that CFCs usually provide.
I suppose when I go to refactor my app, I should highly consider a RESTful approach. That does seem to be the "proper way to do it" these days.
Thanks for the help!
No prob Ben, thanks for responding. I have another question for you or anyone who might be watching. When I invoke a webservice that was built with java and it has a complex object and the stubs ColdFusion creates is 99% correct and the resulting XML request just needs to be tweaked a very small amount (remove a typing) for it to process..which I've validated using soapUI. Any idea on how this can be done? Can I create a version of the stub myself and use that instead of CF recreating the stub? Any guidance is greatly appreciated.
@Jim,
Typically, with XML-based web services, I generally craft the XML manually and post it using CFHTTP; then, I manually parse the XML that comes back in the response. While the CF-SOAP stuff is very cool, I have found it to be a bit too complicated sometimes. And, as much as it might sound backwards, dropping down into the raw XML tends to simplify things for me.
Ben, What are your thoughts on "RESTful" webservices (I still have a hard time calling them webservices since they aren't REALLY).
I'd be interested in reading your approach to building them with CF.
@Ryan,
I'm definitely a fan of URL-based web services. I'll resist calling the RESTful since that's not really the kind that I build. When it comes to ColdFusion, you acn invoke both CFM and CFC templates using URL invocation:
SomeObject.cfc?method=sometMethod
... and:
some_template.cfm?method=someMethod
They both work well and are easy to test. CFC-based invocation has some additional data handling that it does for you, but also adds some complexity as well (that is often easier to handle in a CFM-based approach).
I am happy to use either one, usually called using a standard HTTP request (either from CFHTTP or from an AJAX request).
@Ryan,
As far as what a "Web service" actually is, I don't think there is a particular format that is required to make it a web service. SOAP, REST, whatever - they are all just ways to allow two machines to communicate (which I believe is all the definition of a web service is).



