When you generate a scaffold in Rails, you’ll see the usual respond_to blocks:

app/controllers/tasks_controller.rb def destroy @task . destroy respond_to do | format | format . html { redirect_to tasks_url , notice: 'Task was successfully destroyed.' } format . json { head :no_content } end end

But some of your actions, like index , don’t have them!

app/controllers/tasks_controller.rb # GET /tasks # GET /tasks.json def index @tasks = Task . all end

This is bad. Why? If you hit /tasks.txt , and txt isn’t supported by your app, you’ll get the wrong error:

ActionView::MissingTemplate (Missing template tasks/index, application/index with {:locale=>[:en], :formats=>[:text], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :coffee, :jbuilder]}

This isn’t quite right. You should be telling the client that they’re requesting a format you don’t support, not that you can’t find the right file.

If this was a UnknownFormat error, you could return a better response code. Instead, these errors will get mixed together with other, unrelated errors, and it’ll be really hard to handle them.

You could add a respond_to block to your index action:

app/controllers/tasks_controller.rb # GET /tasks # GET /tasks.json def index @tasks = Task . all respond_to do | format | format . html format . json end end

Then, you’d get the exception and error code you’d expect:

Started GET "/tasks.txt" for 127.0.0.1 at 2014-11-03 22:05:12 -0800 Processing by TasksController#index as TEXT Completed 406 Not Acceptable in 21ms ActionController::UnknownFormat (ActionController::UnknownFormat): app/controllers/tasks_controller.rb:8:in `index'

Much better. But littering all your controllers with respond_to is crazy. It feels un-Rails-ish. It violates DRY. And it distracts you from the work your controller is actually doing.

You still want to handle bad formats correctly. So what do you do?

A respond_to shortcut

If you’re not doing anything special to render your objects, you can take a shortcut. If you write:

app/controllers/tasks_controller.rb def index @tasks = Task . all respond_to :html , :json end

it works the same way as writing the full respond_to block in index . It’s a short way to tell Rails about all the formats your action knows about. And if different actions support different formats, this is a good way to handle those differences without much code.

Handle formats at the controller level

Usually, though, each action in your controller will work with the same formats. If index responds to json , so will new , and create , and everything else. So it’d be nice if you could have a respond_to that would affect the entire controller:

app/controllers/tasks_controller.rb class TasksController < ApplicationController before_action :set_task , only: [ :show , :edit , :update , :destroy ] respond_to :html , :json # GET /tasks # GET /tasks.json def index @tasks = Task . all respond_with ( @tasks ) end

And this actually works:

Started GET "/tasks.txt" for 127.0.0.1 at 2014-11-03 22:17:37 -0800 Processing by TasksController#index as TEXT Completed 406 Not Acceptable in 7ms ActionController::UnknownFormat (ActionController::UnknownFormat): app/controllers/tasks_controller.rb:8:in `index'

Exactly the kind of error we were hoping to get! And you didn’t have to mess with each action to do it.

Sometimes you’ll want to do different things depending on the state of a model. For instance, for create , you’d either redirect or re-render the form, depending on whether or not the model is valid.

Rails can handle this. But you still have to tell it which object you want it to check, with respond_with . So instead of:

app/controllers/tasks_controller.rb def create @task = Task . new ( task_params ) respond_to do | format | if @task . save format . html { redirect_to @task , notice: 'Task was successfully created.' } format . json { render :show , status: :created , location: @task } else format . html { render :new } format . json { render json: @task . errors , status: :unprocessable_entity } end end end

you can write:

app/controllers/tasks_controller.rb def create @task = Task . new ( task_params ) flash [ :notice ] = "Task was successfully created." if @task . save respond_with ( @task ) end

This way, you separate your code from the formats you respond to. You can tell Rails once which formats you want to handle. You don’t have to repeat them in every action.

The responders gem

In Rails 4.2, there’s a catch: respond_with is no longer included. But you can get it back if you install the responders gem. And the responders gem brings some other nice features with it.

You can set flash messages in respond_with by including responders :flash at the top of your controller:

app/controllers/tasks_controller.rb class TasksController < ApplicationController responders :flash

Conveniently, you can set defaults for these flash messages in your locale files.

Also, if you have the responders gem in your Gemfile and you generate a Rails scaffold, the generator will create controllers using respond_with instead of respond_to :

app/controllers/tasks_controller.rb class TasksController < ApplicationController before_action :set_task , only: [ :show , :edit , :update , :destroy ] respond_to :html , :json def index @tasks = Task . all respond_with ( @tasks ) end def show respond_with ( @task ) end # ...

This is a lot cleaner than the scaffolds Rails comes with.

And finally, if you want to only respond with a format for specific controller actions, you can call respond_to multiple times:

class TasksController < ApplicationController respond_to :html respond_to :js , only: :create end

Thanks to Jeroen Weeink in the comments for that last tip!

respond_with or respond_to ?

If you want to return different information for different formats, you have a few options. The controller-level respond_to combined with respond_with is a great way to get short controllers. But it tends to help the most when all of your controller actions respond to the same format, and act in the way Rails expects them to.

Sometimes, though, you want to be able to have a few actions that act differently. The one-liner respond_to is great for handling that situation.

If you need more control, use the full respond_to with a block, and you can handle each format however you want.

With any of these, requests for formats you don’t support will get the right error. And both your app and its clients will be a lot less confused.