When you generate a scaffold in Rails, you’ll see the usual respond_to
blocks:
But some of your actions, like index
, don’t have them!
This is bad. Why? If you hit /tasks.txt
, and txt
isn’t supported by your app, you’ll get the wrong error:
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:
Then, you’d get the exception and error code you’d expect:
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:
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:
And this actually works:
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:
you can write:
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:
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
:
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:
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.