APIs Web REST: Un tutoriel super-simple

Note : Ce billet de blog est clairement destiné à des développeurs.

Nous sommes tombés récemment sur l’explication très claire de Philip Guo, Professeur Assistant au MIT, sur les APIs Rest. Dans la prochaine version de Koha, une nouvelle version des webservices restful seront disponibles, il nous a donc semblé intéressant de traduire ce billet.

Voici ma tentative d’expliquer les APIs REST (aussi connu sous le nom de services Web REST, APIs Web REST, ou APIs Web RESTful) d’une manière simple, sans jargon.

Voici à mon avis la définition la plus simple : REST est un guide (NdT : « style guide » traduit par « Code typographique » sur Wikipedia) pour les APIs Web.

Mettons que vous êtes en train de développer une API Web pour une version très simple de Facebook. Voici les opérations CRUD (NdT : « Create, Read, Update, Delete » soit « Création, Lecture, Mise à jour, Suppression ») que vous voulez supporter :

  • Créer un nouveau profil utilisateur en saisissant ses nom, emploi et animal de compagnie.
  • Lire le profil de l’utilisateur ayant un nom donné
  • Mettre à jour le profil d’un utilisateur ayant un nom donné, comme par exemple ajouter un nouvel emploi ou animal de compagnie
  • Supprimer un utilisateur donné.

Comment concevriez-vous cette API ?

VERSION 1: LA PLUS SIMPLE API NON-REST

Commençons par le design le plus simple, qui ne suit pas les principes REST. Créez un simple script (disons en Python) avec lequel le client interragira via des requêtes HTTP POST. Appelons-le doStuff.py et hébergeons-le sur le serveur Facebook à l’URL suivante (évidemment c’est une fausse URL, mais elle sera très bien pour l’exemple) :

http://facebook.com/doStuff.py

Pour utiliser votre API pour faire du CRUD, le client doit envoyer des requêtes HTTP POST, soit à partir d’un formulaire HTML, soit depuis son propre script.

Création

Pour créer un nouvel utilisateur Facebook, le client envoit la requête POST suivante :

POST http://facebook.com/doStuff.py
Body: operation=create&name=Philip&job=professor&pet=cat

Votre script Python doStuff.py analyse les paramètres dans le corps de la requête POST, voit que l’opération demandée est ‘create’, crée un nouvel utilisateur avec comme nom ‘Philip’, comme emploi ‘professor’, et comme animal de compagnie ‘cat’, puis insère les données de ce nouvel utilisateur dans la base de données. Jusqu’ici tout va bien.

Lecture

Pour lire le profil de l’utilisateur Philip, le client envoit la requête POST suivante :

POST http://facebook.com/doStuff.py
Body: operation=read&name=Philip

Cette fois votre script doStuff.py analyse les paramètres, voit que l’opération demandée est ‘read’, lit l’entrée en base de données pour le nom Philip, et retourne les données JSON suivante à l’utilisateur :

{« name »: « Philip », « job »: « professor », « pet »: « cat »}

Mise à jour

Pour mettre à jour l’emploi de Philip, une fois encore le client envoit une requête POST à la même URL avec des paramètres légèrement différents:

POST http://facebook.com/doStuff.py
Body: operation=update&name=Philip&job=cat_herder

Philip est maintenant un éleveur de chat.

Suppression

Enfin, pour supprimer Philip de la base de données:

POST http://facebook.com/doStuff.py
Body: operation=delete&name=Philip

Récapitulatif de la version 1

Cette API simple fonctionnera, mais quels sont ses défauts ?

– Nous avons surchargé une simple URL http://facebook.com/doStuff.py pour effectuer 4 actions différentes. C’est un peu comme créer une API avec une unique fonction nommée doStuff() qui effectue différentes actions en fonction de ses paramètres. Pas très élégant.
– L’infrastructure web ne mettra en cache aucune requête puisqu’elle sont toutes des requêtes POST. L’opération de lecture peut être mise en cache mais elle ne l’est pas. (Si nous avions utilisé GET à la place, cela n’aurait pas été sûr car les opérations qui modifient la base de données ne doivent pas être mises en cache.)

VERSION 2: UNE MEILLEURE API MAIS TOUJOURS PAS REST

OK maintenant séparons notre API en quatre scripts Python différents, chacun implémentant une partie de CRUD.

Création

Pour créer un nouvel utilisateur Facebook, le client envoit la requête
POST suivante:

POST http://facebook.com/createUser.py
Body: name=Philip&job=professor&pet=cat

Le script createUser.py analyse les paramètres du corps de la requête POST, crée un nouvel utilisateur avec le nom ‘Philip’, l’emploi ‘professor’, et l’animal de compagnie ‘cat’, puis insère les données de ce nouvel utilisateur dans la base de données.

Contrairement à doStuff.py dans la version 1, le client n’a plus besoin d’envoyer un paramètre ‘operation=create’ à createUser.py, puisque sa seule fonction est de créer de nouveaux utilisateurs. Cela simplifie la liste des paramètres.

Lecture

Pour lire le profil de l’utilisateur Philip, le client envoit la requête GET suivante (qu’il est maintenant possible de mettre en cache):

GET http://facebook.com/readUser.py?name=Philip

readUser.py analyse les paramètres de l’URL, lit l’entrée en base de données pour le nom Philip, et retourne des données JSON:

{« name »: « Philip », « job »: « professor », « pet »: « cat »}

