Quelle est la manière reposante de représenter une opération de clone de ressource dans l’URL?

J’ai l’API REST qui expose une grande ressource complexe et je souhaite pouvoir cloner cette ressource. Supposons que la ressource est exposée dans /resources/{resoureId}

Pour cloner la ressource 10, je pourrais faire quelque chose comme.

  • GET /resources/10
  • POST /resources/ body of put contenant un duplicata de la représentation par GET /resources/10 sans l’identifiant pour que le POST crée une nouvelle ressource.

Le problème avec cette approche est que la ressource est très volumineuse et complexe, cela n’a aucun sens de retourner une représentation complète au client, puis de le renvoyer car ce serait simplement une perte totale de bande passante, et cpu sur le serveur. serveur. Le clonage de la ressource sur le serveur est tellement plus simple que je veux le faire.

Je pourrais faire quelque chose comme POST /resources/10/clone ou POST resources/clone/10 mais ces deux approches sont fausses à cause du verbe dans l’URL.

Quelle est la manière la plus “reposante / nouny” de créer une URL qui peut être utilisée dans ce type de situation?

Comme il n’y a pas de méthode de copie ou de clone dans HTTP, c’est vraiment à vous de décider ce que vous voulez faire. Dans ce cas, un POST semble parfaitement raisonnable, mais d’autres normes ont adopté des approches différentes:

  • WebDAV a ajouté une méthode COPY .
  • Amazon S3 utilise PUT sans corps et un en x-amz-copy-source spécial x-amz-copy-source . Ils appellent cela un PUT Object - Copy .

Ces deux approches supposent que vous connaissez l’URI de destination. Votre exemple semble manquer d’une destination connue, vous devez donc utiliser POST. Vous ne pouvez pas utiliser PUT ou COPY car votre opération de création n’est pas idempotente.

Si votre service définit POST /resources comme “create a new resource”, alors pourquoi ne pas simplement définir une autre manière de spécifier la ressource autre que le corps du POST? Par exemple POST /resources?source=/resources/10 avec un corps vide.

La réponse de Francis est excellente et probablement ce que vous recherchez. Cela dit, ce n’est pas techniquement RESTful puisque (comme il le dit dans les commentaires) il repose sur le fait que le client fournit des informations hors bande. Étant donné que la question était “quelle est la manière reposante” et non “ce qui est un bon moyen / la meilleure façon”, cela m’a fait réfléchir à la question de savoir s’il existe une solution RESTful. Et je pense que ce qui suit est une solution RESTful, même si je ne suis pas sûr que ce soit nécessairement mieux dans la pratique.

Tout d’abord, comme vous l’avez déjà identifié, GET suivi de POST est la méthode RESTful simple et évidente, mais elle n’est pas efficace. Nous recherchons donc une optimisation, et nous ne devrions pas être trop surpris si cela semble moins naturel que cette solution!

La solution POST + sourceId crée une URL spéciale – une URL qui ne pointe pas vers une ressource, mais vers une instruction pour faire quelque chose. Chaque fois que vous créez des URL spéciales comme celle-ci, il est utile de savoir si vous pouvez résoudre le problème en définissant simplement plus de ressources.

Nous voulons pouvoir copier

 resources/10 

Et si nous trouvons une autre ressource:

 resources/10/copies 

… et la définition de cette ressource est simplement “la collection de ressources qui sont des copies de ressource / 10”.

Avec cette ressource définie, nous pouvons maintenant ré-exprimer notre opération de copie en termes différents – au lieu de dire “Je veux que le serveur copie des ressources / 10”, nous pouvons dire “Je veux append une nouvelle chose à la collection de choses sont des copies de ressources / 10 “.

Cela semble étrange, mais cela s’intègre naturellement dans la sémantique de REST. Par exemple, supposons que cette ressource ressemble à ceci (je vais utiliser une représentation JSON ici):

 [] 

Nous pouvons simplement mettre à jour cela avec un POST ou un PATCH [1]:

 POST resources/copies/10 ["resources/11"] 

Notez que tout ce que nous envoyons au serveur est constitué de métadonnées sur une collection, ce qui est très efficace. Nous pouvons supposer que le serveur sait maintenant où obtenir les données à copier, puisque cela fait partie de la définition de cette ressource. Nous pouvons également supposer que le client sait que cela se traduit par la création d’une nouvelle ressource à «ressources / 11» pour la même raison.

Avec cette solution, tout est clairement défini comme une ressource et tout a une URL canonique, et aucune information hors bande n’est requirejse par le client.

En fin de compte, est-ce que cela vaut la peine d’aller avec cette solution étrange, juste pour être plus RESTful? Cela dépend probablement de votre projet individuel. Mais il est toujours intéressant d’essayer de définir le problème différemment en créant différentes ressources!

[1] Je ne sais pas si cela a du sens d’autoriser GET sur “resources / 10 / copies”. Évidemment, dès que la ressource d’origine ou une copie de celle-ci change, la copie n’est plus une copie et ne devrait pas figurer dans cette collection. Du sharepoint vue de l’implémentation, je ne vois pas l’intérêt de contraindre le serveur à suivre cela, donc je pense que cela devrait être traité comme une ressource de mise à jour uniquement.

Je pense que POST /resources/{id} serait une bonne solution pour copier une ressource.

Pourquoi?

  • POST /resources est la norme REST par défaut pour créer une nouvelle ressource
  • POST /resources/{id} doest déjà existant, car vous ne générerez jamais une nouvelle ressource avec un identifiant défini par vous-même. Le serveur définira toujours l’identifiant.

Cela n’a pas de sens, car vous ne copiez jamais la ressource A sur la ressource B:

 POST /resources?source=/resources/10 POST /resources-a?source=/resources-b/10 

Alors pourquoi ne pas utiliser simplement POST / resources / {id}

  • Il créera une nouvelle ressource
  • Le parent de copie est défini par le {id}
  • La copie sera uniquement sur la même ressource
  • C’est la variante la plus semblable à REST

Comment y pensez-vous?

Je vais le mettre là-bas si cela peut aider quelqu’un.
Nous avons eu un scénario similaire, où nous fournissions «clone vm» en tant que fonctionnalité permettant d’élargir notre offre IaaS. Donc, si un utilisateur souhaitait POST: /vms/vm101 à POST: /vms/vm101 endpoint avec POST: /vms/vm101 étant

 {"action": "clone", // Specifies action to take, since our users can do couple of other actions on a vm, like power_off/power_on etc. "body": {"name": [vm102, vm103, vm104] // Number of clones to make "storage": 50, ... // Optional parameters for specifying differences in specs one would want from the base virtual machine } 

et 3 clones de vm101 à savoir. vm102, vm103 et vm104 seraient lancés.