REST

REST est un style d’architecture pour les systèmes hypermédia distribués.

En 2000, Roy Fielding a formalisé l’architecture REST dans le chapitre 5 de sa thèse de doctorat. En 2007, Leonard Richardson et Sam Ruby présentent ROA (Resource Oriented Architecture) dans leur ouvrage RESTful Web Services. ROA est devenu depuis le modèle d’application de REST pour les API Web.

Le modèle REST a été conçu au moment de l’évolution de HTTP vers sa version 1.1 afin d’assurer les évolutions nécessaires du World Wide Web dans les années 1990. HTTP est ainsi le premier protocole basé sur une architecture REST.

REST est le modèle d’architecture du Web ! Cela ne signifie pas que l’ensemble du Web se conforme au modèle décrit par REST mais cela signifie que HTTP a été conçu selon ce modèle (principalement pour des raisons de scalabilité et de fiabilité). Dans ce chapitre nous prendrons donc HTTP comme exemple pour illustrer notre propos, mais il faut garder à l’esprit que REST est un modèle plus général.

Depuis 2007, beaucoup de développeurs Web ont voulu créer des applications RESTful… sans réellement comprendre ce que REST signifie et implique. Il en a résulté une tour de Babel d’articles et de posts sur le Web (voire de publication d’ouvrages) expliquant ce que devait être une application RESTful… mais sans véritable rapport avec REST voire en étant en complète contradiction avec le modèle d’architecture auquel les auteurs souhaitaient se conformer.

La ressource

REST est un style d’architecture orienté ressource. Mais qu’est-ce qu’une ressource ?

Any information that can be named can be a resource

—Roy Fielding - Thèse de doctorat

Dans le domaine du Web, la notion d’adressabilité (addressability) vient renforcer celle de nommage. Une ressource Web est donc quelque chose qui est adressable à partir d’une URI.

Par exemple, cette page est adressable à partir d’une URI, elle est donc une ressource Web.

Une ressource Web peut être accessible à partir de plusieurs URI. Une bonne pratique consiste à considérer qu’une des URI est l’URI canonique.

Par exemple une ressource peut être constituée par la liste des modules d’une formation. Elle peut être adressable à partir d’une URI précise mais également d’une URI plus volatile comme celle donnant la liste des modules pour la formation en cours. Ces deux URI désignent la même ressource pendant une période :

Nous avons vu dans les cas d’utilisation de HTTP, que la canonicalisation pouvait se traduire par une redirection avec un code statut 307 (Temporary Redirect) ou par l’en-tête de réponse Content-Location.

URI informative ou URI opaque ?

Concevoir des URI descriptives et hiérarchiques semble a priori une bonne pratique. Par exemple, si l’on considère cette URI :

La structure de cette URI est porteuse d’information et le lecteur comprend facilement qu’elle suit un modèle du type :

http://formation.fr/{ville}/{spécialité}/{module}/{année}

Cette bonne pratique ne relève absolument pas d’une contrainte de l’architecture REST. Il s’agit souvent d’une erreur d’interprétation des développeurs de penser que REST repose avant tout sur la conception d’une API basée sur des chemins d’URI. Une URI est simplement un nom donné à une ressource. Pour notre exemple, l’URI suivante est tout aussi valide :

Dans une architecture REST, les noms ne servent qu’à désigner les ressources. Il n’y a aucune règle particulière à connaître et à partager sur la signification de ces noms. Pour le Web, les URI sont la plupart du temps imposées par le serveur mais le client n’a pas à connaître les règles qui prévalent à leur construction. Cela donne beaucoup plus de liberté au serveur si son évolution implique de formaliser les URI différemment. On dit ainsi qu’une URI doit être opaque pour le client.

On trouve malheureusement énormément de littératures sur les soi-disantes règles à respecter dans la façon de créer des URI pour des API RESTful. On peut prendre comme exemple l’ouvrage de Mark Massé : REST API Design Rulebook. Il nous explique ainsi qu’une URI désignant un document doit se terminer par un nom au singulier et une URI désignant une collection doit se terminer par un nom au pluriel. Ces règles stylistiques sont certes utiles pour améliorer la lisibilité mais elles n’ont aucun rapport avec REST. Pire, elles véhiculent l’idée qu’un client pourrait tirer partie de ces règles pour deviner les URI des ressources du serveur.

Ressource contre RPC

Il existe une autre grande famille de modèles d’architecture pour les échanges entre processus : le RPC (Remote Procedure Call). L’idée générale est de permettre d’invoquer une procédure à travers un réseau pour la faire exécuter sur un serveur et récupérer le résultat par le client.

