I’m building a series of APIs that bypass Model-Glue for use by Javascript applications using jQuery and EXT. I wired the whole deal up using the super-cool Remote Proxy Beans. When it came time to request JSON back from the CFC using remote.cfc?returnFormat=json, there were four problems:
- Extraneous whitespace being returned
- Content-type was understood by Firefox, meaning it was potentially insecure
- Keys were all being forced upper-case which is plain ugly.
- Integers are being represented as floats which is a feature, not a bug (see comments at bottom)
To solve these problems, I used Coldspring’s aspect oriented programming (AOP) and wrote a custom around advice that I applied to my remote proxy to better control the JSON formatting for the client.
jsonAroundAdvice.cfc
The following Around Advice eliminates the built-in ?returnFormat=json support in a remote CFC in favor of more explicit conversion:
<cfcomponent output="false" name="jsonAroundAdvice" extends="coldspring.aop.MethodInterceptor">
<cffunction name="invokeMethod" access="public" returntype="any">
<cfargument name="mi" type="coldspring.aop.MethodInvocation" required="true" />
<cfset var record = structNew() />
<cftry>
<!--- just pass results straight through --->
<cfset record = arguments.mi.proceed() />
<cfcatch type="any">
<cfset record["data"] = "Serialization failed" />
<cfset record["result"] = false />
</cfcatch>
</cftry>
<!--- control output, forcing json --->
<cfset URL.returnFormat = "plain" />
<cfheader name="Access-Control" value="allow <*>" />
<cfcontent reset="true" type="application/json"><cfreturn serializeJson(record) /><cfabort />
</cffunction>
</cfcomponent>
Basically this code takes any results from the actual method that was called and creates a Javascript object with two keys, data and result, that are serialized and returned with a proper header. We set the URL.returnFormat = “plain” so we can override any parameter passed by the user to the method. Note that we can use the CF8 SerializeJSON method or we could use Epiphantastic’s CFJSON.cfc. Only the latter, in my experience, will solve the decimal formatting of integers (#4 in my list above).
Coldspring Configuration
To complete the example, this is the Coldspring configuration required to set up my remote proxy:
<bean id="jsonAroundAdvice" class="model.aop.jsonAroundAdvice" />
<bean id="jsonAdvisor" class="coldspring.aop.support.NamedMethodPointcutAdvisor">
<property name="advice"><ref bean="jsonAroundAdvice" /></property>
<property name="mappedNames"><value>*</value></property>
</bean>
<bean id="remoteFormService" class="coldspring.aop.framework.RemoteFactoryBean" lazy-init="false">
<property name="target"><ref bean="formService" /></property>
<property name="serviceName"><value>remoteFormService</value></property>
<property name="relativePath"><value>/myMapping/myDirectory</value></property>
<property name="beanFactoryName"><value>cs</value></property>
<property name="interceptorNames">
<list>
<!-- run in reverse order; or think of first being "around" second being "around" third -->
<value>jsonAdvisor</value>
<value>authorizationAdvisor</value>
<value>authenticationAdvisor</value>
</list>
</property>
<property name="remoteMethodNames"><value>*</value></property>
</bean>
Note that I have more than one around advice here. Coldspring evaluates them from the bottom up so jsonAdvisor is “around” authorizationAdvisor which is “around” authenticationAdvisor. I have them stacked like this so I can reuse authorization/authentication in other remote proxies as well.
Formatting
One thing I didn’t like about CF8’s SerializeJSON() is that it changes your data slightly (as compared to JSON.cfc). Take for example the following structure:
str.camelHumpName = "Some Value";
str.NumericValue = 52;
SerializeJSON translates that to:
{"CAMELHUMPNAME": "Some Value", "NUMERICVALUE": 52.0}
That’s kind of goofy although the case issue is consistent with how the structure functions treat your key names. Because CF is not case-sensitive, we can still refer to them with our normal mixed case. Not so in case-sensitive Javascript-land. You can get around the upper case keys by using a slightly different structure notation:
// maintains mixed-case key in json serialization
str["camelHumpName"] = "Some Value";
// when quoted, value remains an integer (52) rather than a float (52.0)
str["NumericValue"] = "52";
This structure produces the following JSON:
{"camelHumpName": "Some Value", "NumericValue": 52.0}
Mashup Controls
You might have noticed the line above that read:
<cfheader name="Access-Control" value="allow <*>" />
This is a new header used to better control XSS attacks that is understood by Firefox 3’s XHR implementation. You can control which web sites can and cannot request data using this header.
There we have it. A couple of tricks and tips for building AJAX applications using ColdSpring’s RemoteProxyBean and AOP!
Martijn van der Woud said:
on November 22, 2008 at 3:53 pm
Great example, thanks!