method

respond_to

rails latest stable - Class: ActionController::MimeResponds::InstanceMethods

Method deprecated or moved

This method is deprecated or moved on the latest stable version. The last existing version (v2.3.8) is shown here.

respond_to(*types, &block)
public

Without web-service support, an action which collects the data for displaying a list of people might look something like this:

  def index
    @people = Person.find(:all)
  end

Here’s the same action, with web-service support baked in:

  def index
    @people = Person.find(:all)

    respond_to do |format|
      format.html
      format.xml { render :xml => @people.to_xml }
    end
  end

What that says is, "if the client wants HTML in response to this action, just respond as we would have before, but if the client wants XML, return them the list of people in XML format." (Rails determines the desired response format from the HTTP Accept header submitted by the client.)

Supposing you have an action that adds a new person, optionally creating their company (by name) if it does not already exist, without web-services, it might look like this:

  def create
    @company = Company.find_or_create_by_name(params[:company][:name])
    @person  = @company.people.create(params[:person])

    redirect_to(person_list_url)
  end

Here’s the same action, with web-service support baked in:

  def create
    company  = params[:person].delete(:company)
    @company = Company.find_or_create_by_name(company[:name])
    @person  = @company.people.create(params[:person])

    respond_to do |format|
      format.html { redirect_to(person_list_url) }
      format.js
      format.xml  { render :xml => @person.to_xml(:include => @company) }
    end
  end

If the client wants HTML, we just redirect them back to the person list. If they want Javascript (format.js), then it is an RJS request and we render the RJS template associated with this action. Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also include the person’s company in the rendered XML, so you get something like this:

  <person>
    <id>...</id>
    ...
    <company>
      <id>...</id>
      <name>...</name>
      ...
    </company>
  </person>

Note, however, the extra bit at the top of that action:

  company  = params[:person].delete(:company)
  @company = Company.find_or_create_by_name(company[:name])

This is because the incoming XML document (if a web-service request is in process) can only contain a single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):

  person[name]=...&person[company][name]=...&...

And, like this (xml-encoded):

  <person>
    <name>...</name>
    <company>
      <name>...</name>
    </company>
  </person>

In other words, we make the request so that it operates on a single entity’s person. Then, in the action, we extract the company data from the request, find or create the company, and then create the new person with the remaining data.

Note that you can define your own XML parameter parser which would allow you to describe multiple entities in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow and accept Rails' defaults, life will be much easier.

If you need to use a MIME type which isn’t supported by default, you can register your own handlers in environment.rb as follows.

  Mime::Type.register "image/jpg", :jpg

12Notes

How to test different responses in controller tests/specs

rubymaverick · Jun 25, 200813 thanks

When you want to write a controller test or spec (rspec) to test out a different response type other than html, just set the HTTP_ACCEPTS header like so before the request:

@request.env['HTTP_ACCEPT'] = "application/rss" post :create, :blog => {}

Types array shorthand

rob-twf · Nov 26, 200810 thanks

You can have respond_to blocks that look like this:

respond_to do |format|
format.html
format.xml
end

Here each individual format doesn't receive a block and so Rails automatically tries to render the appropriate view for the mime type (e.g. action.html.erb, action.xml.erb or as a last resort action.erb)

You can do exactly the same thing by passing an array of Mime types to respond_to like this:

respond_to(:html, :xml)

Rails defined Mime Types

rubymaverick · Jun 26, 20089 thanks

Here are all the default Rails Mime Types:

"/" => :all "text/plain" => :text "text/html" => :html "application/xhtml+xml" => :html "text/javascript" => :js "application/javascript" => :js "application/x-javascript" => :js "text/calendar" => :ics
"text/csv" => :csv
"application/xml" => :xml "text/xml" => :xml "application/x-xml" => :xml "text/yaml" => :yaml "application/x-yaml" => :yaml "application/rss+xml" => :rss
"application/atom+xml" => :atom
"application/json" => :json "text/x-json" => :json

