Invoking My First Web Service With ColdFusion (Amazon Web Service)

Posted December 16, 2006 at 11:45 AM by Ben Nadel

Tags: ColdFusion

Not so long ago, I created my first web service. I figured it was time to see how the other half live (those that invoke web services, not create them). To test it out, I decided to invoke one of Amazon.com's web services. To start out, I had to register to use their eCommerce web service. Then, I had to dig through many many links to actually find some documentation on how to use this stuff. The whole Amazon Web Service (AWS) section is a bit confusing to someone who has never used it before. It is hard to differentiate between examples and pre-built solutions and documentation.

Finally, I figured it out, and worked out this small example to search for a book with the title "Muscle: Confessions of an Unlikely BodyBuilder." This happens to be an amazing book and for anyone who loves to workout, this is a must-read!

But anyway, onto the code:

  • <!---
  • Use the REST web service that uses traditional URL
  • invokation. In this methodology, the arguments for
  • the web service are sent as standard, encoded URL
  • arguments (essentially query string arguments).
  • --->
  • <cfhttp
  • url="http://webservices.amazon.com/onca/xml"
  • method="GET"
  • result="REQUEST.WSResponse">
  •  
  • <!--- Desired Amazon web service. --->
  • <cfhttpparam
  • type="URL"
  • name="Service"
  • value="AWSECommerceService"
  • />
  •  
  • <!--- Authorization ID for use of Amazon Web Services. --->
  • <cfhttpparam
  • type="URL"
  • name="AWSAccessKeyId"
  • value="xxxxxxxxxxxxxxxxxxxx"
  • />
  •  
  • <!---
  • Action to take on this web service. In this case,
  • we are going to be searching the product catalog.
  • --->
  • <cfhttpparam
  • type="URL"
  • name="Operation"
  • value="ItemSearch"
  • />
  •  
  • <!---
  • Searching must be done within a macro-category. In
  • this case, we are searching for books.
  • --->
  • <cfhttpparam
  • type="URL"
  • name="SearchIndex"
  • value="Books"
  • />
  •  
  • <!--- Search for books of this title. --->
  • <cfhttpparam
  • type="URL"
  • name="Title"
  • value="Muscle confessions bodybuilder"
  • />
  •  
  • </cfhttp>
  •  
  •  
  • <!---
  • Check to see if the request hit a valid page and that the
  • generated reponse is a valid XML document.
  • --->
  • <cfif (
  • Find( 200, REQUEST.WSResponse.StatusCode ) AND
  • IsXml( REQUEST.WSResponse.FileContent )
  • )>
  •  
  •  
  • <!---
  • We have gotten a valid response back from the web
  • service. Before we start to work with it, let's strip
  • out all XML name spaces. This will make my XPath
  • search queries much easier (HEY! I don't know that much
  • about XPath so get off my back).
  •  
  • We are stripping out any instance of something like:
  • xmlns="...."
  •  
  • This should create simple XML nodes.
  • --->
  • <cfset strXmlData = REQUEST.WSResponse.FileContent.ReplaceAll(
  • "\s*xmlns=""[^""]*""",
  • ""
  • ) />
  •  
  •  
  • <!---
  • Now that we have cleaned the XML data, let' parse it
  • into a ColdFusion XML document structure.
  • --->
  • <cfset xmlResults = XmlParse( strXmlData ) />
  •  
  • <!--- Get the item nodes. --->
  • <cfset arrItems = XmlSearch( xmlResults, "//Item" ) />
  •  
  • <!---
  • Get the error nodes. This will contain any problems
  • that occurred with the web service invokation.
  • --->
  • <cfset arrErrors = XmlSearch( xmlResults, "//Error" ) />
  •  
  •  
  • <!--- Check to see if we have any errors. --->
  • <cfif ArrayLen( arrErrors )>
  •  
  • <!---
  • Something has gone wrong with the web service.
  • Output these errors.
  • --->
  • <cfloop
  • index="intError"
  • from="1"
  • to="#ArrayLen( arrErrors )#"
  • step="1">
  •  
  • <h4>
  • #arrErrors[ intError ].Code.XmlText#
  • </h4>
  •  
  • <p>
  • #arrErrors[ intError ].Message.XmlText#
  • </p>
  •  
  • </cfloop>
  •  
  • <cfelse>
  •  
  • <!---
  • The web service request went off without a problem.
  • Now, all we have to do is list out the results.
  • --->
  • <table cellspacing="2" cellpadding="3" border="1">
  •  
  • <!--- Loop over items. --->
  • <cfloop
  • index="intItem"
  • from="1"
  • to="#ArrayLen( arrItems )#"
  • step="1">
  •  
  • <!--- Get a reference to this item. --->
  • <cfset xmlItem = arrItems[ intItem ] />
  •  
  • <tr>
  • <td colspan="2">
  • <strong>
  • #xmlItem.ItemAttributes.Title.XmlText#
  • </strong>
  • </td>
  • </tr>
  • <tr>
  • <td>
  • Author
  • </td>
  • <td>
  • #xmlItem.ItemAttributes.Author.XmlText#
  • </td>
  • </tr>
  • <tr>
  • <td>
  • ASIN
  • </td>
  • <td>
  • <a
  • href="#xmlItem.DetailPageURL.XmlText#"
  • target="_blank"
  • >#xmlItem.ASIN.XmlText#</a>
  • </td>
  • </tr>
  •  
  • </cfloop>
  • </table>
  •  
  • </cfif>
  •  
  •  
  • <cfelse>
  •  
  • <!---
  • Something went wrong with the web service request.
  • Either the server returned a non-200 response code or
  • the generated content was not in XML format. Output
  • the data for debugging.
  • --->
  • <p>
  • <em>There was a problem invoking the web service.</em>
  • </p>
  •  
  • <code>
  •  
  • #HtmlEditFormat(
  • REQUEST.WSResponse.FileContent
  • )#
  •  
  • </code>
  •  
  • </cfif>

