Rails’ respond_to method

August 6, 2007 by Michael

Ruby On Rails has a neat little feature called “respond_to”:

class WeblogController < ActionController::Base
  def index
    @posts = Post.find :all
    respond_to do |format|
      format.html
      format.xml { render :xml => @posts.to_xml }
      format.rss { render :action => "feed.rxml" }
    end
  end
end

Quelle

This feature uses the routes with the new default route since 1.2:

map.connect ':controller/:action/:id.:format'.

as well as accept header.

With the later and certainly co-starring Internet Explorer 5.5 to 7.0 the fun starts.

In most examples developers serve html, xml and javascript like here. I cannot confirm neither negate that the following problem occurs with these types as well:

I just finished developing an “respond_to jpg” method:

This was there from the beginning: http://dailyfratze.de/michael/2007/8 and i could easily achieve this http://dailyfratze.de/michael/2007/8.jpg with the exact same method:

respond_to do |format|      
  if(!user)
    # some stuff to see if the user exists, blah blah
    redirect_to(:action => :index)
    return
  else
    # some common stuff
 
    format.html {}
 
    format.jpg do
      # some rmagick stuff
      images = Array.new
      # some more and finally 
      send_data image.to_blob, :type =>"image/jpeg", :disposition =>'inline'
      # the return is important, without it and without render, the other block is executed as well
      return
    end
  end
end

I use the following routes in the following order:

map.connect ':user/:year/:month.:format',
                  :controller   => 'daily',
                  :action       => 'month',
                  :requirements => {:year  => /(19|20)\d\d/,
                  :month => /(0?[1-9]|1[012])/}
 
map.connect ':user/:year/:month/',
                   :controller   => 'daily',
                  :action       => 'month',
                  :requirements => {:year  => /(19|20)\d\d/,
                                           :month => /(0?[1-9]|1[012])/}

I know, this is not very DRY, but i didn’t find a way to make the dot in ‘:user/:year/:month.:format’, optional. Format isn’t a problem, but the dot was there to stay. (*)

Nevertheless, the thing worked perfect. Firefox and Safari send “*/*” as an accept header, the format argument was taken and all was well.

IE sends something like “image/jpeg”, “image/png”, “application/x-shockwave-flash”, [… some more stuff…], “*/”* the first request and all subsequent request send “*/*” as accept header. Hell yeah. After closing the window, the process repeats itself.

What does this mean? The first visit to a monthly view on Daily Fratze brought an jpeg image to IE users, the second the standard html page. Crap!

I found a post on a similar problem here: Strange behaviour of respond_to in IE.

The commentors says to put the most important block first but this isn’t an option if the accept header is used wrong. To me it seems that respond_to is somewhat broken. It should use the :format parameter or the accept header, not a mix of it.

I came up with the following solution:

respond_to do |format|      
  format.html {}
 
  format.jpg do
     # some rmagick stuff
     images = Array.new
     send_data image.to_blob, :type =>"image/jpeg", :disposition =>'inline'
     return
  end if params[:format] == 'jpg'  # If responds_to doesn't take care of :format, i'll do!
end

If i generate links to this method with url_for(…blah…, :format => ‘jpg’) i get the correct format and i guess this will work for js, rss and co. as well.

Edit:(*) I’ve noticed some change in behaviour in Rails 2.3.x: I now use :format => nil in the routes and the dot is optional as well then and i have dropped the more or less duplicate routes.

3 comments

  1. Jillian wrote:

    The project I’m working on is having the same sort of issue with IE– except with excel being favored over everything else. XML behaves nicely when placed after html in the respond_to code, but xls is always trumping, even at the end.
    Your solution with :format works wonderfully. I just wish IE would act right, or that there was some way to tell IE what priority each format should have?

    Posted on August 16, 2007 at 8:55 PM | Permalink
  2. Michael wrote:

    Hey Jilian,
    glad to hear that someone can make use of my findings and even better, that it works with other formats as well.

    I can tell you, it was devasteting, that this wonderful function won’t work properly in IE.

    But to be fair, i guess the problem is partially on the Rails Side with Rails not correctly checking the accept header, but i can be wrong.

    With firefox you can change the request headers, i don’t know if its possible in IE, too. Also, it wouldn’t be a convinient user experience, i guess.

    Have a nice day,
    Michael.

    Posted on August 16, 2007 at 9:02 PM | Permalink
  3. Braxton Beyer wrote:

    for other’s that end up here, I was having a related issue with my ajax calls vs html pages. I fixed it by adding accept:’text/javascript’
    to my options of my prototype ajax calls.

    Posted on February 5, 2010 at 4:08 AM | Permalink
6 Trackbacks/Pingbacks
  1. […] which is dedicated to entirely to feed generation (yeah, i know about about REST and especially respond_to, but i wouldn’t get rid of thousands of useless sessions without: class FeedsController < […]

  2. […] RAILS’ RESPOND_TO METHOD – Michael Simons […]

  3. […] things are not really different in Rails and Grails world. The pendant to Rails’ respond_to method is Grails withFormat […]

  4. Adrian Birch: RoR on March 25, 2009 at 5:41 PM

    […] HTML and [JSON|http://www.json.org/]. RoR deals with this by having a [respond_to|http://info.michael-simons.eu/2007/08/06/rails-respond_to-method/] […]

  5. […] routes, unfortunately, one that includes the :format designation and one that doesn’t. See this blog post for […]

  6. […] different times in my app.  That’s why you have them right?  Anyway, after reading over  a related post at info.michael-simons.eu I discovered that IE was sending the wrong accept-headers.  So I checked out the Prototype API and […]

Post a Comment

Your email is never published. We need your name and email address only for verifying a legitimate comment. For more information, a copy of your saved data or a request to delete any data under this address, please send a short notice to michael@simons.ac from the address you used to comment on this entry.
By entering and submitting a comment, wether with or without name or email address, you'll agree that all data you have entered including your IP address will be checked and stored for a limited time by Automattic Inc., 60 29th Street #343, San Francisco, CA 94110-4929, USA. only for the purpose of avoiding spam. You can deny further storage of your data by sending an email to support@wordpress.com, with subject “Deletion of Data stored by Akismet”.
Required fields are marked *