Consuming ColdFusion RESTful Service - web-services

I created a Rest Service with ColdFusion. It returns an xml file or json like this:
<query id ='1'>
<columnnames>
<COLUMN NAME="Name">
<COLUMN NAME="Surname">
</columnnames>
<ROW>
<COLUMN TYPE="STRING">...</COLUMN>
<COLUMN TYPE="STRING">...</COLUMN>
</ROW>
or
{"COLUMNS":["Name","Surname"],"DATA":[["...","..."]
Is there anyone that can explain how I can parse this data into a grid with ExtJS or another javascript framework?
This is my cfc:
<cfcomponent rest="true" restpath="/hello">
<cffunction name="query" returntype="query" access="remote" produces="application/json" httpmethod="GET">
<cfquery name="myQuery" datasource="dbrc">
SELECT Name, Surname
FROM TBUSERS
LIMIT 10
</cfquery>
<cfreturn myQuery>
</cffunction>
</cfcomponent>
Thanks and sorry for my English.

Have you looked using at the JSONReader in extjs or jQuery.getJSON function?
NOTE :
Despite the fact that the web service was created using ColdFusion, this is a javascript/ext.js question and not a ColdFusion question.

returntype="any" produces="application/json"
in the api cfcomponent is what I used to return the query results in json format
<cfcomponent rest="true" restpath="restService" produces="application/json">
I can't tell if returnformat="JSON" in the cffunction tag does anything for the api being returned in json.
<cffunction name="test" access="remote" returnformat="JSON" returntype="any" httpmethod="GET" >

Related

Coldfusion - Creating SOAP Web Service

Here is the XML format that I'm trying to create a web service for:
<test a1="a1">
<e1>e1</e1>
<e2 a2="a2">
<e3>e3</e3>
<e3>e3</e3>
</e2>
</test>
My problem is that I don't know how to create a Coldfusion (cfcomponent) SOAP web service that would conform to the XML format.
This is what I have come up with:
<cfcomponent style="document" wsversion = 1 >
<cffunction name="test" returntype="String" access="remote">
<cfargument type="String" required="no" name="e1"/>
<cfargument type="xml" required="no" name="e2" />
<cfreturn "ok">
</cffunction>
</cfcomponent>
As you can see, a1,a2 and e3 are left out as I have no idea how to define them in the cffunction, and I'm not sure if making e2 as xml type is correct.
Any help is appreciated.
you have to use
<cfsavecontent variable="textxml">
<cfoutput>
<test a1="a1">
<e1>e1</e1>
<e2 a2="a2">
<e3>e3</e3>
<e3>e3</e3>
</e2>
</test>
</cfoutput>
</cfsavecontent>
and pass the testxml variable to the function as xml type argument.
<cfinvoke method="test" component="compname" xmltest="#textxml#">
no need to send seperate arguments e1 e2...can send testxml variable to the function.
<cfcomponent >
<cffunction name="test" returntype="String" access="remote">
<cfargument type="xml" required="no" name="xmltest" />
<cfset newXML = XMLParse(arguments.xmltest)>
<cfdump var="#newXML#">
<cfreturn "ok">
</cffunction>
</cfcomponent>

cfcomponent web service - getting SOAP attributes

Here is a SOAP request example:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ws="http://ws_test">
<soapenv:Header/>
<soapenv:Body>
<ws:testService a1="a1" a2="a2">
<ws:e1>e1</ws:e1>
<ws:e2>e2</ws:e2>
</ws:testService>
</soapenv:Body>
</soapenv:Envelope>
And here is my example cfc web service:
<cfcomponent style="document" wsversion = 1>
<cffunction name="testService" returntype="String" access="remote" >
<cfargument type="string" name="e1"/>
<cfargument type="string" name="e2"/>
<!--- Missing: code to extract a1 and a2 --->
<cfreturn "#e1# #e2#">
</cffunction>
</cfcomponent>
I'm new to Coldfusion and web service, and I have no idea on how to extract attributes a1 and a2 from <testService>, googled it but can't find any reference. Any ideas?
=== Edit ===
Thought it might be useful if I attach the type definition:
<complexType name="testServiceType">
<sequence>
<element name="e1" type="string"></element>
<element name="e2" type="string"></element>
</sequence>
<attribute name="a1" type="string"/>
<attribute name="a2" type="string"/>
</complexType>
Note that although this is my test web service, but it is based on a data schema that is provided by our partner, which means my web service has to conform with it.
=== Resolution ===
Based on Gerry's answer, this is what I ended up doing:
<cfcomponent style="document" wsversion = 1>
<cffunction name="testService" returntype="String" access="remote" >
<cfargument type="string" name="e1"/>
<cfargument type="string" name="e2"/>
<cfset soapReq = getSOAPRequest()>
<cfset soapXML = xmlParse(soapReq)>
<cfset attributes = soapXML.Envelope.body.XmlChildren[1].XmlAttributes>
<cfset a1 = attributes.a1>
<cfset a2 = attributes.a2>
<cfreturn "#e1# #e2# #a1# #a2#">
</cffunction>
</cfcomponent>
Based on your comment, I think you need getSoapRequest() and then parse it using the code from the answer given by keshav-jha
<cfcomponent style="document" wsversion = 1>
<cffunction name="testService" returntype="String" access="remote" >
<cfargument type="string" name="e1"/>
<cfargument type="string" name="e2"/>
<cfscript>
soapReq=GetSOAPRequest();
soapXML=xmlParse(soapReq);
bodyAttributes = {
a1:soapXML.Envelope.body.XmlChildren[1].XmlAttributes.a1
,a2:soapXML.Envelope.body.XmlChildren[1].XmlAttributes.a2
};
return serializejson(bodyAttributes);
</cfscript>
</cffunction>
</cfcomponent>
You just need to parse your XML and then get the value of a1 and a2
<cfsavecontent variable="myXML" >
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws_test">
<soapenv:Header/>
<soapenv:Body>
<ws:testService a1="a1" a2="a2">
<ws:e1>e1</ws:e1>
<ws:e2>e2</ws:e2>
</ws:testService>
</soapenv:Body>
</soapenv:Envelope>
</cfsavecontent>
<cfset parXML = xmlParse(myXML) />
<cfdump var="#parXML.Envelope.body.XmlChildren[1].XmlAttributes.a1#">
If you are creating the web service, then you have complete control over how the webservice is consumed. In this case a1 and a2 are not ever passed into the CFC for processing. So if they have meaning, you can set them up as parameters like e1 and e2.
One of the best tools I've ever used for understanding and using web services is SoapUI. If you create the example.cfc and point SOAPUI at it, you will get an XML request that looks like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://soap.stack">
<soapenv:Header/>
<soapenv:Body>
<soap:testService>
<soap:e1>e1</soap:e1>
<soap:e2>e2</soap:e2>
</soap:testService>
</soapenv:Body>
</soapenv:Envelope>
If you want to process a1 and a2, there is no reason not to handle them as regular arguments.
So you could make a CFC that looks like this:
<cfcomponent style="document" wsversion = 1>
<cffunction name="testService" returntype="String" access="remote" >
<cfargument type="string" name="a1"/>
<cfargument type="string" name="a2"/>
<cfargument type="string" name="e1"/>
<cfargument type="string" name="e2"/>
<cfset var ret=serializeJSON(arguments) />
<cfreturn "#ret#">
</cffunction>
</cfcomponent>
And if you point SOAPUI at it, then it will generate a SOAP envelope that looks like this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://soap.stack">
<soapenv:Header/>
<soapenv:Body>
<soap:testService>
<soap:a1>?</soap:a1>
<soap:a2>?</soap:a2>
<soap:e1>?</soap:e1>
<soap:e2>?</soap:e2>
</soap:testService>
</soapenv:Body>
</soapenv:Envelope>

Coldfusion CFINVOKE parameter not working?

I want to invoke a cfc file on my webserver, but I always get the error: The required parameter [UserID] was not provided.
Coldfusion code:
<CFINVOKE component="changeTree" method="getTreeWidth" returnVariable="httpTreeWidth">
<cfinvokeargument name="UserID" value="#checklogin.UserID#">
</CFINVOKE>
changeTree.cfc:
<CFFUNCTION name="getTreeWidth">
<CFPARAM name="UserID" required="true">
...
Thanks for help.
In changeTree.cfc, it's suppposed to be <cfargument name="UserID" required="true"> not <cfparam>.

cfxml vs cfsavecontent - unable to build xml using cfxml

I'm trying build a custom XML file and splitting the building into separate functions, but I'm having trouble doing this with cfxml. This example is obviously simplified.
This works fine:
<cfcomponent accessors="true">
<cfproperty name="Name" />
<cfproperty name="Model" />
<cfproperty name="Make" />
<cffunction name="BuildCarXML" output="false">
<cfsavecontent variable="xmlCar">
<cfoutput>
<?xml version="1.0" encoding="UTF-8" ?>
<car>
<name>#variables.Name#</name>
#AddMakeElement()#
</car>
</cfoutput>
</cfsavecontent>
<cfreturn xmlCar />
</cffunction>
<cffunction name="AddMakeElement">
<cfsavecontent variable="xmlMake">
<cfoutput>
<make>Something</make>
</cfoutput>
</cfsavecontent>
<cfreturn xmlMake />
</cffunction>
</cfcomponent>
But this produces an XML string with spaces:
<?xml version="1.0" encoding="UTF-8" ?> <car> <name>Ferrari</name> <make>Something</make> </car>
If I use cfxml, or even do an XMLParse() on the cfreturn of BuildCarXML, i get the following error:
An error occured while Parsing an XML document.
The processing instruction target matching "[xX][mM][lL]" is not allowed.
Is it possible to do this using cfxml?
In AddMakeElement(), if you use <cfxml> and toString(), the output is:
<?xml version="1.0" encoding="UTF-8"?> <make>Something</make>
Therefore it couldn't be embedded into your xmlCar. So for AddMakeElement(), keep using <cfsavecontent>, or just return "<make>Something</make>".

why is it not possible to call two methods of the same component sequentially in Coldfusion?

So, I have allmost spend the night chasing a bug.... found it and no idea what is wrong.
I have script in Coldfusion which sends two emails. Both mails are in a mailer script which I'm calling with cfinvoke like so:
<cfinvoke component="form_mailer_basket" method="msg_order_seller">
... parameters
</cfinvoke>
<cfinvoke component="form_mailer_basket" method="msg_order_retailer">
... parameters
</cfinvoke>
Both mail parameters are all ok, but the 2nd mailer throws an error:
mailer orders
************************************************************************************
type: Application
************************************************************************************
message: Could not find the ColdFusion Component or Interface form_mailer_basket.
************************************************************************************
detail: Ensure that the name is correct and that the component or interface exists.
************************************************************************************
Question:
Can anyone tell me why the 2nd mail cannot find the component when the first script 5 lines above can?
Thanks!
EDIT:
Here is my code for calling both methods:
<cfif new_mail.recordcount GT 0>
<cfloop query="new_mail">
<cfset variables.newMail = new_mail.email_bestelleingang>
<cfinvoke component="form_mailer_basket" method="msg_order_seller">
<cfinvokeargument name="delDate" value="#variables.liefdatum_mail#"/>
<cfinvokeargument name="delMsg" value="#variables.bestell_text_mail#"/>
<cfinvokeargument name="delOrd" value="#LOCAL.Basket.bestelltyp#"/>
<cfinvokeargument name="mailto" value="#variables.newMail#"/>
<cfinvokeargument name="client" value="#LOCAL.Basket.re_firma#"/>
<cfinvokeargument name="rebate" value="#variables.kopf_rabatt#"/>
<cfinvokeargument name="sellerIln" value="#variables.iln_verkaeuferNEU#"/>
<cfinvokeargument name="ordNo" value="#variables.bestellnummer_neu#"/>
</cfinvoke>
</cfloop>
</cfif>
...
<cfloop query="active_check">
<cfif active_check.freigeschaltet NEQ "1" AND active_check.freigeschaltet NEQ "0">
<cfinvoke component="form_mailer_basket" method="msg_order_retailer">
<cfinvokeargument name="delDate" value="#variables.liefdatum_mail#" />
<cfinvokeargument name="delOrd" value="#LOCAL.Basket.bestelltyp#" />
<cfinvokeargument name="mailto" value="#variables.cusMail#" />
<cfinvokeargument name="client" value="#order_recipients.firma#" />
<cfinvokeargument name="rebate" value="#variables.kopf_rabatt#" />
<cfinvokeargument name="sellerIln" value="#variables.iln_verkaeuferNEU#" />
<cfinvokeargument name="ordNo" value="#variables.bestellnummer_neu#" />
<cfinvokeargument name="total" value="#variables.gesamtsumme#" />
<cfinvokeargument name="menge" value="#variables.gesamtmenge#" />
<cfinvokeargument name="curr" value="#variables.waehrung#" />
<cfinvokeargument name="agentF" value="#variables.agentFirma#" />
<cfinvokeargument name="agentN" value="#variables.agentName#" />
</cfinvoke>
</cfif>
</cfloop>
First one works, second one doesn't. Method names are correct, all parameters are ok (I know I should use an argumentsColletion...), so I'm clueless and need to take a nap. Checking back later!
And the cfc:
<cfcomponent output="false" hint="basket mailing cfc - sends out all basket related mail messages">
<!--- LOAD LANGUAGES --->
<cfinclude template="../templates/tmp_lang.cfm">
<!--- INIT --->
<cffunction name="Init" access="public" returntype="any" output="false" hint="Initialize">
<!--- nothing here for now --->
<cfreturn true />
</cffunction>
... msgs like this:
<!--- NEW ORDER SELLER --->
<cffunction name="msg_order_seller" access="public" output="false" hint="msg for new orders">
<cfargument name="delDate" type="date" required="true" hint="delivery date" />
<cfargument name="delMsg" type="string" required="true" hint="text message by retailer" />
<cfargument name="delOrd" type="string" required="true" hint="order type pre/asap" />
<cfargument name="mailto" type="string" required="true" hint="email adress" />
<cfargument name="client" type="string" required="true" hint="buyer" />
<cfargument name="rebate" type="string" required="true" hint="rebate 1/0" />
<cfargument name="sellerIln" type="string" required="true" hint="seller ILN" />
<cfargument name="ordNo" type="string" required="true" hint="order number" />
<cfprocessingdirective suppresswhitespace="No">
<cfmail
TO="#mailto#"
FROM="automailer#..."
SERVER="mail.bbb.de"
USERNAME="ddd"
PASSWORD="123456"
SUBJECT="#tx_automailer_order_new# - #client#">
#tx_automailer_default_anrede#
#tx_automailer_order_info#
#tx_automailer_order_type#: #ordertype# #rebateTxt#
#tx_automailer_order_del#: #deliveryDate#
#tx_automailer_order_no#: #ordNo#
#tx_automailer_order_date#: #DateFormat(now(),"dd.Mm.yyyy")#
#tx_kaeufer#: #client#
#tx_automailer_order_msg#:
#delMsg#
#tx_automailer_order_iln#: #sellerIln#
#tx_automailer_default_gruss#
#tx_automailer_default_team#
-------------
#tx_automailer_default_disclaimer#
</cfmail>
</cfprocessingdirective>
<cfreturn />
</cffunction>
...
</cfcomponent>
If you just want to solve your problem instead of trying to figure out what is causing it, I have a suggestion. Instead of using the cfinvoke tag for the same component many times, use either cfobject or CreateObject() to make just one instance of it. Then call the methods directly.
Even if you do get your current approach to work, it will be slower than my suggestion. cfinvoke creates an object each time you call it and that takes processing time.
Are you sure that both methods exist and are public?
Using cfinclude in cfc is NOT good practice, though admittedly I had to do it too on occasions.
Your error Could not find the ColdFusion Component or Interface form_mailer_basket seems to imply that something happens to the component itself - I suspect it has something to do with your return statement in Init() method.
In earlier versions of CF (I think around version 6/7 maybe 8) you could overwrite your function by setting identically named variable inside your cfc. You did not mention the CF version that you are running.