[uf-rest] RESTifying RAILs

Dan Kubb dan.kubb at autopilotmarketing.com
Thu Nov 3 23:50:32 PST 2005


Hi Ernie,

> I promised him I'd write up my "wishlist" of things I wish Rails  
> did "out of the box" to make it easier to create REST-style web  
> services/apps.  I suspect these are all _possible_ in Rails (even  
> easy!), but in my dream world they are 'automatic' so that everyone  
> naturally does "the right thing."   To be sure, some of these may  
> be impractical or bad ideas, but that's why I'm posting them for  
> feedback from the list.

I've been thinking about how to RESTify Rails for the
last week, and have been RESTifying other MVC systems
for several months, so I've got a few points to add.

> That is, make the default URL style:
>     http://host/base/table/id/style
> as in:
>     http://localhost:3000/mydemo/person/113

In my routes.rb file I set up this style of URI like so:

   ActionController::Routing::Routes.draw do |map|
     map.connect ':controller/:id/:action'
   end

People should be encouraged to use nouns for
the components of a URI, especially for the
whatever you use in place of :action.

As I understand it, :action is what Rails uses to
dispatch to the correct method within a controller.
Having it named "action" sort of encourages people to
use verbs for this part of the URI.  To discourage
this practice, it would be nice to be able to alias
it to another word, like :aspect so I could have
routes like :controller/:id/:aspect and have Rails
still perform the dispatch as before.

> == POST vs. GET ==
>
> Right now, as far as I can tell, Rails doesn't differentiate  
> results from a POST vs. results from a GET.  Those are semantically  
> different in REST, so I'd like [to know] a way to handle that cleanly.

The key with this I think is to dispatch to a different
method to handle GET requests vs. POST and the other
HTTP methods.  It makes it easier to concentrate on
returning the most appropriate status, headers and body
in response to the specific type of request.

I've attached a simple Controller example I've been playing
around with for the last couple of days that shows one
approach to dispatching based on the HTTP method and action.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: resource_controller.rb
Type: text/x-ruby-script
Size: 2033 bytes
Desc: not available
Url : http://microformats.org/discuss/mail/microformats-rest/attachments/20051103/80c451f4/resource_controller.bin
-------------- next part --------------

With this requests will map to the controller & action like so:

   GET     /person/            -> Person.get_index
   GET     /person/123         -> Person.get_index
   GET     /person/123/friend  -> Person.get_friend
   HEAD    /person/            -> Person.get_index   # HEAD handled  
by get method
   POST    /person/            -> Person.post_index
   PUT     /person/123         -> Person.put_index
   POST    /person/123/friend  -> Person.post_friend
   DELETE  /person/123         -> Person.delete_index
   DELETE  /person/123/friend  -> Person.delete_friend

This is being done in a before_filter, but ideally I'd
prefer to have the dispatched-to method called /in place of/
the normal action method (which would be *_index and *_friends
in the above examples).  I'm sure this can be done, but
I've only begun learning ruby (and rails) in the last
week, so I'm not sure the best way to do this.

One other thing this Controller is doing is that it
allows tunneling of PUT, DELETE (and any other method) over
top of a POST.  Before dispatch I look for a specific
parameter that says what method I really want to call
and dispatch to it.

While this isn't RESTful, its a trade-off that will allow
the web apps to work with all browsers until support
for more than GET and POST is common.  Hey I've got to
tunnel update and delete operations over POST somehow
anyway, so I figure I'll use a convention to do it.

---

Here's a couple of other things to add to the list:

OPTIONS Handling
================

The controller should be able to handle OPTIONS requests
and know what specific methods it can handle for a given
URI.

Output Content Negotiation
==========================

It would be nice to be able to have the view look at
the Accept headers and choose the best template to
output.

For example, imagine you had the following files:

   index.rhtml
   index.rxml
   index.rtxt

If the browser sent the Accept as text/html then the
index.rhtml would be used.  For text/plain the
index.rtxt would be used, and so on.

If there was a way for a view handler to "register"
itself and say what mime types it knows how to handle,
then the optimal handler could be chosen at runtime.
The handlers could do anything they want in order
to output the response body, including using libraries
like GD.

Input Content Negotiation
=========================

When a request has a body, it needs to be parsed and
stored into the request parameters.  Rails handles
normal web forms and multi-part form input.. but
it would be nice to be able to write custom handlers
that knows how to parse requests for specific
mime-types and set the appropriate request parameters
inside rails.

This would allow you to POST a web form or a yaml
string and have the values funneled into the same
parameters -- your controller wouldn't care where
the data is come from.  I realize yaml is supported
by rails natively, but it should be possible to
register handlers for any other mime types.

Perl's Catalyst MVC is starting to do this with
their HTTP::Body module.. they made it so you can
write custom modules to parse any mime-types and
set the request parameters.

HEAD responses
==============

Responses to HEAD requests should be identical to
GET requests, minus the body.  Rails should omit sending
the body for HEAD requests, but still send all the appropriate
headers.  Its especially important that the Content-Length be
the same for HEAD and GET responses.

Conditional GET requests
========================

When writing mod_perl handlers I was able to do something
equivalent to:

   def get_index
     # do some work to set up @resource

     set_etag @resource.digest_hash
     set_last_modified @resource.updated_at

     return not_modified if conditional_get?
     render
   end

This resulted in a snappier response from some apps since it
didn't require a full transfer if it was not modified since
my last request.

AJAX apps to use HTTP more fully
================================

Most Rails AJAX apps use only GET and POST, but
they should be encouraged to use the full set of
HTTP verbs in examples.

Encouragement of Conditional GET in AJAX apps
would be a bonus as well.

--

Thanks,

Dan Kubb





More information about the microformats-rest mailing list