Il existe une différence de perspective entre une approche RPC et une approche orientée ressource telle que REST. Avec le RPC, la conception du service se focalise sur le traitement et sur l’appel à une procédure comme on le ferait dans un langage impératif. Dans une approche orientée ressource, la conception du service se focalise sur l’information et on tend à uniformiser les opérations disponibles pour manipuler cette information (les méthodes HTTP GET, PUT, POST, DELETE et PATCH en sont une très bonne illustration). Si l’approche orientée ressource peut sembler plus restrictive dans un premier temps, elle est en fait beaucoup plus simple à implémenter et beaucoup plus facile à généraliser.

Ainsi, une URI doit représenter une ressource et non pas une procédure.

Même si l’utilisation d’URI informatives n’est pas une exigence de REST, elle sont parfois utiles pour détecter une API orientée RPC. Le jeu d’URI suivant n’est pas correct du point de vue d’une architecture orientée ressources :

Chacune de ces URI semble désigner une action (inscrire, désinscrire, valider) pour un individu.

La distinction entre orienté ressource et RPC est fondamentale. Pour HTTP qui est orienté ressource, l’action est portée par la méthode employée dans la requête (GET, POST, PUT, DELETE…) pas par l’URI.

La représentation

Dans une architecture REST, un client et un serveur n’échangent pas une ressource mais des représentations, c’est-à-dire un ensemble de données relatives à l’état courant d’une ressource. Ainsi, on peut dire que, du point de vue du client, la nature d’une ressource est inconnue puisqu’il ne manipule que des noms et des représentations. Avec HTTP, lorsqu’un client veut créer une ressource sur le serveur en utilisant la méthode PUT, il ne fournira qu’une représentation de ce qu’il souhaite pour l’URI utilisée. Le serveur est libre d’utiliser tout ou partie de l’information transmise par le client ou même l’enrichir pour générer la ressource. Le serveur peut également choisir les formats de représentation disponibles lors de l’accès à la ressource avec la méthode GET. Parmi ces formats, il peut choisir de supporter ou non le format utilisé par le client au moment de la création.

Une même ressource Web peut avoir plusieurs représentations : une image JPEG, une page HTML, un document XML, un document JSON… Si le serveur dispose de plusieurs représentations d’une ressource, il retourne celle qui correspond le mieux aux capacités du client. Pour cela le client doit fournir ses préférences. C’est ce que l’on appelle la négociation de contenu proactive. Nous avons vu que cette négociation se fait en HTTP grâce à des en-têtes particuliers.

Il faut garder à l’esprit que la négociation de contenu ne se limite pas uniquement au format de données (HTML, XML, JSON…). La négociation peut également porter sur la langue utilisée dans la représentation.

Les contraintes REST

Dans le chapitre 5 de sa thèse, Roy Fielding présente les contraintes d’une architecture REST. En effet, ce modèle d’architecture n’est en fait constitué que de six contraintes (la dernière étant même optionnelle).

  • client/serveur

  • interface uniforme

  • sans état (stateless)

  • mise en cache (caching)

  • layered system

  • code-on-demand

Client/Serveur

Cette contrainte ne semble pas en être une tellement nous sommes habitués à interagir avec des réseaux informatiques basés sur ce modèle. Pourtant une approche client/serveur n’a rien de « naturelle » et il s’agit bien de contraindre les rôles que peuvent jouer les éléments d’un réseau. Dans cette approche un élément du réseau peut être client et/ou serveur. En tant que serveur, il dispose d’un ensemble d’information qu’il peut transmettre à la demande à d’autres éléments du réseau qui sont appelés ses clients.

Il existe des modèles de réseau qui ne sont pas basés sur le modèle client/serveur. Par exemple, les architectures de micro-services reposent sur un bus de données dans lequel un service peut écrire ou lire de l’information sans avoir connaissance des autres services.

Interface uniforme

Si chaque élément d’un réseau dispose de sa propre interface, un client devra s’adapter à chaque serveur vers lequel il désire émettre une requête. Pour éviter ce problème, il est nécessaire de définir une interface claire et minimaliste à laquelle chaque serveur doit se conformer. Pour HTTP, cette interface est composée des méthodes (GET, HEAD, PUT, POST, DELETE…), des URI et de la possibilité d’échanger des représentations en y associant des méta-informations (notamment sur le type de données).

La contrainte de l’interface uniforme entraîne de facto le rejet du style RPC (Remote Procedure Call) pour les échanges dans une architecture REST. Contrairement au RPC, une interface uniforme fait porter la sémantique du service sur le contenu de la représentation et dans l’enchaînement des requêtes.

