Dans cet article, l'auteur affirme que
Parfois, il est nécessaire d'exposer une opération dans l'API qui est intrinsèquement non RESTful.
et cela
Si une API a trop d'actions, cela indique que soit elle a été conçue avec un point de vue RPC plutôt que d'utiliser les principes RESTful, soit que l'API en question est naturellement mieux adaptée à un modèle de type RPC.
Cela reflète aussi ce que j'ai lu et entendu ailleurs.
Cependant, je trouve cela assez déroutant et j'aimerais mieux comprendre la question.
Exemple I: arrêt d'une machine virtuelle via une interface REST
Il y a, je pense, deux façons fondamentalement différentes de modéliser l'arrêt d'une machine virtuelle. Chaque voie peut avoir quelques variations, mais concentrons-nous sur les différences les plus fondamentales pour l'instant.
1. Corrigez la propriété d'état de la ressource
PATCH /api/virtualmachines/42
Content-Type:application/json
{ "state": "shutting down" }
(Alternativement, PUT
sur la sous-ressource /api/virtualmachines/42/state
.)
La machine virtuelle s'arrêtera en arrière-plan et, à un moment ultérieur, selon que l'arrêt se terminera ou non, l'état peut être mis à jour en interne avec «mise hors tension».
2. PUT ou POST sur la propriété actions de la ressource
PUT /api/virtualmachines/42/actions
Content-Type:application/json
{ "type": "shutdown" }
Le résultat est exactement le même que dans le premier exemple. L'état sera mis à jour pour "arrêter" immédiatement et peut-être éventuellement pour "s'éteindre".
Les deux modèles sont-ils RESTful?
Quel design est le meilleur?
Exemple II: CQRS
Que se passe-t-il si nous avons un domaine CQRS avec de nombreuses "actions" (commandes aka) qui pourraient potentiellement conduire à des mises à jour de plusieurs agrégats ou ne peuvent pas être mappées aux opérations CRUD sur des ressources et sous-ressources concrètes?
Devrions-nous essayer de modéliser autant de commandes que le béton crée ou met à jour sur des ressources concrètes, dans la mesure du possible (en suivant la première approche de l'exemple I) et utiliser des «points de terminaison d'action» pour le reste?
Ou devrions-nous mapper toutes les commandes aux points de terminaison d'action (comme dans la deuxième approche de l'exemple I)?
Où devrions-nous tracer la ligne? Quand le design devient-il moins RESTful?
Un modèle CQRS est-il mieux adapté à une API de type RPC?
Selon le texte cité ci-dessus, c'est, si je comprends bien.
Comme vous pouvez le voir dans mes nombreuses questions, je suis un peu confus à ce sujet. Pouvez-vous m'aider à mieux le comprendre?
la source
Réponses:
Dans le premier cas (arrêt des machines virtuelles), je n'envisagerais aucune des alternatives OP RESTful. Certes, si vous utilisez le modèle de maturité Richardson comme critère, ce sont tous les deux des API de niveau 2 car ils utilisent des ressources et des verbes.
Aucun d'eux, cependant, n'utilise de contrôles hypermédia, et à mon avis, c'est le seul type de REST qui différencie la conception de l'API RESTful de RPC. En d'autres termes, respectez les niveaux 1 et 2 et vous aurez une API de style RPC dans la plupart des cas.
Afin de modéliser deux façons différentes d'arrêter une machine virtuelle, j'exposerais la machine virtuelle elle-même comme une ressource qui (entre autres choses) contient des liens:
Si un client souhaite arrêter la
Ploeh
machine virtuelle, il doit suivre le lien avec leshut-down
type de relation. (Normalement, comme indiqué dans le livre de recettes des services Web RESTful , vous utiliseriez un IRI ou un schéma d'identification plus élaboré pour les types de relations, mais j'ai choisi de garder l'exemple aussi simple que possible.)Dans ce cas, il y a peu d'autres informations à fournir avec l'action, donc le client doit simplement faire un POST vide par rapport à l'URL dans le
href
:(Comme cette demande n'a pas de corps, il serait tentant de la modéliser comme une demande GET, mais les demandes GET ne devraient pas avoir d'effets secondaires observables, donc POST est plus correct.)
De même, si un client souhaite éteindre la machine virtuelle, il suivra le
power-off
lien à la place.En d'autres termes, les types de relations des liens fournissent des opportunités qui indiquent l'intention. Chaque type de relation a une signification sémantique spécifique. C'est la raison pour laquelle nous parlons parfois du web sémantique .
Afin de garder l'exemple aussi clair que possible, j'ai intentionnellement masqué les URL de chaque lien. Lorsque le serveur d' hébergement reçoit la requête entrante, il sauriez que des
fdaIX
moyens fermés , et desCHTY91
moyens hors tension .Normalement, je coderais simplement l'action dans l'URL elle-même, afin que les URL soient
/vms/1234/shut-down
et/vms/1234/power-off
, mais lors de l'enseignement, cela brouille la distinction entre les types de relation (sémantique) et les URL (détails d'implémentation).Selon les clients que vous possédez, vous pouvez envisager de rendre les URL RESTful non piratables .
CQRS
En ce qui concerne le CQRS, l'une des rares choses sur lesquelles Greg Young et Udi Dahan sont d'accord est que le CQRS n'est pas une architecture de haut niveau . Ainsi, je serais prudent de faire une API RESTful trop semblable à CQRS, car cela signifierait que les clients font partie de votre architecture.
Souvent, la force motrice d'une véritable API RESTful (niveau 3) est que vous voulez pouvoir faire évoluer votre API sans casser les clients et sans avoir le contrôle des clients. Si c'est votre motivation, alors le CQRS ne serait pas mon premier choix.
la source
DELETE
me semble étrange car après l'arrêt du vm, il existera toujours, uniquement en état "power off" (ou sth. comme ça).Arrêter une machine virtuelle via une interface REST
Il s'agit en fait d'un exemple quelque peu célèbre, présenté par Tim Bray en 2009 .
Roy Fielding, discutant du problème, a partagé cette observation :
En bref, vous disposez d'une ressource d'informations qui renvoie une représentation actuelle de l'état surveillé; cette représentation peut inclure un lien hypermédia vers un formulaire qui demande une modification de cet état, et le formulaire a un autre lien vers une ressource pour gérer (chaque) demande de modification.
Seth Ladd avait les idées clés du problème
La programmation RESTful est la bureaucratie de Vogon à l'échelle du Web. Comment faites-vous quoi que ce soit RESTful? Inventez-lui de nouveaux documents et numérisez-les.
Dans un langage un peu plus sophistiqué, vous définissez le protocole d'application de domaine pour «arrêter une machine virtuelle» et identifiez les ressources dont vous avez besoin pour exposer / implémenter ce protocole.
Regarder vos propres exemples
C'est bon; vous ne traitez pas vraiment la demande elle-même comme une ressource d'informations distincte, mais vous pouvez toujours la gérer.
Vous avez manqué un peu dans votre représentation du changement.
Par exemple, les instructions de formatage du type de support JSON Patch comme si vous modifiiez directement un document JSON
Dans votre alternative, l'idée est proche, mais pas évidemment correcte.
PUT
est un remplacement complet de l'état de la ressource à l'URL cible , donc vous ne choisiriez probablement pas une orthographe qui ressemble à une collection comme cible d'une représentation d'une seule entité.Est cohérent avec la fiction selon laquelle nous ajoutons une action à une file d'attente
Est conforme à la fiction selon laquelle nous effectuons une mise à jour de l'élément de queue dans la file d'attente; c'est un peu bizarre de le faire de cette façon. Le principe de la moindre surprise recommande de donner à chaque PUT son propre identifiant unique, plutôt que de les mettre tous au même endroit et de modifier plusieurs ressources en même temps.
Notez que, dans la mesure où nous discutons de l'orthographe de l'URI - REST s'en fiche;
/cc719e3a-c772-48ee-b0e6-09b4e7abbf8b
est un URI parfaitement cromulent en ce qui concerne REST. La lisibilité, comme pour les noms de variables, est une préoccupation distincte. L'utilisation d'orthographes conformes à la RFC 3986 rendra les gens beaucoup plus heureux.CQRS
Greg Young sur CQRS
Étant donné que vous parlez de CQRS dans le contexte de HTTP / REST, il semble raisonnable de supposer que vous travaillez dans ce dernier contexte, alors allons-y.
Étonnamment, celui-ci est encore plus facile que votre exemple précédent. La raison en est simple: les commandes sont des messages .
Jim Webber décrit HTTP comme le protocole d'application d'un bureau des années 50; le travail se fait en prenant des messages et en les mettant dans des boîtes de réception. La même idée tient - nous obtenons une copie vierge d'un formulaire, le remplissons avec les détails que nous connaissons, le livrons. Ta da
Oui, dans la mesure où les «ressources concrètes» sont des messages, plutôt que des entités dans le modèle de domaine.
Idée clé: votre API REST est toujours une interface ; vous devriez pouvoir changer le modèle sous-jacent sans que les clients n'aient besoin de changer les messages. Lorsque vous publiez un nouveau modèle, vous publiez une nouvelle version de vos points de terminaison Web qui savent comment prendre votre protocole de domaine et l'appliquer au nouveau modèle.
Pas vraiment - en particulier, les caches Web sont un excellent exemple d'un «modèle de lecture finalement cohérent». Rendre chacune de vos vues adressable indépendamment, chacune avec ses propres règles de mise en cache, vous donne un tas de mise à l'échelle gratuitement. Il y a relativement peu d'intérêt pour une approche exclusivement RPC des lectures.
Pour les écritures, c'est une question plus délicate: envoyer toutes les commandes à un seul gestionnaire à un seul point de terminaison, ou à une seule famille de points de terminaison, est certainement plus facile . REST est vraiment plus sur la façon dont vous trouvez communiquer où le point de terminaison est au client.
Le traitement d'un message comme sa propre ressource unique présente l'avantage que vous pouvez utiliser PUT, alertant les composants intermédiaires du fait que la gestion du message est idempotente, afin qu'ils puissent participer dans certains cas de gestion des erreurs, c'est une bonne chose d'avoir . (Remarque: du point de vue des clients, si les ressources ont des URI différents, alors ce sont des ressources différentes; le fait qu'ils peuvent tous avoir le même code de gestionnaire de requêtes sur le serveur d'origine est un détail d'implémentation caché par l'uniforme interface).
Fielding (2008)
la source
Vous pouvez utiliser 5 niveaux de type de support pour spécifier la commande dans le champ d'en-tête de type de contenu de la demande.
Dans l'exemple VM, ce serait quelque chose dans ce sens
alors
Ou
Voir https://www.infoq.com/articles/rest-api-on-cqrs
la source
L'exemple de l'article lié est fondé sur l'idée que le démarrage et l'arrêt de la machine doivent être dirigés par des commandes plutôt que par des changements dans l'état des ressources modélisées. Ce dernier est à peu près ce que REST vit et respire. Une meilleure modélisation de la machine virtuelle nécessite de voir comment fonctionne son homologue du monde réel et comment vous, en tant qu'humain, interagiriez avec elle. C'est de longue haleine, mais je pense que cela donne un bon aperçu du type de réflexion requis pour faire une bonne modélisation.
Il existe deux façons de contrôler l'état d'alimentation de l'ordinateur sur mon bureau:
Pour une machine virtuelle, les deux peuvent être modélisés en tant que valeurs booléennes en lecture / écriture:
power
- Lorsqu'il est changé entrue
, rien ne se passe, sauf une note indiquant que le commutateur a été placé dans cet état. Lorsqu'elle est modifiée enfalse
, la machine virtuelle est commandée dans un état de mise hors tension immédiate. Par souci d'exhaustivité, si la valeur reste inchangée après une écriture, rien ne se produit.onoff
- Lorsqu'il est changé entrue
, rien ne se passe sipower
c'est le casfalse
, sinon la VM est commandée pour démarrer. Le changement à l'false
, si rien ne se passepower
estfalse
, sinon, la machine virtuelle est commandée de notifier le logiciel pour faire un arrêt ordonné, ce qu'il fera et informer la machine virtuelle qu'il peut aller dans la mise hors tension état. Encore une fois, pour être complet, une écriture sans changement ne fait rien.Avec tout cela vient la réalisation qu'il y a une situation où l'état de la machine ne reflète pas l'état des commutateurs, et c'est pendant l'arrêt.
power
sera toujourstrue
etonoff
serafalse
, mais le processeur exécute toujours son arrêt, et pour cela nous devons ajouter une autre ressource afin que nous puissions dire ce que la machine fait réellement:running
- Une valeur en lecture seule qui esttrue
lorsque la machine virtuelle est en cours d'exécution etfalse
quand elle ne l'est pas, déterminée en demandant à l'hyperviseur son état.Le résultat est que si vous voulez qu'une VM démarre, vous devez vous assurer que les ressources
power
etonoff
ont été définiestrue
. (Vous pouvez permettre que l'power
étape soit ignorée en la réinitialisant automatiquement, de sorte que si elle est définie surfalse
, elle devienne unetrue
fois que la machine virtuelle a été définitivement arrêtée. Que ce soit une chose RESTly-pure à faire est un aliment pour une autre discussion.) Si vous voulez faire un arrêt ordonné, vous définissezonoff
àfalse
et revenir plus tard pour voir si la machine effectivement arrêté, la misepower
àfalse
si elle ne l'a pas.Comme dans le monde réel, vous avez toujours le problème d'être invité à démarrer la machine virtuelle une fois qu'elle a été
onoff
changée enfalse
mais c'est toujoursrunning
parce qu'elle est au milieu de l'arrêt. La façon dont vous gérez cela est une décision politique.la source
Donc, si vous voulez réfléchir tranquillement, oubliez les commandes. Le client ne dit pas au serveur d'arrêter la machine virtuelle. Le client "ferme dow" (métaphoriquement) sa copie de la représentation de la ressource en mettant à jour son état puis en remettant cette représentation sur le serveur. Le serveur accepte cette nouvelle représentation d'état et, comme effet secondaire, arrête la machine virtuelle. L'effet secondaire est laissé au serveur.
C'est moins de
Hé serveur, client ici, ça te dérangerait d'arrêter la VM
et plus de
Hé serveur, client ici, j'ai mis à jour l'état de la ressource VM 42 dans l'état d'arrêt, mettez à jour votre copie de cette ressource et faites ce que vous pensez être approprié
En tant qu'effet secondaire du serveur acceptant ce nouvel état, il peut vérifier les actions qu'il doit réellement effectuer (telles que l'arrêt physique de VM 42), mais cela est transparent pour le client. Le client n'est pas concerné par les actions que le serveur doit entreprendre pour devenir cohérent avec ce nouvel état
Oubliez donc les commandes. Les seules commandes sont les verbes en HTTP pour le transfert d'état. Le client ne sait pas, ni se soucie, comment le serveur va mettre la machine virtuelle physique dans l'état d'arrêt. Le client n'émet pas de commandes au serveur pour y parvenir, il dit simplement c'est le nouvel état, comprenez-le .
La puissance de ceci est qu'il dissocie le client du serveur en termes de contrôle de flux. Si plus tard le serveur change la façon dont il fonctionne avec les VM, le client s'en fiche. Il n'y a aucun point de terminaison de commande à mettre à jour. Dans RPC si vous modifiez le point de terminaison API de
shutdown
àshut-down
vous avez interrompu tous vos clients car ils ne connaissent plus la commande à appeler sur le serveur.REST est similaire à la programmation déclarative, où au lieu de lister un ensemble d'instructions pour changer quelque chose, vous indiquez simplement comment vous voulez qu'il soit et laissez l'environnement de programmation le comprendre.
la source
POST /api/virtualmachines/42/shutdown
au lieu d'avoir un "effet secondaire". L'API doit être compréhensible pour l'utilisateur, comment savoir si, par exempleDELETE /api/virtualmachines/42
, la machine virtuelle sera arrêtée? Un effet secondaire pour moi est un bug, nous devons concevoir nos API pour qu'elles soient compréhensibles et auto-descriptives