Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at cf.Objective() 2011 (Minneapolis, MN) with:

My AJAX / ColdFusion Response Framework (er, um ...Methodology)

By Ben Nadel on
Tags: AJAX, ColdFusion

I hesitate to call this a framework (as that seems to bring the pain from the Community). This is not really a framework, but rather a methodology that I am finding quite nice when developing a ColdFusion response to an AJAX request. I am sure stuff like this is all over the place, but in typical blood-and-guts fashion (any Dorian Yates fans out there???), I like to reinvent the wheel to figure out what works best for me (laugh at me all you want, but I am learning stuff).

For every AJAX request that comes to the application, I create an request struct (to become a Javascript object) that contains three keys:

Success

This is a boolean that defines wether or not the AJAX request was executed successfully. This will be true (be default) if everything goes according to plan. This will be false if the page has an error, required data wasn't passed, or anything else that causes things to not run smoothly.

Message

This is a string that contains an optional message describing the events that have taken place.

Data

This is variable that could be a struct, string, boolean, or anything else that needs to be returned. This structure will depend on the request that was made and any errors that occur during the request (ex. after a ColdFusion error, this value contains the detailed error info - CFCATCH.Detail).

Here is the basic skeleton of the page that provides the AJAX request API and ColdFusion response:

  • <!--- Kill extra output. --->
  • <cfsilent>
  •  
  • <!--- Create the default response object. --->
  • <cfset REQUEST.Response = StructNew() />
  •  
  • <!--- This is the flag for API success. --->
  • <cfset REQUEST.Response.Success = true />
  •  
  • <!---
  • This is a message that may or may not be populated
  • by the request API.
  • --->
  • <cfset REQUEST.Response.Message = "" />
  •  
  • <!---
  • This is the data that is being returned for
  • the API call.
  • --->
  • <cfset REQUEST.Response.Data = "" />
  •  
  •  
  • <!---
  • This is the ColdFusion component that will be used
  • to store any API data validation errors. It is really
  • just an array around which I have wrapped some
  • extra functionality
  • --->
  • <cfset REQUEST.FormErrors = APPLICATION.ServiceFactory.GetFormErrorCollection() />
  •  
  •  
  • <!--- Try to perform the API call. --->
  • <cftry>
  •  
  • <!---
  • Param the action attribute. This is the value
  • that will determine which method of the API is
  • being called.
  • --->
  • <cfparam
  • name="REQUEST.Attributes.action"
  • type="string"
  • />
  •  
  •  
  • <!---
  • Check to see which API action we are performming
  • for this AJAX request. ALL requests will be
  • handled here, either explicitly or by default case.
  • --->
  • <cfswitch expression="#REQUEST.Attributes.action#">
  •  
  •  
  • <!--- .... API CODE GOES HERE .... --->
  •  
  •  
  • </cfswitch>
  •  
  •  
  • <!---
  • Catch any errors that have occurred during the AJAX
  • request processing.
  • --->
  • <cfcatch>
  •  
  • <!---
  • Set the success flag to false. Since this is
  • error we are going to assume that the overall
  • process was not successful.
  • --->
  • <cfset REQUEST.Response.Success = false />
  •  
  • <!---
  • Set the message from the error. Not every error
  • produced by ColdFusion has a really nice message
  • so let's check the message length. If there
  • is no message, try to use the detail error info
  • as the message.
  • --->
  • <cfif Len( CFCATCH.Message )>
  •  
  • <!--- Use the standard message. --->
  • <cfset REQUEST.Response.Message = CFCATCH.Message />
  •  
  • <cfelseif Len( CFCATCH.Detail )>
  •  
  • <!--- Use the detail as the message. --->
  • <cfset REQUEST.Response.Message = CFCATCH.Detail />
  •  
  • <cfelse>
  •  
  • <!---
  • Something went wrong. ColdFusion did not
  • provide a message or a detail about the
  • error that occurred.
  • --->
  • <cfset REQUEST.Response.Message = "Unknown error occurred." />
  •  
  • </cfif>
  •  
  • <!--- Set the data from the error detail. --->
  • <cfset REQUEST.Response.Data = CFCATCH.Detail />
  •  
  • </cfcatch>
  • </cftry>
  •  
  •  
  • <!---
  • ASSERT: At this point we have processed the API. We may
  • have thrown some sort of error in the process, or the API
  • event may have gone perfectly. Either way, we have a fully
  • populated REQUEST.Response object at this point that we
  • can return.
  • --->
  •  
  •  
  • <!---
  • Let's check to see if we have any data validation errors.
  • We are going to handle this centrally so that each API
  • action doesn't have to handle it individually.
  • --->
  • <cfif REQUEST.FormErrors.Size()>
  •  
  • <!---
  • Set the success flag to false. If there are any
  • errors in our form error collection then we will
  • assume that the overall action was not successful.
  • --->
  • <cfset REQUEST.Response.Success = false />
  •  
  • <!--- Set data validation message. --->
  • <cfset REQUEST.Response.Message = "Please check your data." />
  •  
  • <!--- <br>
  • Set the validation errors as data. For this, we are
  • going to return the array of error messages.
  • --->
  • <cfset REQUEST.Response.Data = REQUEST.FormErrors.ToArray() />
  •  
  • </cfif>
  •  
  •  
  • <!---
  • Return the response object. The response should be
  • treated as an inline request.
  • --->
  • <cfheader
  • name="content-disposition"
  • value="inline"
  • />
  •  
  • <!---
  • Set the content to be plain text. Then, convert the
  • AJAX response object to a JSON (Javascript Object
  • Notation) string. In order to return this using
  • the VARIABLE attribute, we must convert this string
  • to a binary data object.
  • --->
  • <cfcontent
  • type="text/plain"
  • variable="#ToBinary( ToBase64( REQUEST.UDFLib.AJAX.CFToJSON( REQUEST.Response ) ) )#"
  • />
  •  
  • </cfsilent>

