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.

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!

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 inde
@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!

Originally published at kolosek.com on March 5, 2018.

More by Alexandra Mladenovic

Topics of interest

More Related Stories