Skip to content
This repository has been archived by the owner on Dec 30, 2024. It is now read-only.

RestfulX XML over HTTP Provider API

dima edited this page Aug 14, 2010 · 1 revision

RESTful CRUD

At the core of the RestfulX framework is an assumption that REST and by implication RESTful CRUD is a good way to develop networked applications. As a result XML-over-HTTP providers are assumed to expose 5 standard REST methods. Let’s use an example to illustrate what this means. If we have a model (a.k.a resource) called project, the following provider methods must be exposed:

method HTTP method URL
index GET /projects.fxml
show GET /projects/1.fxml
create POST /projects.fxml
update PUT /projects/1.fxml
destroy DELETE /projects/1.fxml
  1. Of course we know that most browsers don’t actually support HTTP PUT and DELETE methods. As a result, update and destroy are actually implemented using HTTP POST with a special _method parameter sent along to indicate if PUT or DELETE should be used. This is a pretty big hack, but let’s call this an implementation detail since it doesn’t really affect the way we develop applications.
  2. In case you are wondering what’s up with that .fxml extension, why not .xml? Well, there’s a good reason for that. Flex imposes certain conditions on how the content is delivered and what kind of XML formatting is used1. Instead of hijacking the default .xml content type, we’ve decided to use a special Flex specific content-type (.fxml stands for Flex XML). This allows us to support standard XML API consumers that respect HTTP response headers and can consume just about any XML using .xml content type and Adobe Flex clients using the same controller.
  3. The other 2 standard Rails RESTful Controller methods (new and edit) are optional. They are typically used to render a form that allows you to create a new (or edit an existing) resource. Unlike normal HTML/JS websites, applications written for the Adobe Flash platform are stateful so the concept of requesting a form rendered by the server is meaningless. (new and edit methods are never used by the RestfulX framework.)

XML syntax requirements

1 Staying with our project resource example, we can now focus on the details of XML-over-HTTP messaging.

1. If our RESTful projects controller receives index method invocation XML of the following form must be returned:


<?xml version="1.0" encoding="UTF-8"?>
<projects type="array">
  <project>
    <completed type="boolean">false</completed>
    <created_at type="datetime">2008/07/09 20:08:28</created_at>
    <end_date type="date">2008/07/09</end_date>
    <id type="integer">490909803</id>
    <name>Project4NameString</name>
    <notes>Project4NotesText</notes>
    <start_date type="date">2008/07/09</start_date>
    <updated_at type="datetime">2008/07/09 20:08:28</updated_at>
    <user_id type="integer">276171944</user_id>
  </project>
  <project>
    <completed type="boolean">false</completed>
    <created_at type="datetime">2008/07/09 20:08:28</created_at>
    <end_date type="date">2008/07/09</end_date>
    <id type="integer">1043718716</id>
    <name>Project2NameString</name>
    <notes>Project2NotesText</notes>
    <start_date type="date">2008/07/09</start_date>
    <updated_at type="datetime">2008/07/09 20:08:28</updated_at>
    <user_id type="integer">981972180</user_id>
  </project>
  <project>
    <completed type="boolean">false</completed>
    <created_at type="datetime">2008/07/09 20:08:28</created_at>
    <end_date type="date">2008/07/09</end_date>
    <id type="integer">1060557696</id>
    <name>Project1NameString</name>
    <notes>Project1NotesText</notes>
    <start_date type="date">2008/07/09</start_date>
    <updated_at type="datetime">2008/07/09 20:08:28</updated_at>
    <user_id type="integer">938764944</user_id>
  </project>
  <project>
    <completed type="boolean">false</completed>
    <created_at type="datetime">2008/07/09 20:08:28</created_at>
    <end_date type="date">2008/07/09</end_date>
    <id type="integer">1063252898</id>
    <name>Project3NameString</name>
    <notes>Project3NotesText</notes>
    <start_date type="date">2008/07/09</start_date>
    <updated_at type="datetime">2008/07/09 20:08:28</updated_at>
    <user_id type="integer">887745387</user_id>
  </project>
</projects>

If you are familiar with Ruby On Rails you’ll notice that this is pretty much standard XML returned by calling .to_xml on any ActiveRecord model (or in this case calling .to_xml on an array of ActiveRecord models). There is one caveat however:

  • Flex E4X implementation is actively unhappy with XML element names that contain dashes. As a result, all XML returned must use underscores instead of dashes by convention.

Speaking of conventions… There are a few more things you’ll want to pay attention to when working with XML providers for the RestfulX framework.

  • If the value of a particular XML element represents a string you can omit type attribute on that particular element, otherwise type attribute is required. The following types are supported:
    1. integer
    2. boolean
    3. date
    4. datetime
    5. array
  • If you are referring to another model/resource by id the following syntax must be used:

    <user_id type="integer">887745387</user_id>

It is essentially equal to model_name followed by _id.

This should all sounds familiar if you’ve been working with Rails for a while.

restfulx gem provides a .to_fxml implementation that follows all of the rules required by the RestfulX framework.

2. The result of invoking show is pretty similar, except that a single element is expected:


