Prototype, JSON and Catalyst
Posted in json, catalyst, perl, ajax, prototype Thu, 01 Jun 2006 00:42:00 GMT
I use the prototype.js library (aka prototype) for AJAX and recently moved to using JSON to return multiple elements instead of a single one. I had to rearchitect my client request code and my server code to handle JSON but it's worth it to minimize the number of HTTP requests. I use Catalyst and JSON::Syck which make generating the JSON response on the server-side very easy.
Prototype.js is interesting in that it auto-evals a JSON object that's placed in the X-JSON header instead of the response body like other libaries such as Dojo Toolkit. This allows you to send HTML in the response body but I'm not convinced this is a good implementation since it supports only one HTML fragment in the response body. I think it would be better to simply put the JSON object in the response body and have all the HTML fragments in the JSON object. I just seems cleaner to put all the HTML fragments in JSON instead of fragment 1 in the response body and fragments 2-n in JSON. Although I have the X-JSON header working, I may just move to the more standard JSON in response body approach and eval the JSON string myself. To read the auto-evaled JSON response, the following example expects a JSON associative array and displays the value of the myItem key (more code is available in the wiki link below):
onComplete: function( request, json ) { alert( json.myItem ); }
On the server-side, I use the Catalyst framework to set the header and JSON::Syck to transform Perl data structures to JSON. JSON::Syck is a wrapper around the C libsyck library and very fast. There is a view, Catalyst::View::JSON, however it puts the JSON string in the response body because that is what Dojo and other libraries expect. Since AJAX libraries don't idnetify themselves in anyway to the server (e.g. user agent), the Catalyst View would probably need a patch to with a configuration switch to populate X-JSON instead of the response body. For now, I'm using JSON::Syck directly. The following are the minimal calls to use. If you have UTF-8 output, see C::V::JSON for UTF-8 encoding.
$c->res->header( 'X-JSON' => JSON::Syck::Dump( \%args ); );
I've put together some example code that I thought would be better placed on the wiki. It can be found at: Prototype.js, JSON and Catalyst.
UPDATE: After running into the IE max header length issue, I've decided to put the JSON object in the response body and manually eval request.responseText on the client.
Thanks for posting this. It will save me a lot of time—I would have hit the same issues.
I’ve found another workaround: you can put a code snippet in the response header to eval the response body, acting like a redirect:
$c->res->header( ‘X-JSON’ => ‘eval(“(“this.transport.responseText“)”);’ );
And then include the JSON text in the response body like you’re already doing.
This will return the JSON object as prototype originally intended and you don’t have to eval the responseText yourself, making the change transparent to your other js functions.
Cool solution Jason. Just some typos in your post:
$c->res->header( ‘X-JSON’ => ‘eval(“(“+this.transport.responseText+“)”)’ );
I just found your blog from Planet Catalyst. Nice work!
Prototype (at least 1.5.0_rc1) does alert the server about itself by setting the following headers:
‘X-Requested-With’, ‘XMLHttpRequest’, ‘X-Prototype-Version’, Prototype.Version
Thanks Drew. I actually mention Prototype’s request header identifiers in a subsequent post about trying to get the AJAX libraries to use a standarized identifier but I haven’t loked at getting that added to C:V:JSON. With a standard request header, server-side code like C:V:JSON won’t have to have code specific to each library. Dojo and MochiKit were considering going in that direction I’m not sure what the status of that is.
I think the best approach for C:V:JSON is probably to look for X-Prototype-Version and then use Jason’s eval in the X-JSON header solution due to IE header length issues.
I’ve supplied jason’s solution as a patch for C:V:JSON which has been accepted for v0.12. The change is just equivalent of the following (space adjusted to fit within margins here) with some additional text: