L’architecture REST expliquée en 5 règles

23 , Permalink

REST (Representational State Transfer) ou RESTful est un style d’architecture permettant de construire des applications (Web, Intranet, Web Service). Il s’agit d’un ensemble de conventions et de bonnes pratiques à respecter et non d’une technologie à part entière. L’architecture REST utilise les spécifications originelles du protocole HTTP, plutôt que de réinventer une surcouche (comme le font SOAP ou XML-RPC par exemple).

  • Règle n°1 : l’URI comme identifiant des ressources
  • Règle n°2 : les verbes HTTP comme identifiant des opérations
  • Règle n°3 : les réponses HTTP comme représentation des ressources
  • Règle n°4 : les liens comme relation entre ressources
  • Règle n°5 : un paramètre comme jeton d’authentification

Les 5 règles à suivre pour implémenter REST

Règle n°1 : l’URI comme identifiant des ressources

REST se base sur les URI (Uniform Resource Identifier) afin d’identifier une ressource. Ainsi une application se doit de construire ses URI (et donc ses URL) de manière précise, en tenant compte des contraintes REST. Il est nécessaire de prendre en compte la hiérarchie des ressources et la sémantique des URL pour les éditer :

Quelques exemples de construction d’URL avec RESTful :
Liste des livres

NOK : http://mywebsite.com/book
OK : http://mywebsite.com/books

Filtre et tri sur les livres

NOK : http://mywebsite.com/books/filtre/policier/tri/asc
OK : http://mywebsite.com/books?fitlre=policier&tri=asc

Affichage d’un livre

NOK : http://mywebsite.com/book/display/87
OK : http://mywebsite.com/books/87

Tous les commentaires sur un livre

NOK : http://mywebsite.com/books/comments/87
OK : http://mywebsite.com/books/87/comments

Affichage d’un commentaire sur un livre

NOK : http://mywebsite.com/books/comments/87/1568
OK : http://mywebsite.com/books/87/comments/1568

En construisant correctement les URI, il est possible de les trier, de les hiérarchiser et donc d’améliorer la compréhension du système.

L’URL suivante peut alors être décomposée logiquement :

http://mywebsite.com/books/87/comments/1568 => un commentaire pour un livre
http://mywebsite.com/books/87/comments => tous les commentaires pour un livre
http://mywebsite.com/books/87 => un livre
http://mywebsite.com/books => tous les livres

Règle n°2 : les verbes HTTP comme identifiant des opérations

La seconde règle d’une architecture REST est d’utiliser les verbes HTTP existants plutôt que d’inclure l’opération dans l’URI de la ressource. Ainsi, généralement pour une ressource, il y a 4 opérations possibles (CRUD) :

  • Créer (create)
  • Afficher (read)
  • Mettre à jour (update)
  • Supprimer (delete)

HTTP propose les verbes correspondant :

  • Créer (create) => POST
  • Afficher (read) => GET
  • Mettre à jour (update) => PUT
  • Supprimer (delete) => DELETE

Exemple d’URL pour une ressource donnée (un livre par exemple) :

Créer un livre

NOK : GET http://mywebsite.com/books/create
OK : POST http://mywebsite.com/books

Afficher

NOK : GET http://mywebsite.com/books/display/87
OK : GET http://mywebsite.com/books/87

Mettre à jour

NOK : POST http://mywebsite.com/books/editer/87
OK : PUT http://mywebsite.com/books/87

Supprimer

NOK : GET http://mywebsite.com/books/87/delete
OK : DELETE http://mywebsite.com/books/87

Le serveur peut renvoyer les opérations acceptées sur une ressource via l’entête HTTP Allow.

Règle n°3 : les réponses HTTP comme représentation des ressources

Il est important d’avoir à l’esprit que la réponse envoyée n’est pas une ressource, c’est la représentation d’une ressource. Ainsi, une ressource peut avoir plusieurs représentations dans des formats divers : HTML, XML, CSV, JSON, etc.

C’est au client de définir quel format de réponse il souhaite reçevoir via l’entête Accept. Il est possible de définir plusieurs formats.

Quelques exemples :

Réponse en HTML

GET /books
Host: mywebsite.com
Accept: text/html

Réponse en XML

GET /books
Host: mywebsite.com
Accept: application/xml