Sans état (stateless)

Dans une architecture client/serveur, un serveur ne peut pas mémoriser l’ensemble des échanges qu’il a avec ses clients. Pour qu’un réseau soit scalable, le client et le serveur ne doivent pas dépendre d’autres informations que celles contenues dans la requête. La requête est auto-suffisante pour que le serveur puisse la traiter.

Une pratique (trop) courante dans le développement d’application Web est de définir une session côté serveur qui stocke temporairement des informations issues de requêtes précédentes. Ce mécanisme de session est le plus souvent géré grâce à des Cookies. Cette pratique va à l’encontre de la contrainte d’un échange sans état.

Cette contrainte est probablement celle qui perturbe le plus les habitudes des développeurs. Il est tellement plus simple de concevoir des services en utilisant une session utilisateur côté serveur que cette contrainte paraît excessive. Ce que les développeurs oublient, ce sont les complications créées par un service avec état (stateful) en exploitation : gestion de la montée en charge (ajout de serveur et répartition de charge), rigidité dans l’utilisation du service (le client est supposé suivre une succession d’étapes pour remplir sa session), bugs dus à des données manquantes et/ou des pertes de session.

Mise en cache (caching)

Un serveur a la responsabilité de fournir au client des informations sur la possibilité de conserver une représentation et de la considérer comme valide pendant un certain laps de temps. Cette contrainte est malheureusement très largement sous-estimée par les développeurs d’API Web alors qu’elle permet d’améliorer considérablement les performances d’une application. Il ne faut pas oublier que la mise en cache est un mécanisme qui est largement exploité par les éléments intermédiaires du Web comme les proxies pour améliorer l’efficacité des échanges.

Nous avons déjà abordé les grands principes de la gestion du cache pour HTTP.

Layered system

Un réseau doit être stratifié en couches. Un élément du réseau ne peut dialoguer qu’avec ses voisins immédiats, c’est-à-dire les composants appartenant aux couches adjacentes. Ainsi si un client veut envoyer une requête à un serveur qui n’est pas localisé dans une couche adjacente, il va devoir passer par des intermédiaires. Pour le Web, les intermédiaires sont les proxies et les gateways.

Code on demand

Il s’agit d’une contrainte optionnelle de l’architecture REST. Un serveur peut étendre les fonctionnalités d’un client en lui proposant de télécharger du code exécutable. Le code-on-demand est très largement utilisé dans le Web pour les humains avec notamment le support de JavaScript pour étendre les fonctionnalités du navigateur Web.

Hypermédia

Dans sa thèse de 2000, Roy Fielding cite une contrainte d’interface :

hypermedia as the engine of application state

Cette contrainte est devenue célèbre et est parfois abrégée en HATEOAS. Alors que le lecteur s’attend à une explication, Roy Fielding ne revient pas directement et explicitement sur cette contrainte. Depuis, la notion d’hypermédia est associée à REST mais est trop rarement explicitée.

Un site Web ou une API Web n’est pas qu’un ensemble d’URI permettant d’identifier des ressources et de les manipuler à travers des représentations. Les représentations sont également des hypermédias : elles contiennent des liens vers d’autres ressources.

Les formats hypermédias

Lorsque l’on prend l’exemple du Web, le format hypermédia le plus courant pour une représentation est le HTML. Avec les balises a, form et link, il est possible d’inclure des liens afin de guider le client dans les choix d’interaction autorisés par le serveur. Lorsque l’on dit couramment que l’on navigue ou que l’on surfe sur le Web, on décrit les actions rendues possibles par le fait que HTML est un format hypermédia.

Pour l’implémentation d’API Web, on pourra préférer d’autres langages que HTML : généralement XML et JSON. En effet, ces langages sont plus neutres que HTML qui ajoute une sémantique de structuration documentaire (et JSON est bien évidemment plus proche d’un langage de programmation). Cependant, XML et JSON ne sont pas par défaut des langages hypermédias. Il n’existe pas de support natif pour les liens dans ces formats. Soit le client doit s’adapter pour comprendre la manière dont le serveur inclut le support de l’hypermédia, soit il faut avoir recours à des extensions. Pour le XML, le support de l’hypermédia peut se faire grâce à l’extension XLink et pour le JSON grâce au format HAL (Hypertext Application Language).

Actuellement, JSON semble être le langage naturel pour la représentation des ressources dans une API Web. Quiconque comprend les apports de l’hypermédia dans la mise en place de telles API Web sait que l’utilisation de JSON est un leurre car il donne une certaine facilité aux développeurs mais en sacrifiant le support de l’hypermédia qui est justement au cœur de REST.

