Créer une API en Ruby on Rails avec la gem Grape - 2 ème partie.

Publié le 10 février 2015 par Théo Delaune | back

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

Nous allons aujourd’hui reprendre la suite de notre précédent article sur la création d’une API minimaliste avec Grape.

La première partie de ce tutoriel est disponible sur notre GitHub.

Actuellement l’API qui est exposée nous propose un CRUD basique :

GET        /api/:version/cars(.:format)
GET        /api/:version/cars/:id(.:format)
POST       /api/:version/cars(.:format)
PUT        /api/:version/cars/:id(.:format)
DELETE     /api/:version/cars/:id(.:format)

Nous aborderons tout d’abord la mise à jour de la gem grape, car son développement est très actif. Je vous invite à suivre toute évolution de cette gem sur son dépôt GitHub.

Nous parlerons également de la mise en place de validations sur les attributs de nos modèles exposés ainsi que d’une authentification basique pour requêter notre API en toute sécurité.

Nous élaborerons une version 2 de notre API qui ne laissera pas le choix à l’utilisateur de spécifier la valeur d’un paramètre d’une voiture, nous le mettrons directement en place au seing de notre API.

Mise à jour de la gem grape

Gemfile

Pour mettre à jour la gem grape dans notre application Rails il suffit de remplacer la ligne correspondante à la gem par celle-ci :

# Gemfile
gem 'grape', '~> 0.10.1'

Une fois la ligne changée il ne reste plus qu’à lancer un bundle install pour mettre à jour le projet.

$ bundle install

Validations

Les validations obligatoires

Nous avons déjà des validations présentes sur notre API. Nous allons étoffer le sujet en abordant des validations plus spécifiques sur certains paramètres.

Pour cet exemple nous allons prendre le cas de la création d’une voiture via l’API.

Actuellement notre création de voiture ressemble à cela :

desc "Create a car"
  params do
    requires :car, type: Hash do
      requires :manufacturer, type: String
      requires :design, type: String
      requires :style, type: String
    end
  end
  post do
    Car.create!(params[:car])
  end

Nous allons restreindre le type de voiture possible en ajoutant un choix limité sur notre attribut :design. Pour ce faire nous allons utiliser le paramètre :values en lui spécifiant deux choix possibles.

Lors de la mise en production d’une telle contrainte sur la validation d’un enregistrement, n’oubliez pas de fournir une documentation à l’utilisateur final de votre API.

...
requires :design, type: String, values: ["tourer", "racing"]
...

Nous pouvons maintenant tester, et nous remarquons que lorsque le paramètre design ne correspond pas à l’un des deux précisés au dessus, nous obtenons un message d’erreur.

Nous allons voir maintenant comment vérifier que la chaîne de caractère saisie pour l’attribut :manufacturer correspond bien à nos attentes.

Pour cela nous allons utiliser une regex grâce au paramètre :regexp. Pour notre exemple nous allons demander à ce que l’attribut :manufacturer soit sous la forme “Cccc”.

Le manufacturer de notre voiture devra donc contenir une majuscule en début de nom.

...
requires :manufacturer, type: String, regexp: /^[A-Z][a-z]+$/
...

Comme pour le paramètre :values nous obtenons une erreur lorsque l’attribut :manufacturer ne correspond pas à notre regex.

Les validations optionnelles

Grape nous permet de spécifier des paramètres optionnels. Le paramètre n’est donc pas obligatoire lors de la validation d’un enregistrement.

Pour ce faire nous allons ajouter un attribut doors en base à notre modèle Car.

# Création d'une migration
$ be rails g migration AddDoorsToCar doors:integer

# Prise en compte de la migration dans notre BDD
$ be rake db:migrate

Ajoutons maintenant notre attribut optionnel à la création d’une voiture.

requires :manufacturer, type: String, regexp: /^[A-Z][a-z]+$/
requires :design, type: String, values: ["tourer", "racing"]
requires :style, type: String
# Notre paramètre optionnel 'doors'
optional :doors, type: Integer
...

Lors du test de la création d’une voiture nous pouvons remarquer que si nous ne spécifions pas l’attribut :doors, le nouvel enregistrement se crée. Mais nous obtenons une valeur null pour l’attribut doors.