Règle n°4 : les liens comme relation entre ressources

Les liens d’une ressource vers une autre ont tous une chose en commun : ils indiquent la présence d’une relation. Il est cependant possible de la décrire afin d’améliorer la compréhension du système. Pour expliciter cette description et indiquer la nature de la relation, l’attribut rel doit être spécifier sur tous les liens. Ainsi l’IANA donne une liste de relation parmi lesquelles :

  • contents
  • edit
  • next
  • last
  • payment
  • etc.

La liste complète sur le site de l’IANA : http://www.iana.org/assignments/link-relations/link-relations.xml

On peut alors parler d’hypermedias.

Exemple de réponse en XML d’une liste paginée de livres :

<?xml>
<search>
  <link rel="self" title="self" href="http://mywebsite.com/books?q=policier&page=1&c=5" />
  <link rel="next" title="next" href="http://mywebsite.com/books?q=policier&page=2&c=5" /> 
  <link rel="last" title="last" href="http://mywebsite.com/books?q=policier&page=4&c=5" /> 
  <book>
     //...
  </book>
</search>

Règle n°5 : un paramètre comme jeton d’authentification

C’est un des sujets les plus souvent abordé quand on parle de REST : comment authentifier une requête ? La réponse est très simple et est massivement utilisée par des APIs renommées (Google, Yahoo, etc.) : le jeton d’authentification.

Chaque requête est envoyée avec un jeton (token) passé en paramètre $_GET de la requête. Ce jeton temporaire est obtenu en envoyant une première requête d’authentification puis en le combinant avec nos requêtes.

Ainsi, on peut construire le scénario suivant :

1. demande d’authentification

GET /users/123/authenticate?pass=lkdnssdf54d47894f5123002fds2sd360s0

<?xml>
<user>
  <id>123</id>
  <name>Nicolas Hachet</name>
</user>
<token>
  fsd531gfd5g5df31fdg3g3df45
</token>

2. accès aux ressources

Cet token est ensuite utilisé pour générer un hash de la requête de cette façon :

hash = SHA1(token + requete)
hash = SHA1(fsd531gfd5g5df31fdg3g3df45 + "GET /books")
hash = 456894ds4q15sdq156sd1qsd1qsd156156

C’est ce hash qui est passé comme jeton afin de valider l’authentification pour cette requête:

GET /books?user=123&hash=456894ds4q15sdq156sd1qsd1qsd156156

Plus d’infos…