Les apports de l’hypermédia

L’hypermédia a pour objectif de déléguer au serveur la responsabilité de guider le client dans ce qu’il est possible de réaliser. Au client de choisir ce qu’il souhaite faire parmi les possibilités qui lui sont proposées. Cette pratique permet au client de ne connaître que le minimum requis pour interagir avec le serveur. L’hypermédia n’est pas une technologie ni une méthode de développement. Il est le résultat de la conception d’un service respectant les contraintes REST.

Ainsi, grâce à l’hypermédia, un client ne sera pas dépendant des règles de construction des URI. Il doit connaître un point d’entrée du service mais le serveur doit lui fournir dans les représentations (et/ou grâce à l’en-tête Link) les URI à utiliser pour accéder aux autres ressources. Cette pratique permet un découplage entre l’implémentation du client et les choix d’implémentation et de déploiement du service.

En spécifiant au client les liens possibles dans ses réponses, le serveur définit un workflow qui décrit les états successifs du client. Avec l’hypermédia, il devient possible de modéliser des interactions client/serveur complexes par un enchaînement de requête/réponse. On peut donc bien dire, à la suite de Roy Fielding, que l’hypermédia permet de décrire une machine à état d’une application formée par l’interaction entre un client et un serveur.

A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media types that are appropriate for the intended audience (i.e., expected to be understood by any client that might use the API). From that point on, all application state transitions must be driven by client selection of server-provided choices that are present in the received representations or implied by the user’s manipulation of those representations. The transitions may be determined (or limited by) the client’s knowledge of media types and resource communication mechanisms, both of which may be improved on-the-fly (e.g., code-on-demand).

—Roy Fielding - REST APIs must be hypertext-driven

REST : ça veut dire quoi ?

REST est un acronyme pour Representational State Transfer.

Il s’agit bien d’une architecture dont l’objectif et de transférer des états sous la forme de représentation.

Les API Web

Les API Web (appelées également Web service RESTful) ne sont fondamentalement pas différentes des sites Web traditionnels. Elles obéissent aux mêmes contraintes d’architecture. La différence est une différence d’utilisation.

Dans le cas d’un site Web, le travail du programme client consiste à présenter l’information à un être humain. L’interprétation des données est donc de la responsabilité de l’être humain et c’est bien lui qui va sélectionner tel ou tel lien hypermédia selon ses objectifs ou ses goûts.

Pour une API Web, on attend le plus souvent du logiciel client qu’il sache, non seulement exploiter les URI et invoquer correctement les méthodes HTTP, mais également adapter son comportement en fonction du contenu de l’information échangée. Cette capacité à analyser le contenu des représentations doit être favorisée par le serveur qui à la charge de définir la sémantique de protocole et la sémantique applicative (ces notions sont empruntées à l’ouvrage RESTful Web APIs - O’Reilly 2013).

Sémantique de protocole

Il s’agit de la compréhension que l’on peut avoir du service par la connaissance du format des URI et par les méthodes HTTP autorisées sur ces URI. Généralement cette compréhension est suffisante pour un être humain afin de tester un service. Par contre l’implémentation d’un client basé uniquement sur la sémantique de protocole est possible mais reste limitée, notamment pour prendre en compte les évolutions du service. Un navigateur Web est un bon exemple de logiciel qui se limite à la sémantique de protocole en utilisant principalement le support hypermédia du HTML pour présenter à un utilisateur la liste des liens possibles.

Sémantique applicative

La sémantique applicative porte sur la signification des représentations échangées et sur la signification des liens entre ces représentations. Si une API Web définit un lien entre deux ressources comme étant de type caused by, cela n’a a priori aucune signification pour un logiciel client. La sémantique applicative est généralement décrite dans une documentation en ligne. Des formats de description traitables par une machine commencent à voir le jour et à être utilisés (par exemple le JSON-LD).

Concevoir une API Web digne de ce nom nécessite donc de réduire au minimum le niveau de compréhension du client nécessaire pour l’utiliser. Minimiser l’effort nécessaire à la maîtrise de la sémantique de protocole est la tâche la plus simple si l’on prend la peine d’utiliser un format hypermédia de représentation (HTML, HAL+JSON, HAL+XML). En ce qui concerne la sémantique applicative, le plus efficace consiste à réutiliser ce qui a déjà été défini par d’autres. À ce titre, le recours à des formats proches des microformats ou des schémas Web permettent de créer un standard de fait pour la représentation de certaines ressources. Enfin, pour une API Web qui souhaiterait utiliser HTML comme format hypermédia de représentation, il existe trois techniques très simples pour introduire une sémantique applicative en HTML : les microformats, les microdata ou RDFa.

