En se basant sur HTTP (v1 et maintenant v2 [http2-rest] ) Rest bénéficie d’un mode de communication simple et universel (technologiquement parlant) dont les mécanismes de sécurisations et d’attaques sont connus (ssl, tls, dos, basic authent, SAML, OAuth2, JWT, la liste est longue et nous y reviendrons ....).
Dans le principe, l’idée est simple: exposer et fournir une ressource au travers d’HTTP. Conformément à ce protocole ceci est réalisé grâce à son URL (Uniform Resource Locator) [url-uri] qui permet au client d’identifier et de manipuler cette ressource.
Lorsque l’on parle de ressource, il va de soit que cela se conçoit dans le cadre de HTTP. Ainsi la ressource est un objet virtuel présent côté serveur dont le cycle de vie sera gérer par celui ci mais avec lequel le client aura la possibilité d’interagir en manipulant les verbe du protocole: (GET, POST, PUT, DELETE, PATCH).
De manière générale ces interactions du client avec la ressource c’est à dire les verbes HTTP pourront suivre une logique de gestion de type CRUD et ainsi voir l’interface serveur Rest comme une interface d'accès à des données comme on le ferait avec une BDD.
Ainsi, pour être plus explicite, lorsque le client réalisera une requête GET, cela pourra être vu comme une requête Read sur la ressource si celle ci était une base de donnée. Si le client réalise une requête de type de POST, cela équivaudra à une requête Create, s'il réalise un PUT, cela équivaut à un update de la ressource [put-vs-post] et s’il fait un DELETE, cela est comme on pourra s’en douter un Delete de la ressource.
Il est évident que cette vision mappant ReST et CRUD est un raccourci abusif [rest-not-crud] car une API ReST doit être spécifié et construire selon un besoin utilisateur clair et non dans une logique où l’on pourra tout faire sur le même endpoint! Ainsi si, dans le cas des modèles simples, adopter une logique CRUD ne sera probablement pas une bêtise, rapidement, il faudra penser l’API ReST comme étant une fenêtre exposant des ressources orientés d’un même modèle de données.
De même, les interactions sont l’occasion de réaliser des échanges d’informations. Ainsi lorsque le client réalise un GET sur une ressource, son but est de disposer des informations la concernant. De même lorsque qu’il réalise un POST, sont but est de pousser les informations dont il dispose afin d’enregistrer ces informations sous la forme d’une nouvelle ressource dans le server. Mais encore une fois, cela ne préjuge pas de la structure des données qui seront réellement mise en base de données, (si celle ci existe).
Comme nous l’avons déjà dit Rest s’appuie sur HTTP et c’est ainsi qu’est formalisé la manière d'échanger de l’informations, quoi? non pas en html! (bien que dans l’absolu ça ne soit pas impossible) mais plutôt en spécifiant le type MIME dans le header de la transaction.
Pour rappel, dans le protocole http, le type MIME permet au client et au server de se mettre d’accord sur le format des données qu’ils vont s'échanger. Ainsi, dans le web classique, on trouve de html, du text des images… Ici avec ReST on trouvera essentiellement de l’XML et plus probablement du JSON (nous reviendrons sur cela).
De l’XML? mais c’est comme SOAP alors! ? et ba loupé! [soap-vs-rest]
En effet Rest et Soap sont souvent comparés et traité à un même niveau sauf que s’ils sont tous deux des protocoles de communication s’appuyant sur HTTP, il est existe une différence importante entre les deux:
- Rest tend à respecter les standard du web dans l’utilisation de http et utilise ce dernier comme un protocole applicatif où les verbe sont utilisé pour accéder convenablement aux ressources, elle même correctement formalisé selon des URL adaptée.
- SOAP n’utilise HTTP que comme un protocole de transport et toute la logique applicative est dans le message SOAP
Pour mieux comprendre ces différences il convient de lire l’article de Martin Flowers sur Richardson Maturity Model [richardson], [richardson-comment].
Ainsi en tant que style d’architecture, en fournissant un interface virtualisant la consommation ou la production de ressources, facilite le découplage des différentes parties applicatives du systèmes conduisant aux architectures microservices (mais cela est une autre histoire).
Ainsi ces architectures à base ReST en s'appuyant sur http impliquent (ou permettent de bénéficier) différentes propriétés qu’il convient de prendre en compte afin de se prémunir d’erreur de conception.
Entre autre:
- ReST est un style d’architecture basé sur le modèle Client Serveur impliquant que chacun à un rôle différent, le premier consomme les informations, le second, les fournit ou les enregistre.
- ReST est sans etat. Cela est important dans le sens où les requêtes du client sur le serveur sont indépendantes et doivent se suffirent à elle même pour satisfaire le besoin. De son côté, le serveur ne construit pas de session.
- ReST autorise la mise en place de cache pour garantir les performances du système. Ce(s) cache(s) peuvent être implémenté(s) en tout lieu de la chaîne de commande (le client, le serveur où tout autre composant intermédiaire)
- ReST doit fournir une interface uniforme et homogène de façon à faciliter l'accès et le parcours des données
- ReST peut être conçu en couche autorisant l’utilisation de proxy et reverse proxy ainsi que la délégation d’appel à des sous API ReST.
Un dernier point spécifie que ReST, optionnellement, peut fournir du code à la demande. J’admet que ce point reste pour ma part assez nébuleux et je ne l’aborderais pas tant j’ai du mal à concevoir l’utilisation de ce point, sa mise en oeuvre et les éventuelles problèmes de sécurité que cela peut poser.
Bien maintenant que l’on en à parler, ca serait bien d’y gouter aussi un peu à du ReST!
Du coup prenons le cas d’une bibliothèque présentant divers resources selon différents supports.
Alors ici on va commencer par un cas particulier, celui de la ressource unique (ou de type singleton) qui va nous permettre d’obtenir des informations sur des ressources accessibles dans l’API.
Ainsi, sur le endpoint on va trouver un premier chemin accessible tel que:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | > GET: /bibliotheque < 200 : { nom : Bibliotheque de la mairie resources : [ { nom : Les livres type: Livre url : /bibliotheque/livres }, { nom : Les videos type: Video url : /bibliotheque/videos } ] } |
Ici on à donc grace à cette premiere requette la connaissance des resources disposibles. Celle ci de type Livre et Video sont identifiable et accessible via une url. On notera que la nomenclature de nomage est au pluriel car comme on va le voir la ressource associée (contrairement à la bibliotheque qui est un singleton pour sa part) est multiple.
Intéressons nous aux livres:
1 2 3 4 5 6 7 8 9 10 11 | > GET : /bibliotheque/books < 200 : [ { id:1 name: “Clean Code”, ISBN-13: 978-0132350884, nbrChapitre: 17 }, ... ] |
On obtient donc une liste dans laquelle on trouvera le livre Clean Code. A noter qu’avec cette requête, on obtient donc tous les livres! Si on veut limiter cette requete, il est alors possible de faire une requete avec un param:
1 2 3 4 5 6 7 8 9 10 11 | > GET : /bibliotheque/books?nbrChapitre=17 < 200 : [ { id:1 name: “Clean Code”, ISBN-13: 978-0132350884, nbrChapitre: 17 }, ... ] |
Dans ce cas, on obtient toujours une liste de libre mais seul ceux contenant que 17 chapitres… (bon ok comme recherche c’est pas ce qu’il y à de plus courant mais c’est pour l’exemple) De même si l’on fait la même recherche mais sur le nom, on obtient:
1 2 3 4 5 6 7 8 9 10 11 | > GET : /bibliotheque/books?name=Clean Code < 200 : [ { id:1 name: “Clean Code”, ISBN-13: 978-0132350884, nbrChapitre: 17 }, ... ] |
Ce qui revient au même… mais on notera que l’on obtient quand même une liste… pourquoi? car il s’agit de l'équivalent de faire une recherche avec un filtre, du coup ce n’est pas faire une récupération spécifique et si l’on cherchait à récupérer que le livre Clean Code, alors on a mal penser sa requête. Ce qu’il aurait fallu faire c’est :
1 2 3 4 5 6 7 8 | > GET : /bibliotheque/books/1 < 200 : { id:1 name: “Clean Code”, ISBN-13: 978-0132350884, nbrChapitre: 17 } |
La on ne récupère qu’un élément et c’est celui identifié par son identifiant unique, l’id. (équivalent à un clef primaire)
Nous n’avons jusque là fait que des GET, et on remarque qu’il manque Clean Architecture… bon ok, ajoutons le:
1 2 3 4 5 6 7 8 9 10 11 12 13 | > POST : /bibliotheque/books { name: “Clean Architecture”, ISBN-13: 978-0134494166, nbrChapitre: 1 } < 201 : /bibliotheque/books/2 { id:2 name: “Clean Code”, ISBN-13: 978-0132350884, nbrChapitre: 17 } |
Bien on note plusieurs choses: d’une part on ne post pas le livre avec un identifiant, on laisse le serveur se charger de le faire. Ainsi c’est pour cela que la réponse au POST est un code 201, nous spécifiant que la ressource à bien été créée et à cela est ajouté sa localisation (afin de savoir comment la récupérer) mais aussi la ressource elle-même.
Par contre on constate une erreur, ce livre ne contient pas qu’un chapitre mais 34… Il nous faut donc corriger cela. Le hic c’est que si l’on tente de refaire un POST, il y à des chances que l’on nous réponde que la ressource existe déjà… (je vous laisse chercher le code http associé) voir peut être qu’il nous en créer une seconde en faisant par ce biais un doublon. non ce que l’on veut c’est modifier la valeur associée à la ressource.
Bon pour cela, on pourrait faire un DELETE sur l’id et refaire le POST…. bon vous avez compris, ça fonctionnerait mais c’est lourd… non il existe des façon de faire plus efficace.
Entre il est possible d’utiliser (je vous le donne en mille) soit le verbe PUT en fournissant l'intégralité de la ressource ou alors PATCH permettant de cibler la modification à réaliser. Voyons les deux:
1 2 3 4 5 6 7 8 | > PUT : /bibliotheque/books/2 { id:2, name: “Clean Architecture”, ISBN-13: 978-0134494166, nbrChapitre: 34 } < 204 |
Ici dans ce cas, le PUT va donc surcharger complètement la ressource. Seul un code 204 sera retourné pour spécifier que l'opération à été réalisée (mais qu’il n’y à pas d’information supplémentaire à attendre, en effet avec un PUT on connait déjà le localisation de la ressource sinon on aurait pas pu exécuter ce verbe)
Avec le PATCH, c’est un peu plus délicat car il faut spécifier que ce qui est modifier. On prendra garde à ce que l’id ne soit pas surcharger bêtement (à noter d’ailleur que l’id est inutile dans la ressource elle-même si le location du retour du POST est correctement fourni, le seul hic ici est que l’objet ne porte alors pas son identifiant unique, on verra que ceci est alors surtout un problème de représentation des données au sein du modèle ReST. On réfléchira à ce point lorsque ne traiterons d’exemples)
Enfin donc le PATCH: (on considérant que nous n’avons pas fait le PUT précédent)
1 2 3 4 5 | > PATCH : /bibliotheque/books/2 { nbrChapitre: 34 } < 204 |
Voilà les grandes lignes de l’utilisation de ReST et de son design afin d’en tirer le meilleur. Maintenant il reste à le formaliser et pour cela rien de tel que quelques exemples. Du coup on tachera de faire du ReST avec quelques langages histoire de bien en montrer l'intérêt et surtout que finalement, le langage en question ne change rien aux fondamentaux et aux propriétés portées par ReST!
Références
[http2-rest] https://dzone.com/articles/benefits-of-rest-apis-with-http2?edition=352091&utm_source=Daily%20Digest&utm_medium=email&utm_campaign=Daily%20Digest%202018-01-10[Thomas Fielding] https://www.ics.uci.edu/~fielding/
[wiki] https://en.wikipedia.org/wiki/Representational_state_transfer
[rest-tuto] https://www.restapitutorial.com/
[soap-vs-rest] https://dzone.com/articles/differences-in-performance-apis-amp-more?edition=286955&utm_source=Daily%20Digest&utm_medium=email&utm_campaign=dd%202017-03-31
[richardson] https://martinfowler.com/articles/richardsonMaturityModel.html
[richardson-comment] https://blog.xebia.fr/2010/06/25/rest-richardson-maturity-model/
[swagger] https://dzone.com/articles/api-first-with-swagger?edition=330491&utm_source=Daily%20Digest&utm_medium=email&utm_campaign=Daily%20Digest%202017-10-12
[put-vs-post] https://restfulapi.net/rest-put-vs-post/
[rest-not-crud] https://medium.com/@marinithiago/guys-rest-apis-are-not-databases-60db4e1120e4
[rest-quick] https://blog.nicolashachet.com/niveaux/confirme/larchitecture-rest-expliquee-en-5-regles/
[url-uri] https://www.java67.com/2013/01/difference-between-url-uri-and-urn.html
Aucun commentaire:
Enregistrer un commentaire