Mise à jour

Pour mettre à jour l’emploi de Philip, le client envoit une requête POST à l’url suivante:

POST http://facebook.com/updateUser.py
Body: name=Philip&job=cat_herder

Philip est maintenant devenu (encore) un éleveur de chat.

Suppression

Enfin, pour supprimer Philip de la base de données, le client envoit une requête POST à l’URL suivante:

POST http://facebook.com/deleteUser.py
Body: name=Philip

Récapitulatif de la version 2

Cette version s’améliore par rapport à la version 1, puisqu’il y a maintenant des scripts dédiés à chaque opération CRUD. Les URLs ressemblent maintenant à des noms de fonctions, qui eux-mêmes ressemblent à des verbes :

http://facebook.com/createUser.py http://facebook.com/readUser.py
http://facebook.com/updateUser.py http://facebook.com/deleteUser.py

Cette API fonctionnera parfaitement bien, mais d’une certaine façon ne semble pas tellement « Web-like ». Pourquoi ?

Quand nous pensons au Web, nous ne pensons pas à chaque URL comme faisant référence à un verbe, mais plutôt à un nom. Par exemple, l’URL suivante fait référence à un fichier HTML :

http://pgbovine.net/rest-web-api-basics.htm

Et cette URL fait référence à un fichier image :

http://pgbovine.net/cs243-teaching.jpg

Fichiers HTML, fichiers image, fichiers vidéo, et autre ressources Web sont tous des noms, pas des verbes. Alors comment pouvons-nous construire notre API Facebook pour qu’elle ait l’air d’opérer sur des noms, afin de la rendre plus cohérente avec les conventions du Web existantes? C’est ici que REST entre en jeu.

VERSION 3: ENFIN UNE API REST

Voici une API REST simple pour nos quatre opérations CRUD :

Création

Pour créer un nouvel utilisateur Facebook, le client envoit une requête POST à l’URL suivante:

POST http://facebook.com/users/
Body: name=Philip&job=professor&pet=cat

Wouah, qu’est ce qui se passe ici ?!? On dirait que ‘users/’ est juste un nom de répertoire, à en juger par son URL. C’est exactement ce que l’on veut. Conceptuellement, ‘users/’ est un « répertoire » contenant une collection de tous les utilisateurs Facebook.

Mais comment pouvons-nous envoyer une requête POST à un répertoire ? En fait, le fait de se rendre à cette URL exécute un script en coulisses. Un framework d’application Web s’occupe de la correspondance magique entre les URLs et les scripts de sorte que n’importe quelle URL peut exécuter un script.

Traduit en français, la requête ci-dessus se lit comme ça: « POSTe une nouvelle ressource dans la collection ‘users/’ sur http://facebook.com/ avec ‘name=Philip’, ‘job=professor’, et ‘pet=cat' ». En d’autres termes, crée un nouvel utilisateur dans la collection ‘users/’

Lecture

Pour lire le profil de Philip, le client envoit la requête GET suivante :

GET http://facebook.com/users/Philip

Wouah, qu’est ce qui se passe? Traduit en français, cette requête se lit « GET (Récupère) la ressource Philip dans la collection ‘users/’ sur http://facebook.com ». En d’autres termes, c’est demander les données du profil pour l’utilisateur Philip. Le bon script s’exécute comme par magie et retourne les données JSON suivantes :

{« name »: « Philip », « job »: « professor », « pet »: « cat »}

Puisque nous utilisons une requête GET, c’est comme si on demandait simplement une ressource nommée Philip de la même façon que si on demandait un fichier HTML. Mais la magie ici est qu’un script est en fait exécuté pour générer dynamiquement des données JSON pour le client.

Mise à jour

Pour mettre à jour l’emploi de Philip, le client envoit la requête PUT à l’URL suivante :

PUT http://facebook.com/users/Philip
Body: job=cat_herder

Philip est maintenant devenu (encore une fois) un éleveur de chat.

En français, cela donne « PUT (Met) les données ‘job=cat_herder’ dans la ressource ‘Philip’ dans la collection ‘users/' ».

(Pour simplifier, HTTP POST peut aussi fonctionner ici à la place de PUT.)

Suppression

Enfin, pour supprimer Philip de la base de données, le client envoit la requête HTTP DELETE (rarement utilisée) :

DELETE http://facebook.com/users/Philip

En français, cela donne « DELETE (Supprime) la ressource Philip de la collection ‘users/' ».

Récapitulatif de la version 3

Comparé à l’API non-REST de la version 2, cette API REST semble plus concise et élégante, puisque le client visite des ressources Web (noms) plutot que de faire des appels de fonctions. Notez que les verbes viennent en fait du type de requête HTTP (POST, GET, PUT, DELETE), qui opèrent sur les noms (URLs). Aussi, la structure hiérarchique des URLs REST basées sur des noms correspond davantage au schéma de votre base de données que la version 2 basée sur des verbes.

Une dernière manière de penser à leur différence est que la version 2 (non-REST) est comme une API orientée fonction (procédurale), alors que la version 3 (REST) est comme une API orientée objet. Au final, les deux APIs peuvent accomplir les mêmes tâches. REST est simplement un guide pour une convention qui est maintenant populaire sur le Web. C’est à vous de décider si vous voulez suivre ce guide ou non.

Share

Un Commentaire

  1. Vraiment super explications, simples et vulgarisées ! Merci

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *