respond_to et Ruby on Rails

Publié le 18 septembre 2013 par Martin Catty | back

Cet article est publié sous licence CC BY-NC-SA

Dans notre série d’articles sur les bases de Ruby on Rails, explorons aujourd’hui la magie de respond_to.

À quoi sert respond_to ?

Le mécanisme respond_to est présent depuis les toutes premières versions de Ruby on Rails.

Il permet depuis un contrôleur d’offrir des réponses différentes selon le format demandé.

Par exemple si je demande l’affichage de mes utilisateurs en JSON, XML ou HTML, le format de rendu sera différent.

C’est particulièrement pratique si votre application web possède un front (visible en HTML) mais qu’elle agit également comme une API, par exemple au format JSON pour interagir avec une application mobile.

Comment est déterminé le format ?

Prenons un contrôleur tout ce qu’il y a de plus basique:

class ProductsController < ApplicationController
  def index
    @products = Product.all
  end
end

Ruby on Rails utilise 2 moyens pour déterminer le format demandé.

  • Depuis l’extension de l’url (ex: /products.xml)
  • Depuis les entêtes envoyés par le navigateur (entête Accept)

Le premier est prioritaire sur le second, vérifions avec curl:

curl -i -H "Accept: application/json" http://localhost:8000/products.xml

Voici la partie du résultat qui nous intéresse:

Missing template products/index, application/index with {:locale=>[:en], :formats=>[:xml], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder, :coffee]}. Searched in:
  * "/Users/fuse/tmp/respond/app/views"

On voit bien que le format recherché est XML. Ce format n’existe d’ailleurs pas de notre côté.

À noter que le contrôleur n’a pas besoin de respond_to explicite quand la vue est présente, quelque soit le format.

Si nous créons une fichier views/products/index.html.erb, ou views/products/index.xml… le contrôleur rendra automatiquement la vue correspondante au format, autrement nous aurons un Template is missing.

Toutefois dès lors que nous définissons un bloc respond_to, nous devons définir tous les formats acceptés, le mapping ne se fait plus automatiquement avec les vues.

Gestion des formats

Vous aurez remarqué que Rails «connaît» un certain nombre de formats. Ces formats représentent le content-type du fichier, autrement appelé type MIME. Vous pouvez les lister avec Mime::EXTENSION_LOOKUP.

Dans Rails, chaque type est en fait un sous module de MIME. Exemple:

Mime::CSV
=> #<Mime::Type:0x007f8954b5e3b8 @synonyms=[], @symbol=:csv, @string="text/csv">

Si dans une action de votre contrôleur vous n’avez pas de respond_to et que vous demandez par exemple products.foo, c’est le format HTML qui sera utilisé, et ce même si vos entêtes demandent un format qui existe.

C’est tout à fait normal car les entêtes envoyés par le navigateur peuvent contenir à peu près n’importe quoi.

curl -i -H "Accept: application/xml" http://localhost:8000/products.foo renverra donc la liste des produits en HTML, même si le fichier index.xml est présent.

Définir vos propres formats

Définir votre propre format est trivial, un initializer est prévu pour cela, config/initializers/mime_types.rb.

Mime::Type.register "application/vnd.ms-excel", :xls

À partir de là vous pourrez gérer votre format Excel dans votre bloc respond_to. Si vous avez besoin de manipuler le format, celui ci se trouve directement dans la requête (request.format).

Exemple:

  respond_to do |format|
    format.any(:xml, :foo) { render xml: @products }
    format.xls {
      # Charge à notre code d'implémenter le to_xls
      render xls: @products.to_xls
    }
  end

Un mot sur respond_with

Pour simplifier un peu plus Ruby on Rails a introduit le mécanisme respond_with.

Il s’agit de définir au niveau contrôleur les formats auxquels on répond, puis de répondre avec un objet.

class ProductsController < ApplicationController
  respond_to :json, :xml

  def index
    @products = Product.all

    respond_with @products
  end
end

Par défaut Rails utilise le builder intégré, c’est à dire que dans le cas de XML il fera un rendu de @products.to_xml, mais il parfaitement possible de définir sa propre vue.

À noter que les formats qui ne sont pas explicitement spécifiés ne sont pas accessibles. Ici l’URL /products n’affichera pas la version HTML.

L’équipe Synbioz.

Libres d’être ensemble.