In my last post, I looked at the new JSON functions available in ColdFusion 8, and showed how they could be used to consume JSON web services. Today I’ll look at how CF8 lets you expose your applications as JSON web services.
Even with just the CF8 JSON functions, it is easy to create JSON web services - consume arguments from the caller (in the URL or FORM scopes) in JSON format, deserializeJSON() those to CFML types, run your logic on the CFML objects, then serializeJSON() your return type to get the data back to the caller in JSON format. Easy to do, from a CFC or a CFM.
But with our credo of making hard things easy (and easier!) that just wasn’t good enough.
Lost In Translation?
I’ve always liked the notion that the transformation from and to data formats for communication should be independent of application logic. Taking that a step further, I like to think that application designers should not need to worry about data formats (well, not too much, anyway!) - the client of a web service should be able to decide the data format in which it wishes to communicate, depending on its capabilities. Perhaps one client likes to talk SOAP web services, another prefers XML, and, perhaps, is willing to play with WDDX, and yet another chooses to chatter away in JSON. Once an application designer decides that a particular bit of logic should be exposed as a web service, why should it matter what format the data is exposed as?
RESTful Web Services
Before I get into what we’ve done with CF8 in this space, a little background first - did you know you could invoke CFC functions over HTTP? You should - to my mind, it’s one of the coolest things about CF, which I’ve written about before. Here’s how it works:
Simple - form a URL to the location of your CFC, and pass it the function to invoke in the method request parameter (URL or FORM scope). Other request parameters are provided to the CFC as arguments. Of course, you have to be sure that the CFC function you’re invoking is marked access=remote, or CF will not allow you to call it remotely, as a web service. By default, the return format is WDDX - CF will automatically serialize the return value from your CFC to WDDX and return that to the caller. Arguments can be provided to the CFC in complex nested CFML types using the special request parameter argumentCollection, which can take a WDDX-formatted string, which must represent a CFML struct, and is deserialized to form the arguments scope for the CFC function to be invoked.
In the currency of the dynamic web, CF makes it simple to expose CFCs as REST web services. If you are planning to design web services into you applications, and don’t know what REST is, you should - REST, or REpresentational State Transfer, outlines a way of thinking about web services architecture using the mechanisms built into HTTP, rather than reinventing the wheel a la SOAP web services. For more see Roy Fielding’s Ph.D. dissertation (Roy Fielding originated the term, relevant chapter here), and for an easier, more fun, introduction, see Duncan Cragg’s REST dialogues. Personally, I’m a huge fan of REST web services - while SOAP has it’s place, I think it really is needlessly bloated and overly complex for the vast majority of use cases it is applied to.
Getting back on topic, CF8 introduces the new returnFormat attribute for HTTP invocations of CFC functions, which allows a caller to specify the format of data they’d like to get back. returnFormat supports the following options:
- wddx - the default, return data as WDDX
- json - serialize the return value from the function to JSON and return it
- plain - do nothing with the return value from the function, return it as-is. Note that the plain return format works only for simple types or XML documents. If you try to return a complex type (struct, array, query) with returnFormat=plain, CF will throw an error, since complex types cannot be returned as-is; they must be serialized to a string representation (WDDX or JSON) to transmit over HTTP. Also note that if you’re returning a XML document, you should remember to set the HTTP response content type appropriately - text/xml for most cases, or as appropriate for the particular XML dialect that you’re returning.
Taking my example above, a user who wants a JSON response would modify the URL like so:
To flesh things out, CF8 can also accept JSON in the argumentCollection request parameter. For instance:
For completeness, returnFormat is also available as a CFFUNCTION attribute - if you would rather your remote functions had a default other than WDDX you can set it on your functions, and save callers from have to specify it in their requests.
So what’s it good for?
For starters, you can now invoke CFCs directly from AJAX applications - there any any number of AJAX frameworks, including Spry, jQuery and Prototype, which provide support for consuming data from JSON web services. Oh, and lest we forget, CF8 offers up the cfajaxproxy tag to aid in this effort - but more on that later.
Or, if you buy into the “web as a platform” meme (I know I do!), you could choose to expose public JSON web services from your applications, as do del.icio.us, and many others. I particularly like the fact that services such as del.icio.us, built on the backs of their users’ tag gardening activity, open up the data for others to access and do with as they will. Then there’s the it-makes-commercial-sense approach, with companies such as Amazon leading the way - Amazon Web Services provide an astonishing array of functionality (XML rather than JSON, but that’s besides the point), providing pay-as-you-go services, such as S3, and the eCommerce Service, which allows developers to build their own storefronts on top of Amazon’s catalogues.
Think you could be the next Amazon? CF8 makes the technology end of things easier - by a lot - and it certainly doesn’t hurt that CF8 ups the ante on performance to let you operate at Amazon-scale that much more easily.