answer on rest xml and flash as3

Ask and ye shall receive.

Earlier this week I posted a question about REST xml and flash as3. Tony Hillerson of effectiveUI graciously provided an answer. To summarize, I wanted to know best practice on implementing Put and Delete http methods given the limitation that the Actionscript Flash UrlRequest object only supports the Get and Post http methods. It turns out that this problem is not unique to Flash because browsers face the same limitation. To get around this, Ruby on Rails has implemented a standardized hack. To make a Put or a Delete call, simply make a Post with the additional parameter _method=DELETE or _method=POST. The workaround isn’t too obscure and I’m not the first person to ask this question, but I’m new to Rails so consider this my education. :)

I also have to admit that that after buying into the REST vision, I’m a little disappointed that the implementation turns out to just be a hacked action parameter. But no matter, here is my code for a REST service in case it helps anyone with the same questions.

My framework is a custom built collection of design patterns that would be familiar to anyone who has worked with ARP or Cairngorm. I make my REST calls using commands that implement an IResponderCommand interface with execute, onResult, and onFault methods.

Here is the complete Actionscript RestService class as a text file.

The key part of the class is inside the constructor:

/**
 * Constructor. Initialize logger. Initialize UrlLoader the root url of the rest service and the method.
 * Populate the urlVariables with the appropriate _method to support PUT and DELETE operations.
 *
 * @param responderCommand Command that should receive responses from the service
 * @param restMethod Method to use in making request GET, POST, PUT, DELETE
 * @param restPath Begining of address of url to use when making a request.
 * @param restAction Optional end of address of url to use when making a request.
 */
public function RestService(responderCommand:IResponderCommand, restMethod:String, restPath:String, restAction:String = ""):void {
    logger = Logger.buildLogger(this);
    logger.debug("Constructor");

    this._responderCommand = responderCommand;
    this._restMethod = restMethod;
    this._restPath = restPath;
    this._restAction = restAction;

    _urlLoader = new URLLoader();
    _urlLoader.addEventListener(Event.COMPLETE, _responderCommand.onResult);
    _urlLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHttpStatus);
    _urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onIoError);
    _urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);

    _urlVariables = new URLVariables();

    _urlRequest = new URLRequest();
    switch (_restMethod) {
        case GET:
            _urlRequest.method = URLRequestMethod.GET;
            break;
        case POST:
            _urlRequest.method = URLRequestMethod.POST;
            break;
        case PUT:
            _urlVariables._method = RestService.PUT;
            _urlRequest.method = URLRequestMethod.POST;
            break;
        case DELETE:
            _urlVariables._method = RestService.DELETE;
            _urlRequest.method = URLRequestMethod.POST;
            break;
    }

    //assign data to request
    _urlRequest.data = _urlVariables;
}

This approach hides the hack from any command that will be consuming the service. The service is invoked with the method Get, Post, Put, or Delete. Parameters are added using the service’s addParameter method:

public function addParameter(name:String, value:Object) {
    _urlVariables[name] = value.toString();
}

And the call is made using the service’s load method:

public function load(input:Object):void {
    var requestAddress = _restPath + "/" + input.toString() + ".xml" + _restAction;
    _urlRequest.url = requestAddress;
    _urlLoader.load(_urlRequest);
}

Note that the load method takes in an input object as a parameter to build the url string. Best practice is for this input parameter to be an id. This follows REST conventions with an example url for a Ruby call: /service/5.xml;action. The action matches whatever has been mapped in the Ruby /config/routes.rb file.

The end product is an easy to consume service. Here is an example of an execute method of a command.

public function execute(eventObj:CommandEvent):void {

    //retrieve id from the command event
    var id:String = eventObj.getValueString();

    //create service with method type, path, and action
    service = new RestService(this, RestService.GET, Settings.REST_PATH, Settings.REST_GET_ACTION);

    //load the service
    service.load(id);
}

And finally, the onResult method that uses the service data:

public function onResult(resultObj:Object):void {

    var data:XML = service.data;

    . . . //process the xml here
}

For tips on configuring the Rails side of the REST service, I will defer to other resources on the web. It’s pretty straightforward, but I’m not enough of a Rails expert yet to offer anything particularly insightful on that part of the equation.

Any Rails experts out there, adding useful resource links to the comments would be appreciated.

2 comments ↓

#1 Thijs van der Vossen on 07.16.07 at 4:44 am

You can indeed use the _method hack to work around the fact that you can’t do a real PUT and DELETE request from Flash.

The real problem is that you can’t get the body of a non-2xx response, you can’t read most of the response headers, and you can’t get the response code in most cases. This makes it impossible to use Flex with proper RESTful (read ‘ActiveResource’) Rails applications.

So yes, you can build a XML HTTP interface between Flash and Rails, it’s just not really REST.

The only way to really build a REST interface without hacking around the limitations in your Rails application is Alex MacCaw’s socket urlloader which is part of ActiveResource for Actionscript at http://code.google.com/p/as3-active-resource/

#2 Post test « kember.com.au on 05.09.09 at 6:41 am

[…] pages. I wanted to display navigation on the search/archive pages, related subject info on the post pages, irrelevant itunes info on my life story, and keep a generic sidebar on the rest of the […]