This was my first introduction to REST-style web service invocation. I had seen the phrase REST thrown around here and there, but never really understood what it meant. After doing some quick Googling, it looks like REST is basically a no-methodology-methodology???? Basically, it uses standard HTTP to invoke web services. The XML response is just returned as part of the generated file content. Sorry if this is grossly incorrect... like I said, this was my first time trying to even look it up.

So, as you can see, I invoke the web service using ColdFusion's CFHttp tag. Each argument to the web service is passed as a URL CFHttpParameter. You could just as easily put these values as part of the query string in the URL attribute of the CFHttp tag, but this way creates a much nicer structure that will be easier to manipulate programmatically.

The rest of it is just being able to traverse XML documents and perform some simple XPath xml searches. One thing that I do that I would like to touch upon is the removing of all name space attributes from the XML nodes:

  • <cfset strXmlData = REQUEST.WSResponse.FileContent.ReplaceAll(
  • "\s*xmlns=""[^""]*""",
  • ""
  • ) />

I do this because I am not very good at using XPath and ColdFusion's XmlSearch() method. The name space arguments seem to mess up the XPath search "//Item". However, if I go through and use a regular expression replace to strip out all xml node attributes that define a name space, the XPath works quite nicely. So, admittedly, this is a bit of a hack (I am altering the returned data), but it's the only way I know how to get it working. Plus, I couldn't care less about name spaces. I am not even sure yet what they are used for.

That's all there is to it. Quite easy. In fact, I found it was harder to find the web service definition than it was to eventually invoke it. And a final note: Please do not look at any of this as a BEST practice. This was my first time ever invoking a web service, so it's all trial and error.



Reader Comments

Jul 24, 2008 at 10:47 PM // reply »
1 Comments

Thanks, used your code as example to make an amazon cfc to do a couple of REST calls and utilize the amazon webservice. Currently working on http://www.utpabooks.com using amazon as the backbone.


Oct 15, 2009 at 5:03 PM // reply »
2 Comments

Very cool post. I was curious if you had updated this to account for Amazons new authentication policy that went into place in August?


Oct 16, 2009 at 8:12 AM // reply »
10,640 Comments

@Mark,

To be honest, I have not looked at Amazon's web services since this post :)


Dec 8, 2009 at 3:50 PM // reply »
2 Comments

Please whats the difference between REST and SOAP? Why don't you use the normal <cfinvoke webservice=..........>??


Dec 8, 2009 at 5:08 PM // reply »
10,640 Comments

@Leonard,

REST and SOAP are just two different ways to invoke a web service. CFInvoke is no more normal than CFHTTP for web services. It just depends on what kind of technologies you have available. In fact, you can post to a SOAP web service using CFHTTP (which is probably why they have a CFHttpParam of type:xml).

REST uses URL or FORM parametrs. SOAP posts XML packets.

I personally find CFHTTP easier to use and debug.


Jan 11, 2010 at 11:30 AM // reply »
4 Comments

hi, when I dump xml for example from Amazon's API, how do I access the values of the blue struct nodes?


Jan 11, 2010 at 12:53 PM // reply »
2 Comments

@BB,

Hi,

