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.allend
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 } endend
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, :xmlend
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) endend
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
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 } endend
All the controller actions need to be tested to make sure they work as intended!
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.