Sessions and JSON with Ring, Compojure and Flash AS3

The code here is actually several months old, it was only today that I stumbled on it and realized it would be a nice writeup.

This is a continuation of the sandbar and the flash http articles.

Note that as3httpclientlib has been moved to github.

First the Clojure code:

(ns flash.test
   (:use compojure.core
      ring.adapter.jetty
      sandbar.stateful-session
      [clojure.contrib.json read write]))
   
(defroutes main-routes
   (POST "/set-countries*" [countries] 
      (session-put! :countries countries))
   (POST "/get-countries*" []
      (read-string (json-str (session-get :countries [{:name "no countries available"}]))))
   (ANY "*" []
      {:status 404, :body "<h1>Page not found</h1>"}))
 
(def app (-> main-routes
             wrap-stateful-session))
 
(run-jetty (var app) {:join? false :port 8081})

Not much to add here, pretty much the same setup as in the earlier sandbar tutorial, the only addition is the JSON stuff. Note the use of read-string in order to get rid of superfluous double quotes.

And the ActionScript 3:

import flash.net.*;
import com.adobe.net.*;
import org.httpclient.*;
import org.httpclient.http.*;
import org.httpclient.events.*;
import com.adobe.serialization.json.*;

var serv_url = "http://192.168.100.110:8081/";
var sess_id = "";

function httpPost(server_func:String, callBack:Function, post_vars:Object = undefined){
	var client:HttpClient = new HttpClient();

	client.listener.onData = function(event:HttpDataEvent):void {
		var stringData:String = event.readUTFBytes();
		trace(stringData);
		callBack(stringData);
	};
	
	client.listener.onComplete = function(event:HttpResponseEvent):void {
	  trace(event.response);
	};
	
	client.listener.onStatus = function(event:HttpStatusEvent):void {
	  var response:HttpResponse = event.response;
	  if(sess_id == ''){
		  var temp = response.header.getValue("Set-Cookie").split('=');
		  sess_id = temp[1].replace(/"/, "").replace(/"/, "");
	  }
	  
	  trace('Set-Cookie: '+sess_id);
	  
	};
	
	var temp:Array = [];
	
	for (var key:String in post_vars){
		temp.push({name: key, value: post_vars[key]});
	}

	var request:HttpRequest = new Post(temp);
	request.addHeader("Cookie", "ring-session="+sess_id);
	
	client.request(new URI(serv_url+server_func), request);
}

setCountries_btn.addEventListener(MouseEvent.CLICK, function(){ httpPost(
	"set-countries", 
	function(){}, 
	{countries: JSON.encode([{name: "Sweden"}, {name: "Norway"}])}
)});
															  
getCountries_btn.addEventListener(MouseEvent.CLICK, function(){ httpPost("get-countries", function(res){
		var obj:Array = JSON.decode(res as String) as Array;
		var t:Object;
		for each (t in obj){
			trace(t.name);
		}
	}); 
});

Here we simply have two buttons on the stage, the left one will submit a JSON string of country objects to the Clojure code above, the right one will fetch said countries from the session as a JSON string and convert it to the same array of objects we submitted when we pressed the left button.

The big thing here is the session handling; it’s not working out of the box, something that can be a pain in the ass when working with Flash. By examining the headers being sent from Ring/Compojure we’re able to extract the session id from the Set-Cookie parameter (line 27).

The id we now have extracted is being passed along with every call by way of the addHeader function of the request object (in this case of type Post).


Related Posts

Tags: , , , , , ,