Construire une api cross domain avec Rails

By antho1404 • work • 18 Feb 2013

cross

Construire une api basique en Ruby on Rails, rien de plus simple, un coup de response_with :json et c’est à peu près tout pour du très basique. Seulement voilà, le très basique des fois c’est pas suffisant et dans certains cas il est nécessaire d’avoir une API cross domain et là ça se complique un peu plus…

Le meilleurs moyen est de faire cette API en mettant en place un support du mécanisme  CORS (pour Cross-Origin Resource Sharing).

Pour résumer, cela va permettre d’autoriser ou non certain domaines pour savoir s’il à le droit ou non d’accéder aux ressources du site.

Comment mettre ça en place?

Commençons par créer un petit module que nous allons nommer api_secure.rb qui s’occupera d’autoriser l’accès en modifiant le header de le requête.

module ApiSecure
  def self.included base
    base.send :define_method, "authorize_api_access!" do
      headers["Access-Control-Allow-Origin"] = request.env['HTTP_ORIGIN']
      headers["Access-Control-Allow-Methods"] = "PUT, OPTIONS, GET, DELETE, POST"
      headers['Access-Control-Allow-Headers'] = 'X-CSRF-Token,Content-Type'
    end
  end
end

Ici on autorise donc le domaine qui fait la requêtes (request.env['HTTP_ORIGIN']) à pouvoir faire appel à des méthodes en GET (pour lire), POST (pour créer), PUT (pour modifier) et DELETE (pour supprimer). Notez que si vous utilisez du JSONP vous ne pourrez faire que du GET. Là ça commence à devenir un peu plus intéressant du coup.

On peut voir l’arrivé d’une méthode de type OPTIONS, cette méthode à pour seul but de récupérer des informations sur le communication avec le serveur (ici si on à le droit d’y accéder ou pas).

Nous allons donc dans un premier temps effectuer une requête en OPTIONS qui précisera si oui ou non on peut accéder aux ressources du site.

  match '/api/*other' => "application#authorize", constraints: { method: 'OPTIONS' }
class ApplicationController < ActionController::Base
  include ApiSecure
 
  def authorize
    authorize_api_access!
    render text: ""
  end
end

Libre à vous par la suite de rajouter vos propres contraintes en plus.

Une fois que cette requête nous retourne que tout va bien et qu’on à le droit de récupérer les informations ou de les modifier etc… on peut envoyer notre requête.

Par la suite il nous faut construire notre API, là pour une bonne structure, je vous conseille de faire hériter tous vos contrôleurs d’une classe Base et de mettre tout ça dans un module Api.

Ce qui va donner:

Le fichier base_controller.rb

class Api::BaseController < InheritedResources::Base
  include ApiSecure
  before_filter :authorize_api_access!
  respond_to :json
end

puis vos ressources, prenons l’exemple d’un blog avec des articles et des commentaires

class Api::ArticlesController < Api::BaseController
end
 
class Api::CommentsController < Api::BaseController
  belongs_to :article
end

Par la suite il faut aussi bien configurer ses routes, toujours pareil je vous conseille de mettre tout ça dans un namespace api ça fait plus propre.

MyApp::Application.routes.draw do
  namespace :api do
    resources :articles do
      resources :comments
    end
    match '*other' => "application#authorize", constraints: { method: 'OPTIONS' }
  end
end

Et voilà une API cross domain pour un blog en moins d’une 30ènes de lignes (grâce à un gros coup de main de la gem inherited_resources qui permet de ne pas avoir à écrire toutes les actions de nos controlleurs dans le cas d’une application RESTfull) et là plus de problème avec le JSONP ou des erreurs dans la console javascript du type XMLHttpRequest cannot load http://… Origin http://… is not allowed by Access-Control-Allow-Origin.

Du coup maintenant c’est parti, vous pouvez créer plein de webservices complètement fous.

Tags: , , ,

One Response

  1. On peut aussi utiliser le middleware rack-cors (https://github.com/cyu/rack-cors) qui simplifie énormément cette tâche. Par contre si on l’utilise de paire avec Devise, il faut penser à le mettre plus haut que Warden dans la liste des middleware.

    Merci pour ton ticket en tout cas, c’est toujours chouette de voir des devs qui partagent leur expérience

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>