REST 101: Part 5 - Respond! 23

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. CrardVow   April 17, 2008 @ 11:58 PM

    <size>12]Click here to watch homemade movie

    http://www.cruelplanet.com/thumbs/vrvr_26gej/2.jpg

    http://allthumbs.biz/brutalvideo/a3219e.jpg </size>

    <size>10] gay jail rape scenes

    raunchy anal strapon rape

    hardcore adult fantasy rape </size>

    anime girl rapes a anime anal forced fuck pain rough abuse bitch gang rape lesbian sex rape girls rapes girls hentai little hentai girl rapelittle girl rape hentai anime cartoon teen rape hentai girl rapes guy by force asian rape sex forced male rape archive of fantasy rape incest stories male rape fantasy stories rape fantasys lesbian pussy rape monter tentacle rape hentai rape galleries stories youtube gang rape download free gang rape movies bondage rape hentai a list of date rape drugs nirvana rape me forced rape movie date rape lyrics date rape sex youg anal rape sories lolita preteen rape gay rape sex booty bondage rape hardcore tentacle rape game free asian rape video tentacle rape hentai hentai harcore porn rape stories of rape and fantasy night rape fantasy hardcore bondage xxx rape strapon anal rape annual date rapes hentai rape and torture free hardcore rape videos anime rape girl hentai toon sex rape archieves of fantasy rape stories anime rape sites streaming rape videos real rape vids celebrity rape scene clips videos of gay rape fantasy rape porn babysitting rape videos sex rape fantasy anime brutal rape hentai sex rape fantasy rape video underage rape galleries granny rape galleries virgin rape stories underage rape videos prison rape and pdf drugs used for date rape prison rape scenes in movies tiny preteen rape galleries rape fear forced witness rape fantasy free real rape

  23. 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 :)

Comment