<?xml version="1.0" encoding="UTF-8"?>
<project>
  <completed type="boolean">false</completed>
  <created_at type="datetime">2008/07/09 20:08:28</created_at>
  <end_date type="date">2008/07/09</end_date>
  <id type="integer">1060557696</id>
  <name>Project1NameString</name>
  <notes>Project1NotesText</notes>
  <start_date type="date">2008/07/09</start_date>
  <updated_at type="datetime">2008/07/09 20:08:28</updated_at>
  <user_id type="integer">938764944</user_id>
</project>

3. Unlike standard Rails controller implementation create, update and destroy methods must also return full XML representation of the model/resource that has been created, updated or destoyed. For example, if I’ve invoked update on the projects controller and changed project name to FooBar, the following XML should be returned:


<?xml version="1.0" encoding="UTF-8"?>
<project>
  <completed type="boolean">false</completed>
  <created_at type="datetime">2008/07/09 20:08:28</created_at>
  <end_date type="date">2008/07/09</end_date>
  <id type="integer">1060557696</id>
  <name>FooBar</name>
  <notes>Project1NotesText</notes>
  <start_date type="date">2008/07/09</start_date>
  <updated_at type="datetime">2008/07/09 20:08:28</updated_at>
  <user_id type="integer">938764944</user_id>
</project>

Handling Provider Errors

Another interesting feature of Flex’s own HTTPService is that using HTTP responses with status code set to anything other than 200 OK is pretty much useless. When the error is handled by the Flash player client programmer working in ActionScript will not be able to inspect the status code or the body of the error message (if any). All we’ll know at runtime is that something is wrong but not what exactly is wrong.

As a result all errors returned by the remote RESTful controllers must be accompanied by HTTP status code 200 OK.

Use can use AS3XMLHTTPServiceProvider if you don’t like this.

We use top level <errors> element to denote that the response is an error and must be handled in a special way. Here’s an example:


<?xml version="1.0" encoding="UTF-8"?>
<errors>
  <error field="name" message="Some stuff is wrong"/>
</errors>

And here’s the bit of Ruby that allows us to serialize ActiveRecord errors to XML in that way:


  # Add more extensive reporting on errors including field name along with a message
  # when errors are serialized to XML
  class Errors
    def to_fxml(options={})
      options[:root] ||= "errors"
      options[:indent] ||= 2
      options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
      options[:builder].instruct! unless options.delete(:skip_instruct)
      options[:builder].errors do |e|
        # The @errors instance variable is a Hash inside the Errors class
        @errors.each_key do |attr|
          @errors[attr].each do |msg|
            next if msg.nil?
            if attr == "base"
              options[:builder].error("message" => msg)
            else
              fullmsg = @base.class.human_attribute_name(attr) + ' ' + msg
              options[:builder].error("field" => attr, "message" => fullmsg)
            end
          end
        end
      end
    end  
  end

Example Controller

Finally here’s an example RESTful Ruby On Rails controller that exposes the API required by the RestfulX Framework. Of course this code will be very different if you are using (or perhaps developing) and XML-over-HTTP provider for the RestfulX Framework in say Merb or Django or Erlyweb or some Java framework. But it should give you an idea of what’s required.


class ProjectsController < ApplicationController
  def index
    @projects = Project.find(:all)

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @projects }
      format.fxml  { render :fxml => @projects }
    end
  end

  def show
    @project = Project.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @project }
      format.fxml  { render :fxml => @project }
    end
  end

  def new
    @project = Project.new

    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @project }
      format.fxml  { render :fxml => @project }
    end
  end

  def edit
    @project = Project.find(params[:id])
  end

  def create
    @project = Project.new(params[:project])

    respond_to do |format|
      if @project.save
        flash[:notice] = 'Project was successfully created.'
        format.html { redirect_to(@project) }
        format.xml  { render :xml => @project, :status => :created, :location => @project }
        format.fxml  { render :fxml => @project }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @project.errors, :status => :unprocessable_entity }
        format.fxml  { render :fxml => @project.errors }
      end
    end
  end

  def update
    @project = Project.find(params[:id])

    respond_to do |format|
      if @project.update_attributes(params[:project])
        flash[:notice] = 'Project was successfully updated.'
        format.html { redirect_to(@project) }
        format.xml  { head :ok }
        format.fxml  { render :fxml => @project }
      else
        format.html { render :action => "edit" }
        format.xml  { render :xml => @project.errors, :status => :unprocessable_entity }
        format.fxml  { render :fxml => @project.errors }
      end
    end
  end

  def destroy
    @project = Project.find(params[:id])
    @project.destroy

    respond_to do |format|
      format.html { redirect_to(projects_url) }
      format.xml  { head :ok }
      format.fxml  { render :fxml => @project }
    end
  end
end

This controller exposes .html, .xml and .fxml APIs. .xml API can be used by normal ActiveResource-like consumers, while .fxml API is used by Flex clients and .html API can be customized to say iPhone.

edit and new methods above are only used by HTML clients.

Conclusion

If you follow the above conventions you should be able to port just about anything that can message XML and follows basic RESTful conventions to use Flex UI based on the RestfulX framework.