i have actually not used amazon's api before but you need to convert the xml to a coldfusion document object using XmlParse() function and use the XmlSearch() function to get values from each node.

Example: <cfset xmlRequest = XmlParse(strXml.Trim()) />

where strXml is the xml var returned

<cfset arrSearchNodes = XmlSearch(xmlRequest,"//node1/node2") />

where node1 is the root level of the xml object, node2 the next level and so on.

To retrieve values within the last node level

<cfoutput>#arrSearchNodes[1].firstName.XmlText#</cfoutput>

where firstName is the tag name of the xml returned

I also had problems removing soap from soap:nodeName but found a solution on one of Ben's articles http://www.bennadel.com/blog/494-Stripping-XML-Name-Spaces-And-Node-Prefixes-From-ColdFusion-XML-Data-To-Simplify-XPath-.htm

the article should provide u with as much information as possible cos i used it

hope it helps


Jan 11, 2010 at 1:18 PM // reply »
4 Comments

@Leonard Inegbedion,

Hi thanks,

I got it finally with this. It returned "Translator"

tmp.ItemLookupResponse.Items[1].Item.ItemAttributes.Creator[1].XmlAttributes["Role"]


Jan 11, 2010 at 1:48 PM // reply »
10,640 Comments

@BB,

Glad you got it working. You can typically thing of XML documents as structures and arrays. It gets a bit complicated with node names with namespaces and embedded dots (as @Leonard eludes to).


Don
Apr 21, 2010 at 6:28 PM // reply »
57 Comments

Would be cool if you figured out how to do the Amazon authentication without using Java objects. :)


PJ
Jan 19, 2012 at 3:57 AM // reply »
5 Comments

Ben, even Amazon's own "code" area is pathetic for CFML options. Getting all this stuff to work is still, years later!, still just freaking confusing.

If I wanted to do stuff that was really complex and HARD and required many languages, I wouldn't be using CFML. It's the glory of that code's simplicity and built-in features that make me being able to write code at 2am for a single mom's hobby (that is definitely not my job), possible at all.

Someone writing a simple, CF-based way to put something, rename something, copy/move something, from amazon (EC2, or whatever) would be such a hero, and how to plug it in with simple examples, would be SUCH a hero.


PJ
Jan 19, 2012 at 4:10 AM // reply »
5 Comments

WOW. Never mind! I just found this:

http://www.raymondcamden.com/index.cfm/2010/7/15/CF901-Guide-to-Amazon-S3-support-in-ColdFusion-901

I only upgraded to CF9 last week and I've been reading but hadn't yet heard anything about it working with S3! Thank god! (And Ray)


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 10, 2012 at 7:21 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
Update! Instead of $(eval(options.insertAfter)).after(data['insertData']); I now use: var ajaxNode = document.createElement('span'); var parent = $(eval(options.insertAfter))[0].parentNode; ... read »
Feb 10, 2012 at 6:18 PM
jQuery AJAX Strips Script Tags And Inserts Them After Parent-Most Elements
encountered this same, what I consider, jQuery bug last week. I'm building a site in which I load some content via AJAX. This content contains Linkedin share button placeholders which Linkedin API ne ... read »
Feb 10, 2012 at 11:30 AM
Cross-Origin Resource Sharing (CORS) AJAX Requests Between jQuery And Node.js
After you understand the concepts here, this is an awesome cheatsheet for enabling CORS in just about anything http://enable-cors.org/ ... read »
JM
Feb 10, 2012 at 9:10 AM
My Safari Browser SQLite Database Hello World Example
@Amy, Here is a very good tutorial on how to use JOIN: http://www.sqltutorial.org/sqljoin-innerjoin.aspx ... read »
Feb 10, 2012 at 4:42 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
This is great, very useful Ben. I spotted a small typo in the api.cgm listing: <cfthrow type="Unauthroized" /> Cheers Stefan ... read »
Feb 9, 2012 at 10:35 PM
CFDirectory Filtering Uses Pipe Character For Multiple Filters (Thanks Steve Withington)
I was wondering if there would be a filter you could apply so that you got everything but what you included in the filter. As in show me all docs that are not a .pdf. ... read »
Feb 9, 2012 at 10:29 PM
Learning ColdFusion 9: Application-Specific Data Sources
@Ben, No offence, but if people were really wanting advanced features they would be using a platform like ASP.NET MVC. CFML is so structurally compromised as a tag-based scripting language that ... read »
Feb 9, 2012 at 10:03 PM
Subversion - Cleanup Failed To Process The Following Paths
@Leviaguirre, do you still have problems with this? ... read »