DEV Community

Cover image for Everything you needed to know about respond_to πŸ”Ž
Pimp My Ruby
Pimp My Ruby

Posted on • Edited on

Everything you needed to know about respond_to πŸ”Ž

In Rails, creating a controller that returns an HTML view is standard. But did you know that it is possible to return HTML, CSV, JSON, PDF, XML, and many other formats using the respond_to method in the same endpoint ?

That's what we'll explore today!

Table of Contents

  1. What exactly is respond_to?
  2. You can deal with a LOT of formats in the same endpoint
  3. You can set default actions
  4. You can set up customised Mime Types
  5. You can mix variants and formats easily !
  6. Conclusion

What exactly is respond_to?

To answer this question, let's look at a concrete example of using respond_to.

Let's start with this controller, ArticlesController, which has only one method: show.

class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
  end
end
Enter fullscreen mode Exit fullscreen mode

Thanks to Rails' convention, we all know that the show method will return an HTML view. This HTML view is templated via the file app/views/articles/show.html.erb. All these rules are implicit because the default format is HTML.

But imagine that tomorrow you want to be able to return JSON instead of HTML, without changing your logic.

You would use respond_to!

class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    respond_to do |format|
      format.html # will render show.html.erb
      format.json { render json: @article }
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

From now on, you can pass a particular argument to your route: the format.

Let's see how how it works using the routes helpers :

$ bin/rails console
> app.article_path(1) # by default, format is HTML
=> "article/1"
> app.article_path(1, format: :json)
=> "article/1.json"
Enter fullscreen mode Exit fullscreen mode

So, simply by adding the extension .json at the end of your route, it will call for JSON format !

Now that we have the basics, let's look at some advanced use cases for respond_to.


You can deal with a LOT of formats in the same endpoint

As seen earlier, you can return JSON and HTML in the same method. But in fact, there are many other possible formats!

class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @article } # will automatically use #to_json
      format.xml  { render xml: @article.attributes } # will use #to_xml
      format.csv  { render csv: @article.attributes.to_a.to_csv }
      format.pdf  { render pdf: @article.generate_pdf } # let's assume that generate_pdf returns a pdf file
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

With this syntax, you can handle many formats. I have listed here those I have already used in production projects, but there are many others. As many as your application supports MimeTypes.

You can set default actions

Suppose you want to define a default action when the format is not supported. There is the use of format.any that can save you!

class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.any { head :not_acceptable }
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

You can also specify in the any argument the formats you want to consider in your default action:

class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.any(:json, :xml, :csv) { head :not_acceptable }
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

It is important to note that if your route is called with an unsupported format, respond_to will raise an ActionController::UnknownFormat error.

You can set up customised Mime Types

Have you set up your own Mime Type? respond_to can still help you!

# config/initializers/mime_types.rb
Mime::Type.register "application/custom_mime_type", :custom

class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])

    respond_to do |format|
      format.custom { render plain: 'Hello World!' }
    end
  end
end

Enter fullscreen mode Exit fullscreen mode

What is good to note here is that the HTTP response will always have the correct content-type header. In our case, the content-type will be application/custom_mime_type!

You can mix variants and formats easily !

Variants are a very powerful feature in Rails. Suppose you want to do a very simple A/B test:

class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    request.variant = [:a, :b].sample # always variant a or b

    respond_to do |format|
      format.html do |variant|
        variant.a { @ author = nil } # show.html+a.erb
        variant.b { @ author = @article.author } # show.html+b.erb
        variant.none { head :no_content } # but you can still catch if there is no variant
      end
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Here we will only return HTML. Depending on the variant declared in request.variant, we can, for example, assign a value to @author.

We always have access to variant.none to be able to perform processing when no variant has been assigned to the request.


Conclusion

The respond_to method is a powerful tool, allowing you to respond to HTTP requests in various formats such as HTML, JSON, XML, CSV, and PDF. We have seen how respond_to can improve the flexibility of Rails applications by handling multiple formats in the same endpoint and setting default actions for unsupported formats.

I hope this article has made you appreciate the respond_to method. Don't hesitate to subscribe so you don't miss my next breakdown of Ruby and Rails.

Top comments (0)