REST on Rails
by Matt Biddulph
|
Pages: 1, 2, 3
In Action
Let's see a command-line session with our REST-enabled web app. We'll use the REST hacker's number one web services client: /usr/bin/curl.
First create a new Item by POSTing some XML to the app:
$ curl -i -X POST -d
"<item><title>Article
title</title><bodytext>no
body</bodytext></item>"
http://localhost:3000/blog/item
HTTP/1.1 201 Created
Date: Sun, 09 Oct 2005 13:12:54 GMT
Location: http://localhost:3000/blog/item/1137975
The server parsed our XML, created an Item model instance, initialised it with our data and stored it in the database. It indicated success with the HTTP code 201 Created, and told us where to find the new resource with the Location header.
Now request a representation of the resource from its newly-minted URL:
$ curl http://localhost:3000/blog/item/1137975
<item id="1137975">
<title>Article title</title>
<bodytext>no body</bodytext>
</item>
The XML comes back as expected. Make a change to the resource by posting data to its URL:
$ curl -i -X POST -d
"<item><title>New
title</title></item>"
http://localhost:3000/blog/item/1137975
HTTP/1.1 200 OK
Date: Sun, 09 Oct 2005 13:14:29 GMT
Content-Type: text/xml
<item id="1137975">
<title>New title</title>
<bodytext>no body</bodytext>
</item>
Note that the title has changed but the bodytext field remains untouched as we didn't specify it in our input.
Finally, delete the resource:
$ curl -i -X
DELETE http://localhost:3000/blog/item/1137975
HTTP/1.1 204 Deleted
Date: Sun, 09 Oct 2005 13:14:29 GMT
Content-Type: text/xml
And verify that it's gone:
$ curl -i http://localhost:3000/blog/item/1137975
HTTP/1.1 404 Not Found
Date: Sun, 09 Oct 2005 13:17:29 GMT
Let's Make it a One-Liner
Now we have our code written, wouldn't it be nice if we could apply it to any model in any rails project? Let's package up the code so that it can be pulled in with one line of controller code, just like Rails builtins such as belongs_to, validates_uniqueness_of and layout.
To do this, we exploit Ruby's dynamic nature by providing a mixin class that transparently adds features to an existing class. There's a fairly simple code pattern for doing this:
module ExtraMethods
def self.append_features(base)
super
base.extend(ClassMethods)
end
module ClassMethods
def
additional_method(message)
module_eval <<-"end_eval",__FILE__,__LINE__
def example
puts "I've been added to give you this message:
'#{message}'"
end
end_eval
end
end
end
We can now use this in any class with a call to include:
class SomeClass
include ExtraMethods
additional_method "your dynamic method insertion
worked"
end
>> SomeClass.new().example()
I've been added to give you this message: 'your dynamic method
insertion worked
You can see this Ruby pattern in use in the code that accompanies this article. With the final code present in the lib directory of the project, a REST-enabled controller looks like this:
class NewsController << ApplicationController
rest_resource :item
rest_resource :category
end
Conclusion
Rails is a strong foundation for the REST style. Its approach to database modeling and URL routing leads to good URLs representing resources. These are easy to enable for XML reading and writing using simple dispatch mechanisms. Ruby's dynamic language features make it possible to package up convenience code into an easy-to-use one-liner.
In any application where the client can make changes to your database, you have to think carefully about granting access rights. The code presented here could be enhanced to use a Rails-based authentication system, or alternatively a web server such as Apache can be used to front an application and grant fine-grained rights dependent on user and HTTP verb.
It's instructive to compare the style of interaction in this article with the emerging Atom API standard, and to think about how this kind of API lends itself well to embedding in all sorts of clients, from command utilities to OSX Dashboard widgets, to Web 2.0 mashups.
- Isn't this making it too hard?
2007-11-07 12:17:57 SteveMidgley - PUT vs. POST
2006-02-18 16:09:11 StefanTilkov - Another way to do it
2005-12-30 22:39:28 BenStiglitz - Getting request verb..
2005-12-28 10:50:29 cjbrown102 - The "simple" design pattern for dynamic class extension
2005-12-19 15:41:02 richbodo - case out the http verbs
2005-11-09 13:37:17 bitsweat - Nice article but question on code examples
2005-11-08 20:33:09 vcosby