Article
Rapid RESTful Rails Apps -- No, Really!
Page: 1 2
XML Goodness
Now, I'll be the first to admit that this demo isn't really that impressive -- we could have done exactly the same thing using regular CRUD Rails. But where RESTful Rails becomes awesome is when you're able to publish your own web services for free (well, almost for free -- we need to add a couple of lines of code, but that's it).
To do this, we need to tell Rails that it should respond differently to an XML request. Thankfully, the respond_to method does all the heavy lifting for us. So let's modify the post's controller to look like this:
class PostsController < ApplicationController
# GET - displays all posts
def index
@posts = Post.find :all, :order => 'created_at ASC'
respond_to do |format|
format.xml { render :xml => @posts.to_xml }
format.html { }
end
end
# GET - shows one post (based on the supplied id)
def show
@post = Post.find(params[:id])
respond_to do |format|
format.xml { render :xml => @post.to_xml }
format.html { }
end
end
# GET - displays a form which can be used to create a post
def new
@post = Post.new
end
# POST - create a new post
def create
@post = Post.new(params[:post])
@post.save!
respond_to do |format|
format.html { redirect_to post_path(@post) }
format.xml { render :xml => @post.to_xml,
:status => :created }
end
rescue ActiveRecord::RecordInvalid
respond_to do |format|
format.html { render :action => 'new' }
format.xml { render :xml => @post.errors.to_xml,
:status => 500 }
end
end
# GET - displays a form allowing us to edit an existing post
def edit
@post = Post.find(params[:id])
end
# PUT - update an existing post
def update
@post = Post.find(params[:id])
@post.attributes = params[:post]
@post.save!
respond_to do |format|
format.html { redirect_to post_path(@post) }
format.xml { render :xml => @post.to_xml,
:status => :ok }
end
rescue ActiveRecord::RecordInvalid
respond_to do |format|
format.html { render :action => 'edit' }
format.xml { render :xml => @post.errors.to_xml,
:status => 500 }
end
end
# DELETE - delete a post
def destroy
@post = Post.find(params[:id])
@post.destroy
respond_to do |format|
format.html { redirect_to posts_path }
format.xml { head :ok }
end
end
end
Let's take a look at what we've done here. We've introduced the new respond_to block, which allows us to separate the xml and html requests. We've also introduced to the app different types of rendering, using the built-in XML type, as well as utilizing to_xml to convert objects to serialized XML for output. Finally, this example shows us how to output different HTTP status codes.
The index and show methods are very simple: if the request is XML, it serializes the object (or objects) and spits them down the line. If you've installed curl, you can test this very easily. First, add a couple of posts using the web interface, then type the following in to a terminal:
curl -H 'Accept: application/xml' http://localhost:3000/posts.xml
This command should return a list of posts that you've made.
The next features -- creating, updating and deleting -- are the most interesting. If you tell Rails that the content you're posting is XML, it will automagically convert (or at least try to convert) the data into an array from which we can create an object.
On completion of a successful Tumblelog post, we send an HTTP status code of 201 Created, accompanied by a copy of the new post object. Sending the new object in the response means the consumer of the web service doesn't have to make another query to find it, which gives them a handy reference point.
The curl command would look something like this:
curl -H 'Accept: application/xml' -H 'Content-type: application/xml' http://localhost:3000/posts.xml -d '<post><message>A RESTful message</message></post>' -X POST
When we're successfully updating an existing post, we return a regular status code of 200 OK, again with a copy of the object. For example, if we had an object with an id of 4, we could update it with this code:
curl -H 'Accept: application/xml' -H 'Content-type: application/xml' http://localhost:3000/posts/4.xml -d '<post><message>An updated RESTful message</message></post>' -X PUT
In both the examples above, if the submitted data is not complete, Rails will return the error messages as XML with a status code 500 Internal Server Error.
The final example is a delete, which simply returns a 200 OK response:
curl -H 'Accept: application/xml' -H 'Content-type: application/xml' http://localhost:3000/posts/4.xml -X DELETE
Exercises For You at Home
You may have noticed that the error handling for the XML version of the site is fairly basic. If you try to update a post that doesn't exist, you'll get a HTML error. Try to work out a way to do this with XML -- and do the same with other exceptions. Here's a clue: look at the rescue_from class method.
See if you can work out how to generate a JSON version of the site that would be suitable for responding to AJAX requests. Once you start, you'll find it easier than you think!
Tying Up Some Loose Ends
- These instructions are run against Rails 2.0.2 using the default SQLite3 database, which is why we don't need to configure MySQL.
- I'm aware that Rails offers a resource generator that will automatically generate a model, controller, and set of templates for your RESTful app, but you wouldn't learn anything if I used that, would you?
- As I mentioned in my previous post, no browsers can perform HTTP
PUTorDELETErequests. Rails emulates them by injecting a secret hidden input field, named_method. Try it out and view source on the edit page. You'll see the form's method is still a trusty oldPOST, and there's an extra input field. - I hate the fact that there's no default delete special method, to allow you to add a deletion confirmation view. I usually add it using the
:memberattribute in the routes file (config/routes.rb), so our previous addition to this file would become:map.resources :posts, :member => { :delete => :get }
This allows me to add adeleteview (delete.html.erb) as well as letting me replace the delete form and button with a link in theshowview (show.html.erb).
Conclusion
That completes the very basic example of a RESTful Rails site. Obviously, there is some other stuff you would need to add for a working web site (authentication for one), but what we have achieved is still pretty impressive. As promised you can download the complete source code for your own use.