How to test different responses of respond_to

nachocab · Jan 24, 20098 thanks

You can shorten this: @request.env['HTTP_ACCEPT'] = "application/rss"

To this: @request.accept = "application/rss"

Also, if you send more than one Mime type, it will render the first one: @request.accept = "text/javascript, text/html" #=> renders JS @request.accept = "text/html, text/javascript" #=> renders HTML

Security issue with non-HTML formats

szeryf · Feb 10, 20098 thanks

Please note that using default +to_xml+ or +to_json+ methods can lead to security holes, as these method expose all attributes of your model by default, including +salt+, +crypted_password+, +permissions+, +status+ or whatever you might have.

You might want to override these methods in your models, e.g.:

def to_xml
super( :only => [ :login, :first_name, :last_name ] )
end

Or consider not using +responds_to+ at all, if you only want to provide HTML.

Ordering of format blocks is important

netshade · Apr 1, 20096 thanks

The order in which your format blocks appear, like:

format.html { } format.js { }

are used to infer priority in cases where the appropriate format is ambiguous.

A catch-all format

hosiawak · Jun 4, 20095 thanks

If you'd like to specify a respond_to only for 1 or a few formats and render something else for all other formats, eg: (action.rss returns a feed but action.html or action.js should just render 404), use format.all:

respond_to do |format| format.rss { render_rss } format.all { render_404 } end

Rails will render an empty string for all formats that don't specify a response explicitly.

Custom MIME Type

rubymaverick · Jun 25, 20084 thanks

After you register a custom Mime::Type like stated above, you can do:

respond_to do |format| # .jpg corresponds to the second argument passed to #register # Mime::Type.register "image/jpg", :jpg format.jpg { ...do something here... } end

Re: How to test different responses

hosiawak · Jun 19, 20092 thanks

In addition to using:

@request.accept = "text/javascript" #=> request JS

as rubymaverick and nachocab suggested, you can also pass :format when calling your action, eg:

it "GET #most_viewed renders #most_viewed.js.rjs template if js requested" do get :most_viewed, :format => 'js' response.should render_template('most_viewed') end

Rendering After Exception In respond_to() Block

sshaw · Apr 24, 2010

Remember, format blocks set the response's content type. This can present problems when handling errors.

class MediaController rescue_from ActionController::MissingFile do |e| # User's browser probably wont display this # Content-Type is application/x-shockwave-flash render :file => File.join(Rails.public_path, '404.html'), :status => 404 end

show details or stream video

def show @media = Media.find params[:id] respond_to do |format| format.html format.flv { send_file @media.path, :disposition => 'inline' } end end end

For these situations you must set :content_type when calling render:

render :file => File.join(Rails.public_path, '404.html'), :status => 404, :content_type => 'text/html' 

You must use the yielded object

tokland · May 14, 2010

A warning note (at least for v2.3.4): if you don't use the yielded format object, you will get a cryptic error like this:

NoMethodError (You have a nil object when you didn't expect it! The error occurred while evaluating nil.call): app/controllers/comments_controller.rb:11:in `create'

So make sure you use it!

include respond_to in your controller action only if there are multiple formats of that view

Milind · Aug 5, 2014

==consider this

def index

@users=User.get_users respond_to do |format| format.html format.json format.js end end

is good if you have a call to users/index by both <%= link_to ("Show users",user_path)%> ##will render users/index.html.erb ===Also same call but with ajax <%= link_to ("Show users",user_path,remote=>true)%> ##will render users/index.js.erb..handled by respond_to block ===Also same call but with json <%= link_to ("Show users",user_path,remote=>true,:format=>json)%> ##will render users/index.json.erb..handled by respond_to block. ===But if you have just first one,so remove respond_to block as you are sure that you only need index.html.erb ALWAYS def index @users=User.get_users end So if your action is being called both by BOTH ajax,non-ajax call,its good to use respond_to.