Wikipedia : http://fr.wikipedia.org/wiki/Representational_State_Transfer
Gerald’s Blog : http://www.croes.org/gerald/blog/qu-est-ce-que-rest/447/
Slides de David Züelke au Symfony Live 2012 Paris : http://goo.gl/Gc5nD
Site du W3C sur les entêtes HTTP : http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

  • Massire Dia

    Merci. Tutoriel simple et facile à comprendre.

  • Kisito MOMO TENE

    Qu’est ce qui n’est pas « RESTFULL » dans cet requête:
    1) http: // mywebsite.com//comments? bookId = 87
    au lieu de:
    2) http: //mywebsite.com/books/87/ comments

    Je trouve que la première requête à l’avantage d’être uniforme par rapport à l’ensemble des requêtes qu’on peut effectuer sur les commentaires;
    En effet, si on veut obtenir tous les commentaires d’un auteur en respectant la règle 2) on va écrire:
    http: //mywebsite.com/authors/15/ comments

    Et si on veut maintenant obtenir les commentaires d’un livre provenant d’un auteur comment allons nous faire si on veut suivre la règle 2) ?
    Par contre en suivant la règle 1) on écrira simplement:
    http : //mywebsite.com//comments? bookId=87 & authordId=15

    • Nicolas Hachet

      Il faut bien voir REST comme un style d’architecture.
      La conception des dépendances entre les ressources (les commentaires sont liés à des livres par exemple) est au libre arbitre du concepteur. Votre exemple est donc parfaitement correct.

      Pour être plus précis, la hiérarchie des ressources est fortement liée à la notion de composition.

      1. Dans l’exemple suivant :
      /books/87/comments

      Les commentaires sont liés à des livres de façons immuables. En supprimant un livre, on supprime les commentaires associés.

      2. Dans votre exemple :
      /comments?bookid=87

      Les commentaires sont autonomes et ne composent pas le livre. On filtre simplement les commentaires en fonction d’un livre ou d’un auteur donné.

      Dans la vraie vie, les commentaires sont généralement une donnée non composite, rien n’empêche donc de les ajouter de manière autonome comme vous le proposez.

      Un exemple plus concret sont les pages d’un livre (une composition donc) :
      /books/87/pages/34

      Ici, la composition est forte et une recherche directe sur les pages /pages?bookid=87 n’a pas de raison d’être, les pages n’étant rien sans livre.

  • fahim

    article simple et bien expliqué pour ceux qui débutent dans le REST comme moi. Merci

  • Admiral Husker

    Merci c’est l’article le plus clair que j’ai pu voir jusqu’à présent. Par contre j’ai toujours du mal avec authentification / authorization. Si j’ai bien compris le second n’est pas mentionné dans l’article et doit être implémenté via un cryptage asymétrique d’une signature ?

  • iAndie

    Merci, c’est simple et clair

  • Manon

    Merci bcp pour cet article !

  • Pingback: Découverte des web services : REST vs SOAP | Blog développement C# / WPF / WEB()

  • Pingback: Planzone : l’outil pour faciliter la gestion de projets « Le blog PHP de Nicolas Hachet – Développeur Web – Lead dev PHP MySQL()

  • Muss

    Faut-il que la ressource ne soit accessible que d’une seule manière ?

    Exemple pour le commentaire :
    http://mywebsite.com/books/87/comments/1568
    VS
    http://mywebsite.com/comments/1568

    On a 2 possibilités…

    De même, les urls ne sont pas un système de query mais un point d’accès à une ressource..

    • Nicolas Hachet

      Oui la ressource ne doit être accessible que d’une seule manière. Il faut donc bien choisir l’URI d’accès pour qu’il n’y ait pas de redondance.

      Comme tu l’indiques, il arrive qu’on puisse accéder à une ressource selon différentes méthodes. Dans ce cas, il faut choisir la plus appropriée pour n’en conserver qu’une seule.

      Soit un commentaire est forcément lié à un livre, dans ce cas on choisira celle ci : http://mywebsite.com/books/87/comments

      Soit un commentaire n’est pas forcément lié à un livre, dans ce cas on choisira le second http://mywebsite.com/comments

      Attention il est tout de même envisageable d’avoir des commentaires sur des livres et ou des produits, dans ce cas il faut choisir entre ce système :
      http://mywebsite.com/books/17/comments
      http://mywebsite.com/products/17/comments

      et ce système (qui gomme la notion de livre et de produit) :

      http://mywebsite.com/comments

      • En fait,

        Il faut se poser la question, qui est une extension de quoi ?

        Disons que le commentaire d’un point de vue sémantique n’existe pas « seul », on commente un livre ou un produit.

        Le commentaire est une fonctionnalité supplémentaire greffée sur un produit ou un livre. J’opterai donc pour la version /produits/comments/1

        De plus, si on peut commenter et des produits et des livres

        la méthode /comments/1 fait-elle référence commentaire 1 d’un produit ou d’un livre ? ( pour une modélisation sur des tables différentes)

        Pour une modélisation sur une table unique, il faudra enrichir la requête avec un paramètre ?target=produits ou quelque chose du genre.

        L’approche /products/1/comments/1 est définitivement la meilleure je pense.

        Egalement d’un point de vue performance, cela donne aussi une API assez intuitive pour toutes les autres ressources qu’elle peut comporter ( books , magazines ) etc

  • Marco

    Une explication simple, concise et efficace de l’archi. REST
    Merci

  • ines

    super article ,enfin j’ai compris cette approche !!!
    mais j’ai une question
    est-il possible d’utiliser l’archetecture REST entre 3 acteurs (2 serveurs webs et un client mobile (Android)) ?????????
    j’ai besoin de me répondre SVP !!!!!!
    et encore good article

    • Bonjour,

      oui il est tout à fait possible d’utiliser cette approche dans une architecture à plusieurs serveurs Web.
      Quelle est ton interrogation sur le sujet ?

  • Steve

    Ca aurait été intéressant d’évoquer aussi les statuts HTTP des réponses
    Par exemple : http://restpatterns.org/HTTP_Status_Codes

    • Merci Steve. Effectivement, je mettrai à jour l’article quand j’aurais un moment.

    • C’est en effet intéressant mais à peu de choses à voir avec REST, mais tout à voir avec HTTP. 🙂 Pour ce qui est de HTTP, je suis en cours d’éditions d’une série d’articles sur le site developer de Opera. http://dev.opera.com/author/karlcow

      • Je suis d’accord avec toi. REST utilise le protocole HTTP au maximum, donc tout ce qui touche au HTTP à un lien avec REST, et vice versa. Cet article a pour but d’expliciter certaines notions permettant d’implémenter REST, notamment la notion de ressource et l’utilisation des verbes comme opération sur ces ressource. C’est une des bases de REST.

  • Il manque la référence vers la thèse de Roy Fielding. Autant aller à la source. La cinquième règle n’a rien à voir avec REST et même au contraire. 🙂 Il transforme l’URI comme porteur de l’action, ce qui est justement pas REST.

    Admunsen a récemment publié un billet sur le sujet
    http://amundsen.com/blog/archives/1136

    « Almost everytime the answer is the same: « NO. » »

    • Merci pour votre commentaire.

      Concernant la cinquième règle, c’est effectivement un abus. La bonne pratique serait d’utiliser les entêtes HTTP afin de s’authentifier auprès du serveur : HTTP Auth

      • Nicolas,

        comme peu de gens lisent les commentaires, je ferais sauter cette 5eme règle. Notez aussi que pratiquement aucune des règles ci-dessus ne définit une architecture REST. REST est un style d’architecture pour systèmes distribués hypermedia. Il faut vraiment lire le chapitre 5 de la thèse de Roy Fielding, il y a une traduction française.

        Les contraintes sont les suivantes :

        1. Client-Serveur: C’est à dire la séparation des contraintes entre le client et le serveur. Soit séparer l’interface utilisateur de celle du stockage des données. Cela permet aux deux d’évoluer indépendemment.

        2. Stateless: Toutes requêtes d’un client vers un serveur doit contenir toute l’information nécessaire pour comprendre la requête, sans avoir à dépendre d’un contexte conservé sur le serveur. Cela libère de nombreuses interactions entre le client et le serveur.

        3. Cache: Le serveur envoie une réponse qui donne l’information sur la propension de cette réponse à être cachée, comme la fraîcheur, sa date de création, si elle doit être conservée dans le futur. Cela permet à des proxys de décharger les contraintes sur le serveur et aux clients de ne pas faire de requêtes inutiles. Cela permet également d’améliorer les montées en charge des serveurs.

        4. Une interface uniforme: Celle ci est très mal comprise. C’est la tentative de la règle 4 du billet de blog. Elle comprend de nombreux aspects. L’interface est définie selon 4 contraintes essentielles:

        4.1 L’identification des ressources
        4.2 La manipulation des ressources à travers des représentations
        4.3 Un message auto-descriptif
        4.4 Hypermedia comme le moteur de l’état de l’application (chaque accès aux états suivants de l’applications sont décris dans le message courant. Très important. C’est ce qui est tenté dans la règle 4 de ce billet, mais malheureusement incomplet.)

        5. Un système hiérarchisé: c’est à dire que l’on identifie par des ressources uniques des états de l’application plutôt que tout envoyer dans une seule ressource. L’enjeu est que cela augmente les requêtes/réponses entre le client et le serveur et donc fait baisser les performances d’où l’importance du cache, etc. Le bénéfice est que cela rend beaucoup plus flexible l’évolution du système. (C’est la tentative de la règle no 1 du billet)

        6. Code-On-Demand (Optionnel): La possibilité pour les clients d’éxécuter des scripts. Cela permet d’éviter que le processing ne se fasse que du côté serveur et permet donc de faire évoluer les fonctionnalités du client au cours du temps. En revanche cela réduit la visibilité de l’organisation des ressources par exemples. Un état devient dépendant du client et plus du serveur ce qui contredit la règle 2. Il faut donc être prudent en utilisant cette contrainte.

        En espèrant que cela aide.

  • Steve

    J’avais pas vu cet article, concis, clair et documenté !
    J’ai particulièrement aimé la partie 5.
    A mettre dans un wiki 😉

    • Merci !
      En le relisant, je me dis que je vais peut être aller un peu plus loin… 😉

  • Clair et explicite.