So far, I am really liking this implementation. I like that the response has a very standard, yet at the same time, a highly flexible data return. I like how easy it is to tell if the request was successful. I also LOVE using the VARIABLE attribute of the CFContent tag to return the response object. Doing it this way allows me to never come out of my CFSilent tag and releases me of the requirement to manually write my data to the response buffer. Very slick! (Try this... I promise you will never want to do it any other way).

I am still figuring out how I want to handle the CFSwitch statement. Right now, I am just including all the action inline to the document (as I am not working on very large scenarios here), but down the road, this is probably something that I will want to move into CFIncludes for each action.

As you can see from the code, I am converting my responses to JSON, which I think, it the best way to handle AJAX. It just gives you so much power and freedom to be flexible, yet at the same time, allows you to work in a highly structured environment. XML is really cool, but for like 99% of my use cases, it really adds no benefit that I can see.

Well anyway, I am a total AJAX NOOBIE!!! So, please take the above with a grain of salt. I have been doing AJAX for about 2 weeks and have only implemented this in my Code Chat Beta 1.0 and another private project. There are many well established AJAX frameworks out there already which are probably better than this. But I know what I like, and I like this :)




Reader Comments

Just a slight suggestion, in your code comments maybe you could implement a way to collapse the comments in your code. A lot of times I find it difficult to just read the code because of all the comments. A collapsible thing for comments would be pretty cool I think.

Reply to this Comment

That could be cool. I am still looking to improve the code display overall. I will definitely take this into account. Thanks.

Reply to this Comment

Don't be too worried about being new to Ajax. Ajax is awesome, but there isn't much to it. It's more about being smart about where you use it. I bet you already have a pretty good hang of it. As you progress, I'm sure you'll build yourself a cool javascript ajax framework to go along with your CF onea and it'll automate all sorts of things, like automate form submissions with built-in client-side validation, for example. With jQuery, Prototype, YUI, or whatever you want to use, it'll be a breeze, you'll love every minute of it. This stuff is so much fun it should be illegal.

Reply to this Comment

I agree, JSON and AJAX go together very well. I too use the Structure to JSON and send a message as well as data, it feels very natural since the dot notation follows from CF to JS.

I do not use the header and content tags. I just have the output tag touching the closing silent tag.

Reply to this Comment

@Kris,

Yeah, I used to write the output after the close CFSilent tag... but in CFMX 7 when the variable attribute came out, there's something about it which just seems so elegant. No real difference.

Reply to this Comment

Ben,

I uses a similar approach which is more CFC based although it doesn't handle errors at this point. I have an AJAX Facade cfc which has an handle to the internal ColdSpring Bean Factory. All methods in this CFC have a signature of access="remote" so the clients can call those methods directly. These method in turn calls the inner API method by using the ColdSpring factory.

Thanks

Reply to this Comment

I agree with the use of cfcontent and cfsilent.

I was hoping that with Scorpio CF would build in a mechanism. If you've tried to write XML and only XML to the response you've had to deal with this.

I would like to see something like

<cfprocessingdirective ignoreoutputbuffer="yes" >

Then see

<cfresponse data="Hello world" >
<cfresponse xmlobject="#anxmlobject#" >

This is close to the cfcontent tag, but could be called over and over instead of building a very large CF variable.

Btw you can use this same approach to stream JPG or CSS files and use it to track when someone reads an email or that sort of thing. Some readers will strip off the the query string...so if you had

<img src="http://blahblah.com/smallimage.cfm?yourid=15" >

smallimage.cfm will get called, but yourid won't be passed.

The other way around this is to have custom 404 handlers. In IIS you can tell a 404 message to be direct to a .cfm. In the script you get the path. You can build a path that looks legit, but ulitmately is just your id encapsulated...like

http://blahblah.com/myfakedirectorywhere404sarebeingdirected/youridis-15/thisisntarealjpg.jpg

Your custom 404 handler will then stream a transpatent image. The end user will not know it is there. Most email programs will not download jpg's...but they usually will download CSS files. So you can track when someone reads your confirmations or other generated emails to see how they picked up.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
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.