When you generate a scaffold in Rails, you’ll see the usual
1 2 3 4 5 6 7
But some of your actions, like
index, don’t have them!
1 2 3 4 5
This is bad. Why? If you hit
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
1 2 3 4 5 6 7 8 9
Then, you’d get the exception and error code you’d expect:
1 2 3 4 5 6
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?
If you’re not doing anything special to render your objects, you can take a shortcut. If you write:
1 2 3 4
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
create, and everything else. So it’d be nice if you could have a
respond_to that would affect the entire controller:
1 2 3 4 5 6 7 8 9 10
And this actually works:
1 2 3 4 5 6
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:
1 2 3 4 5 6 7 8 9 10 11 12 13
you can write:
1 2 3 4 5
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14
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:
1 2 3 4
Thanks to Jeroen Weeink in the comments for that last tip!
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.