Rails respond_to block Explained
When you start your first Rails application you might think that controller actions are only capable of rendering HTML. So why and how to use a respond_to block? Here, we will show you how handy respond_to blocks can be.
respond_to
The first step is to create a new project and generate a scaffold in Rails. Let's take a look at some of our controller actions.
def index
@blogs = Blog.all
end
As you can see, index
action does not have a respond_to
block. So, this will work when just rendering an HTML. But what if, however, a client asks to get the page in TEXT format? It will lead to an exception:
ActionView::MissingTemplate (Missing template blogs/index ... with { ... :formats=>[:text], ...})
Rails goes through the registered formats and tries to find a compatible format for the MIME type in the request. If there is no handler it will raise an error. Credits to max.
Instead of telling our clients that the file is missing, we want to tell them that the requesting format is not supported. You could add a respond_to
block to your index
action:
def index
@blogs = Blog.all
respond_to do |format|
format.html # index.html
format.js # index.js
format.xml { render xml: @blogs }
end
end
After the change, the client will get 406 error
when the format is not supported. Also, your index
action will respond to two new formats: js and xml.
If you need a simple and quick way to render your objects, you can take this shortcut, it works in the same way as the example above:
def index
@blogs = Blog.all
respond_to :html, :json, :xml
end
Not sure if this really works? Go on and test it using RSpec and FactoryBot!
Responding to the Same Format
It’d be nice if you could have a respond_to
that would affect the entire controller. Usually, each action in your controller can work with the same formats, you can achieve this by using respond_with
. Here is an example of how to implement it:
class BlogController < ApplicationController
respond_to :html, :json, :xml
# GET /blogs
# GET /blogs.json
# GET /blogs.xml
def index
@blogs = Blog.all
respond_with(@blogs)
end
end
If you need more control and want to be able to have a few actions that act differently, you can always use a full respond_to block.
In Rails 4.2, respond_with
is no longer included. But you can get it back if you install the responder gem. Once you install it and generate a Rails scaffold, the generator will create controllers using respond_with
instead of respond_to
.
class BlogController < ApplicationController
before_action :set_blog, only: [:show, :edit, :update, :destroy]
respond_to :html, :json
def index
@blogs = Blog.all
respond_with(@blogs)
end
...
end
Format ALL or Format ANY?
If you’d like to specify a respond_to
to render something for all formats while keeping other options untouched, you can always use format.all in the following way:
respond_to do |format|
format.csv { render_csv }
format.all { render_404 }
end
Use format.any if you want to specify a common block of different formats:
def index
@blogs = Blog.all
respond_to do |format|
format.html
format.any(:xml, :json) {
render request.format.to_sym @blogs
}
end
end
All the controller actions need to be tested to make sure they work as intended!
Defining Custom MIME Type
If you need to use a MIME type which isn't supported by default, you can register your own handlers in config/initializers/mime_types.rb
:
Mime::Type.register "text/markdown", :markdown
That's it for this article! Thank you for taking the time to read it!
Be sure to subscribe to our newsletter, we will respond_to with many more future articles!