Il est également possible de définir son propre type de contenu en définissant un nouveau type MIME. Les formats de type application/vnd.xxxx désignent des formats spécifiques qui ne sont pas forcément déclarés dans le registre du IANA. L’avantage de créer son propre type de contenu est que l’on peut définir simultanément la sémantique de protocole et la sémantique applicative (en fournissant le format autorisé des représentations).

Références

Sites Web & articles

REST cookbook

http://restcookbook.com/

Une étude de cas très complète (commander un café en REST)

https://www.infoq.com/articles/webber-rest-workflow

Le modèle de maturité de Richardson pour une approche REST

https://martinfowler.com/articles/richardsonMaturityModel.html

Livres

Architectural Styles and the Design of Network-based Software Architectures

Roy Fielding - University of California, Irvine 2000 (La thèse de doctorat incluant la présentation de REST)

RESTful Web APIs

Leonard Richardson, Mike Amundsen, Sam Ruby - O’Reilly 2013

REST in Practice

Jim Webber, Savas Parastatidis, Ian Robinson - O’Reilly Media 2010

RESTful Java Web Services - Second Edition

Jobinesh Purushothaman - Packt Publishing 2015

Applications de démo

REST bookmarks

Une application de démo de gestion de marque-pages (le code source en Java est disponible sur GitHub)

You type it, we post it!

Une application de démo issue de l’ouvrage RESTful Web APIs (le code source pour Node.js est disponible sur GitHub)

Exercices

Exploration d’un donjon

On désire réaliser un jeu d’exploration de donjon dans lequel le joueur visite un ensemble de salles. Chaque salle communique avec une autre par une porte. Une salle peut contenir un nombre quelconque de portes.

Vous devez proposer une API Web de ce service afin de permettre à l’utilisateur de parcourir le donjon. Chaque salle est décrite par un court texte. Si le client le supporte, il est possible d’obtenir une illustration pour certaines salles.

Un service de paiement en ligne

Étant donné le workflow suivant décrivant la création et le paiement d’une transaction bancaire :

Diagramme d'activité de paiement

Les activités sont :

créer une transaction

Création d’une transaction auprès de l’organisme bancaire identifiant les coordonnées bancaires du créditeur et le montant à payer.

autoriser paiement

Le débiteur indique ses informations de paiement (numéro de carte de paiement, date limite de validité de la carte et cryptogramme de sécurité). Le client effectue une demande auprès de l’organisme bancaire pour savoir si la transaction est autorisée. L’organisme bancaire fournit un numéro d’autorisation permettant d’identifier la transaction.

capturer paiement

Si l’autorisation s’est bien passée, le client réalise une capture, c’est-à-dire qu’il demande à l’organisme bancaire d’enregistrer le paiement et d’effectuer le transfert du compte du débiteur vers le compte du créditeur.

Décrivez la réalisation d’une API Web par des exemples de requêtes/réponses HTTP.

Décrivez également des scénarios alternatifs :

  • L’autorisation échoue car les informations bancaires fournies sont incorrectes

  • L’autorisation échoue car le client ne dispose pas du crédit suffisant sur son compte.

Un service de réservation de billet

On désire mettre en ligne un service de réservation de billets de concert. Ce service, accessible grâce à une API Web, sera utilisé par des applications de smartphone. Le service ne gère pas de base de données des utilisateurs : un utilisateur est simplement identifié par un pseudo au moment de la réservation.

Les cas d’utilisation définis sont :

  • L’utilisateur consulte la liste des concerts disponibles

  • L’utilisateur réserve une place pour un concert avec un pseudo

  • L’utilisateur annule sa réservation

  • L’utilisateur confirme sa réservation

  • Le gestionnaire du site consulte la liste des réservations confirmées pour un concert.

Attention, un utilisateur qui a confirmé sa réservation ne peut plus l’annuler !

Décrivez une API Web par des exemples de requêtes/réponses HTTP permettant de réaliser les cas d’utilisation ci-dessus.

Le service d’achat en ligne

On désire mettre en ligne un catalogue de produits parmi lesquels un client peut constituer un panier d’achat.

Les cas d’utilisation définis sont :

  • L’utilisateur consulte la liste des articles du catalogue

  • L’utilisateur consulte le détail d’un produit

  • L’utilisateur ajoute un produit à son panier d’achat

  • L’utilisateur supprimer un produit de son panier d’achat

  • L’utilisateur valide son panier pour le paiement en ligne

Décrivez la réalisation d’une API Web par des exemples de requêtes/réponses HTTP.