REST 101: Part 5 - Respond! 28

Posted by jeff Tuesday, May 01, 2007 20:44:00 GMT

Respond game

Photo credit: http://www.flickr.com/photos/pantagrapher/

Last time, we learned how Rails conventions enables your HTML to access your resources. But we want our software to support more than just HTML clients. Our flight schedule software needs to be accessible by web browsers, cell phones, and even third-party libraries.

As far as REST concepts are concerned, we've covered the basics in parts 1 through 4. If this was a dot-to-dot picture, we've already placed our dots. Now we can connect them and see what we've been making! This is actually one of my favorite parts about REST in Rails, because it's another example of the beautiful Ruby language underneath.

Short But Oh So Sweet

To connect our dots, we have to make our Rails controllers provide different responses depending on who's on the other end of the line. Let's take an easy example: getting a list of airports. If you used the scaffold_resource generator in Rails 1.2, you got code that looked something like this:


    class AirportsController < ApplicationController

        def index
            @airports = Airport.find :all

            respond_to do |format|
                format.html  # do nothing, allow Rails to render index.rhtml
                format.js    # do nothing, allow Rails to render index.rjs
                format.xml   { render :xml => @airports.to_xml }
            end
        end

    end

The first line is easy: find all the airports. The rest of it is some weird-looking code. But that's all there is, and it's able to render the list of airports in HTML, XML, and even with some ajax when needed! So once you understand the respond_to stuff, you'll be all set.

In HTTP, client applications provide meta-information about their request in the HTTP "header". The header is just a bunch of optional key-value pairs that the client can send along. There are some predefined key names that can be used. One of the standard keys is the "Accept-Type". The client can pass along an "Accept-Type" value to indicate what kind of format the client can "accept," or understand. If it's omitted, the server assumes that the client can understand HTML. But any valid MIME type can be specified. If the client sends "Accept-Type: text/xml", then the server is supposed to respond with an XML document fragment instead of HTML markup.

So, it's this Accept-Type header that enables all of the magic inside the respond_to block. Let's imagine implementing this the lame way. Pretend that Rails could pass the value of the Accept-Type value to your action method, so you could render the correct response:


    class AirportsController < ApplicationController

        # Pretend that Rails will call our index action, 
            # and will pass in the value of the Accept-Type header
        def index(client_format)
            @airports = Airport.find :all

            if client_format == "text/html"
                # TO DO: render the default template

            elsif client_format == "application/javascript"
                # TO DO: return some javascript

            elsif client_format == "application/xml" || client_format == "text/xml"
                # TO DO: return some XML back the client

            # ... more elsif statements here for each MIME type you want to support
            end
        end

    end

Clean Up Time

You have to squint past the ugliness, but the code is pretty simple: based on the requested format, we return the correct representation of our list of airports. I imagine the core team might have started with code that was something like this (or at least in their head even if they didn't type it), and they quickly refactored by added a helper method called respond_to. Let's look at the respond_to snippet again:


  respond_to do |format|
    format.html  # do nothing, allow Rails to render index.rhtml
    format.js    # do nothing, allow Rails to render index.rjs
    format.xml   { render :xml => @airports.to_xml }
  end

First, while we might not know much about the weird respond_to method, we do know one thing - it takes a Ruby block. (If you don't know about Ruby blocks, well, go do some Googling first and then come back.) Inside the block, you get passed a rather odd-behaving variable I've called format. And then it looks like we're calling three methods on the format object.

Here's the idea: Rails can't magically know which types of clients you want to support, but it can already do 80% of the work for you. You just need to somehow indicate which kinds of clients you want to accept, and for each client, you need to render a representation of your resource that can be understood by that particular client. So instead of Rails telling you which format was requested, it expects you to tell it which formats you are able to "respond to" (get it?). And for each format, you (optionally) provide a block of your own, that is able to provide the representation for that format.

In the example above, we're telling Rails that for HTML and Javascript clients, Rails can do its default behavior - find the right template and render it, but if a client wants XML, then we supply a block with the actual implementation.

Remember, a long time ago, in a galaxy far, far away, we talked about the difference between a resource and its many possible representations? How HTML is really just one representation of a resource? ("Your application is not a web page", etc.?) Now you can even see that in our code: html is just one of the possible ways we render our resources. Awesome, huh?

Parting Advice

Naturally, we can't cover everything there is to know about REST. That's why we called it "101". But we have talked about how to design your application in a "restful" way and how it's different from traditional object-oriented design. We've talked about the concept of resources, and how they are your paradigm for your application.

Wondering what to do next? Try it! Just create a Rails app from scratch, try the scaffold_resource generator, study the code, and then try to customize the views. Try to customize the controllers. It's easier than you think.

Here are some resources I recommend (note, PeepCode supports this blog):

Got others? Link 'em up in the comments.

Comments

Leave a response

  1. Daniel Fischer   May 02, 2007 @ 10:06 AM

    Love the REST tutorials, I already had a good idea of how it all worked; but your visualization certainly helped. I thank you! :)

  2. Dylan Bennett   May 03, 2007 @ 02:52 AM

    Great set of posts. Can't thank you enough for the time you spent to write them. Very much appreciated.

  3. Douglas Morato   May 03, 2007 @ 10:55 PM

    WOOOWW !!! You rock !!! Man.. this is by far one of the best Tutorial / How-To / 101 i've seen on Rails (and a read/watched a bunch of them). You should really consider writing a book, cause you REALLY have that communicational gift.

    I suggest you to put a paypal donation button or an amazon wishlist, cause you really deserve to be compensated for such a great job. I looked for it everywhere but couldn't find ! Thanks, thanks and thanks again for all these content.... Please keep it coming !

  4. Jeff   May 04, 2007 @ 12:47 AM

    Thanks guys, I appreciate it! And please feel free to ask any follow-up questions, either now or later.

  5. BJ Vicks   May 07, 2007 @ 04:06 AM

    To echo Daniel, I worked things out on my own a few months back after piecing together a disparate collection of fragmented tips and articles... I could have used this then. This will be a really valuable tutorial for many new to Rails, to be sure. Thanks.

  6. Stefan   May 15, 2007 @ 04:42 PM

    Thanks, I found this tutorial informative and useful. I'd run across the term REST, and had been confused - and how do you search for 'rest'? Somehow I ended up here, and had all my questions answered. (Actually I ended up at part 1, but yeah...)

  7. SEO G   May 16, 2007 @ 03:55 PM

    Thanks for the tutorial, it really helped to clarify exactly what was going on with the "respond_to" business. Definitely a much more elegant way to respond to requests and this explanation gives more insight into the hows and whys of it.

  8. Rhuantavan   June 01, 2007 @ 08:07 AM

    Hey thanks, that was very informative!

  9. Rebort   June 04, 2007 @ 06:16 PM

    Thanks for the tutorials. These + the Peepcode cast have made me very comfortable with REST -- I can't wait to implement this in my next project.

    One curiousity: You mention outputting resources to mobiles, but as far as I know WAP and WML are not included in the default restful-Rails mime-types. It seems a strange omission in something that can output XML and Javascript in one line of code.

  10. Jeff   June 04, 2007 @ 07:43 PM

    @Rebort: Mobile support will get easier in Rails 2.0. It will be much easier to not only support mime types like WAP and WML, but I wouldn't be surprised if they get added into the default mapping as well. This is another topic we'll cover in our upcoming training class this fall (just drop me a note if you want to be notified when we get class details nailed down).

  11. Yuanyi Zhang   June 10, 2007 @ 03:53 PM

    Hello, Jeff. I am a chinese rubyist, and I've translate your REST tutorials into chinese, you can find it at: http://letrails.cn/archives/6.

    Thanks for your good works.

  12. Jeff   June 12, 2007 @ 04:24 PM

    @Yuanyi Zhang: Wow, thank you! We appreciate it. I'll try to post a link to your translation soon.

  13. Chris Prakoso   July 04, 2007 @ 01:48 PM

    Just to say thanks for a great series. I builds my confidence in using RESTful Rails.

  14. Eugene   August 31, 2007 @ 11:20 AM

    Hi Jeff, many thanks for the great 101. I've tried to translate it into Russian here: http://www.taknado.com/2007/8/31/rest-on-rails

  15. Travis W Black   September 04, 2007 @ 03:29 PM

    Thanks a million. I have been struggling for a while to pull all the bits and pieces I have heard about REST together. This series pulled it all together for me in a nice little bundle and implanted it right into my skull. Now I feel pretty comfortable with the idea. One question though.... Do you know of any more resources past the 101 stage? If I'm gonna do it, I want to do it right!

  16. Bill   September 05, 2007 @ 10:29 PM

    Andrei,

    Thank you! This series has been very helpful to me.

    I have a question. I created a file airport.xml with this in it ...

    <airport><name>Raleigh Durham</name><designation>RDU</designation></airport>
    

    Then I post it like this ...

    curl -i -X POST -d @airport.xml http://localhost:3000/airports.xml
    

    A new record gets inserted into my airports table, but the name and designation are null.

    I added a print statement to my create method. It prints NilClass

    def create @airport = Airport.new(params[:airport])

    p params[:airport].class
    
    respond_to do |format|
      if @airport.save
        flash[:notice] = 'Airport was successfully created.'
        format.html { redirect_to airport_url(@airport) }
        format.xml  { head :created, :location => airport_url(@airport) }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @airport.errors.to_xml }
      end
    end
    

    end

    Is this to be expected? Can you tell me how to get the name and designation into my new records?

    Thanks,

    Bill

  17. John O'Shea   September 26, 2007 @ 12:55 PM

    Bill, You need to specify the Content-Type when passing in an XML param:

    $ curl -i -X POST -H "Content-Type: application/xml" -d @airport.xml http://localhost:3000/airports.xml

  18. Henrique   October 06, 2007 @ 07:59 PM

    Great work, a nice introduction to REST, Rails and scaffold_resource. With this series I have the big picture on my mind now. Keep up the good work!

  19. Federico F.   December 12, 2007 @ 09:31 PM

    Awesome tutorial, thank you!!

  20. George Githinji   December 20, 2007 @ 03:23 PM

    Thank you so much for the tutorial! Its do helpful! and to the point

  21. Fernando Correia   January 01, 2008 @ 10:31 PM

    Thank you for this tutorial. It is very helpful.

  22. Jyotsna   May 22, 2008 @ 02:21 PM

    Fantastic Blog Champ. I m new to ROR n was struggling with RESRful Rails. This helped me a lot :)

  23. Eduardo Mucelli   November 22, 2008 @ 08:17 PM

    I agree with Douglas Morato, you know how to communicate and should consider write a book. I´m not a native english speaker (far from it) and the fluence of reading process was awesome for me.

    The best REST (focused on Rails or not) text ever.

  24. Jeff   November 23, 2008 @ 09:36 PM

    Thanks Eduardo!

    And actually, we have written a book! Our first book has just been published, Rails for .NET Developers.

  25. skob   April 29, 2009 @ 05:28 PM

    Thanks for this!

    What I learned in 2009: REST is cool :D Although Rails 2.x is doing all the ressource and routing stuff, I didn’t understand exactly WHAT it does. Now I get it and it solved a bigger problem in my App. When I can take a REST without getting dirty, I’ll throw away the SOAP and stay DRY ;)

  26. Anandth   November 17, 2009 @ 05:52 PM

    You made me to understand the REST concept in few minutes, which i have been trying to understand it for weeks.

    Thanks and Keep up the Good work.

    My Suggestion: You can try some book writing.

    - Anandth

  27. Melvin Perez-Cedano   February 10, 2010 @ 04:53 AM

    I hate you! How someone could be so good explaining things!? :-)

    Awesome job, love it!!

  28. Emmanuel   March 11, 2010 @ 11:27 PM

    wow! very nice! explained in very simple terms. thanks for posting this. was just starting to learn rails but had difficulty understanding the concept until i read this REST basics. kudos and keep up the good work!

    emmanuel

Comment


(won't be published)