[LRUG] REST and Associations
Eleanor
eleanor at goth-chic.org
Mon Feb 26 21:10:21 PST 2007
Hi Roland,
Apologies in advance but this is going to be a long reply, and
probably not that helpful >8p
On 22 Feb 2007, at 22:02, Roland Swingler wrote:
> 1. From what I've read, I thought all urls were meant to be nouns in
> restland - how does having /posts/1;edit give you any advantage over
> /posts/edit/1 or /posts/1/edit ? A semicolon does not a noun make. For
> me this highlights a possible weakness in thinking of everything as
> nouns - see Steve Yegge's "Kingdom of Nouns" article -
> http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-
> nouns.html
<rant>
Rails' RESTful is a good approximation of the real thing, geared to
an (X)HTML environment. Using raw HTTP (i.e. GET/PUT/POST/DELETE
headers) there really is no problem with edits - an edit would just
be a GET existing resource followed by a POST updated resource, and a
new resource would be created using a PUT resource request. There
would be no such thing as
/posts/1;edit
because that's unnecessary at the HTTP level.
However in (X)HTML land your application has to provide an interface
that allows a resource to be presented in an editable manner, hence
the use of a parameter to a GET request to return a representation
that can be edited by the browser. In this case you are not using
'edit' as a verb, but as a noun identifying the edit form itself. Of
course the
/posts/1;edit
URI is a very non-standard usage as most people wouldn't think of
embedding a semicolon in the URI itself but would instead use a GET
parameter:
/posts/1?edit=
or
/posts/1?action=edit
which has implications for caching of the content. Blame the W3C for
this annoyance - they've had more than enough years to add DELETE and
PUT into the HTML specs, instead of arguing over <BLINK>, the correct
usage of <I> and <B>, and all the other trivia that seems to obsess
them.
</rant>
Of course these limitations don't apply if you're working on an AJAX
application as it's then possible to send the full range of HTTP
verbs :)
> 2. What is the right way to do search? Are query strings still ok in
> urls? Is search a seperate controller?
A RESTful search should consist of a URI approximately similar to the
search criterion, and your app would transparently do the search
routing in the background, returning an index page listing the top
hits. So for example:
/books/authors/a-z/country/us
and
/books/country/us/authors/a-z
would both perform the same search and return the same listing of
authors. This reflects the fact that both URIs refer to the same
resource and that its index page is the natural place to list all of
the matching authors. However nobody does this - probably because
there is a common misconception that REST somehow mandates unique
URIs for resources, which is not the case. Hence the usual
alternative of:
/books/search?authors=a-z;country=us
which is not a valid REST request ('search' is a redundant verb), or
the somewhat more RESTful
/books?authors=a-z;country=us
where GET parameters are being used appropriately to specify
criterion. Of course parameters in GET requests have a habit of
confusing caching servers, increasing traffic for popular searches on
the originating server...
> 3. Similarly what is the right way to do sorting/pagination? For
> example when I GET /posts I may want to only return the top ten in
> html - but what about in the xml respond_to do I return all of them or
> just ten? Is the idea that *exactly* the same resource(s) are
> returned, no matter the mime type or can _what_ is returned vary as
> well as _how_ it is returned?
Pagination could be handled using a URI such as:
/recipes/1/pages/3?page_entries=30
which makes proper use of HTTP parameters with a GET request. There
is of course a Pagination object in Rails, but for many tasks it's
somewhat restrictive and it's simple enough to roll your own.
A common mistake with pagination schemes is to mandate the number of
page entries on the server side, which is not where responsibility
should lie (HTTP servers should not maintain state, even if it is one-
size-fits-all 'default' state). With vanilla (X)HTML the client may
need to be told the number of entries by scripts on the server as
part of creating appropriate browsing links for the page, but web
services and URI interfaces should be more flexible than that. After
all, it's the client that understands the concept of pages and the
client should therefore be able to specify how large it believes
pages should be and which page it thinks it wants in the sequence -
or some other meaningful pagination specifier. This would suggest
alternative URIs of the form:
/books/1?page=3¶graphs_per_page=10
or
/recipes/1?start_item=91&items_to_list=30
or
/recipes/1/items/91-120
and the server would either return valid content or a resource not
found error (either a standard 404 or a redirect to an appropriate
'sensible' error response for an HTML browser).
> 4. The urls nested resources produces only seems to make sense for the
> new / index /create actions - by the time the resource has a unique id
> it already has a unique url, because the id is unique - but there
> doesn't seem to be any way to disable the urls you don't want.
You've just eluded to the single biggest flaw in Rails' URI
conventions. Exposing database IDs over the network is a very bad
idea and everyone should write their applications to use public
resource names rather than IDs. In real-world applications it will
have minimal impact on database query performance (after all, you are
using indexes I hope!) and at the same time will make your URIs much
more robust. Otherwise you're handing implementation-dependent detail
to any random third-party that wants to explore your application, and
constraining your public interface to a static view of your data.
You're also breaking a key tenet of user interface design - a URI is
intended to be a human-friendly resource identifier and as such
should use meaningful semantics wherever possible to simplify the
user experience!
Instead of:
/posts/1
you should favour:
/posts/my-views-on-REST
or
/posts?title=my-views-on-REST&version=0.1
or
/posts?year=2006&month=December&category=REST&author=all
or
/posts/2006/December/REST
none of which expose your database IDs, and all of which can point to
the same resource, allowing users to remember whichever style of URI
makes most sense to them as well as making educated guesses about how
to search for other resources in your application.
This is where the environment/routes.rb file comes in to play. Some
useful resources on Rails routes include:
http://www.pinupgeek.com/2006/5/24/rails-routing-demystified
http://www.afreshcup.com/2007/2/9/basic-routing-in-rails
but these all tend to be pretty basic. I've yet to find any really
deep geek articles on the subject, although LRUGer Paul Battley has
done some interesting work on a procedural alternative to the
standard routing model so he might be able to suggest appropriate
resources.
> 5. For nested resources, how do you avoid a dirty great switch
> statement in your controller if things can be nested under many
> different resources?
Rails tends to encourage one controller per view so if you've lots of
big case statements in your controllers the chances are that you've
too few controllers relative to your views. Also remember that
multiple routes can all lead to the same controller/view combination...
> 6. What do you do when you want to create multiple objects at once?
> What do you return in the location field in the xml response? or
> update or destroy many objects at once? What happens if you want to do
> ANYTHING to more than one resource at once?
You could try using a similar approach to the one suggested above for
search, in that effectively a multi-resource delete or update is
analogous to a GET, so performing a POST with a form returned by the
query:
/books/authors/a-z;edit?theme=&
would change the theme attributes on the specified range of
resources. The returned URI for the location should probably be:
/books/authors/a-z
as that's the resource that in this case has been updated and an
index.html would be returned to a browser (or index.xml, index.jpg,
whatever other MIMI-type is requested).
With regard to how many items to return in a request, this in general
should be specified by the client (as mentioned above).
> 7. How useful is ActiveResource - is anyone here using it? can you do
> much more than find a list of resources/resources by id - like the sql
> options in find()?
The demo of it at RCE looked interesting as it'll allow complex
automation pipelines to be built using a very similar API to that
provided by ActiveRecord. However if it does rely exclusively on
resource IDs I have a feeling it's going to cause almost as many
problems as it will solve.
> Any thoughts on any of these very very welcome - and as a call for
> talk ideas was raised earlier I would really appreciate a talk on REST
> that goes beyond the basics of here is a resource generated with the
> scaffold - "look now I can find a record with an id with
> ActiveResource" - anyone offering...?
>
> On a side note does anyone know if there is a group/mailing list
> devoted to REST - I tried to find one the other day but google groups
> came up blank :(
A good place to start research the subject:
http://rest.blueoxen.net/cgi-bin/wiki.pl
which has links to other resources.
Ellie
Eleanor McHugh
Games With Brains
----
raise ArgumentError unless @reality.responds_to? :reason
More information about the Chat
mailing list