{"id":6,"manufacturer":"Volvo","design":"tourer","style":"4x4","created_at":"2015-02-10T14:21:36.297Z","updated_at":"2015-02-10T14:21:36.297Z","doors":null}

Pour lui spécifier une valeur par défaut il est nécessaire de l’inclure dans notre paramètre optionnel :

optional :doors, type: Integer, default: 3

 Conclusion

Nous avons pu en apprendre plus sur les paramètres obligatoires et optionnels ainsi que sur les validateurs values et default. Grape en propose beaucoup plus, je vous invite donc à vous rendre sur la documentation des validations

Authentification

L’utilisation de Basic-Auth

Grape prend en compte de base l’authentification par Basic Auth, c’est ce que nous allons voir ici.

Toutefois cette authentification est à proscrire en production pour son manque de sécurité, car le mot de passe est transmis en clair. Je vous encourage donc, si vous mettez cette méthode en pré-production, de passer par le protocole HTTPS qui permettra de chiffrer le mot de passe.

Cette authentification reste néanmoins pratique en développement ou en pré-production.

Son utilisation reste très simple puisqu’elle consiste en la mise en place d’un nom d’utilisateur et d’un mot de passe.

resource :cars do
  http_basic do |username, password|
    { 'synbioz' => '4p1' }[username] == password
  end

  desc "Return list of cars"
  ...

L’ajout du http_basic à l’intérieur de notre block resource, va nous permettre de pouvoir protéger tout ce qui se trouve au seing de ce block.

Les autres méthodes d’authentification

Il existe bien sûr d’autres méthodes d’authentification on peut, au même titre que l’authentification basique, citer l’authentification par “Digest” qui peut apporter un peu plus de sécurité que l’authentification basique.

Le meilleur moyen pour protéger son application à l’heure actuelle est de suivre les standards et de passer par un provider OAuth2.

Grape nous permet d’utiliser des Middleware personnalisés pour l’authentification. Nous reviendrons dans un prochain article sur la mise en place d’une authentification par token en utilisant un provider OAuth2.

Une v2 ?

Description

Une nouvelle version d’une API est mise en place lors d’un changement majeur dans son fonctionnement. Nous avons créé notre application de telle sorte à pouvoir apporter facilement une nouvelle version à nos utilisateurs.

Le principe aujourd’hui est de choisir le nombre de portes d’une voiture à la place de l’utilisateur. C’est à dire qu’il ne pourra plus spécifier le nombre de porte qu’il souhaite.

Mise en place

Pour ce faire nous allons créer un nouveau fichier pouvant contenir notre API v2.

# Dans car_world_trader/v2/cars.rb
module CarWorldTrader
  module V2
    class Cars < Grape::API
      version 'v2', using: :path
      format :json
      prefix :api

      resource :cars do
        desc "Create a car"
        params do
          requires :car, type: Hash do
            requires :manufacturer, type: String, regexp: /^[A-Z][a-z]+$/
            requires :design, type: String, values: ["tourer", "racing"]
            requires :style, type: String
          end
        end
        post do
          Car.create!(params[:car].merge(doors: 5))
        end
      end
    end
  end
end

Il ne nous reste plus qu’à monter la version 2 de notre API sur notre router personnalisé :

# Dans car_world_trader/base.rb
module CarWorldTrader
  class Base < Grape::API
    mount CarWorldTrader::V1::Cars
    mount CarWorldTrader::V2::Cars
  end
end

Nous pouvons maintenant tester, et nous remarquons que l’enregistrement de la nouvelle voiture contient bien 5 portes.

Tout comme pour la v1 il nous est possible de rajouter d’autres appels API, et de mettre en place une authentification spécifique pour cette partie de l’api.

Conclusion

Aujourd’hui nous avons pu aborder le principe de validation sur les paramètres, la mise en place d’une authentification basique et la mise en place d’une v2.

Dans un prochain article nous nous retrouverons pour approcher l’authentification par ‘OAuth2’ ainsi que la mise en place de messages d’erreurs personnalisés.

Les sources de cet article sont disponibles sur GitHub.


L’équipe Synbioz. Libres d’être ensemble.