118 votes

Alternatives RESTful au corps de la requête DELETE

Alors que le Spécifications HTTP 1.1 semble permettre les corps de messages sur DELETE il semble indiquer que les serveurs devraient l'ignorer puisqu'il n'y a pas de sémantique définie pour cela.

4.3 Corps du message

Un serveur DOIT lire et transmettre un corps de message pour toute demande ; si la méthode de demande méthode de requête n'inclut pas la sémantique définie pour un corps de message, alors le corps de message DEVRAIT être ignoré lors du traitement de la demande.

J'ai déjà examiné plusieurs discussions connexes sur ce sujet sur SO et au-delà, telles que :

La plupart des discussions semblent convenir que fournir un corps de message sur un DELETE peut être autorisé mais n'est généralement pas recommandé.

En outre, j'ai remarqué une tendance dans diverses bibliothèques client HTTP où de plus en plus d'améliorations semblent être enregistrées pour que ces bibliothèques prennent en charge les corps de requête sur DELETE. La plupart des bibliothèques semblent s'y plier, bien que parfois avec un peu de résistance initiale.

Mon cas d'utilisation nécessite l'ajout de certaines métadonnées requises lors d'un DELETE (par exemple, la "raison" de la suppression, ainsi que d'autres métadonnées requises pour la suppression). J'ai envisagé les options suivantes, dont aucune ne semble totalement appropriée et conforme aux spécifications HTTP et/ou aux meilleures pratiques REST :

  • Corps du message - La spécification indique que les corps de message sur DELETE n'ont pas de valeur sémantique ; pas entièrement pris en charge par les clients HTTP ; pas de pratique standard.
  • En-têtes HTTP personnalisés - Exiger des en-têtes personnalisés est généralement contre les pratiques standard ; leur utilisation est incompatible avec le reste de mon API, dont aucune ne nécessite d'en-têtes personnalisés ; de plus, il n'existe pas de bonne réponse HTTP pour indiquer les mauvaises valeurs d'en-têtes personnalisés (probablement une question distincte).
  • En-têtes HTTP standard - Aucun en-tête standard n'est approprié
  • Paramètres d'interrogation - L'ajout de paramètres d'interrogation modifie en fait l'URL de la demande qui est supprimée ; contre les pratiques standard
  • Méthode POST - (par exemple POST /resourceToDelete { deletemetadata } ) POST n'est pas une option sémantique pour la suppression ; POST représente en réalité l'option en face de l'action souhaitée (par exemple, POST crée des subordonnés de la ressource ; mais je dois supprimer la ressource)
  • Méthodes multiples - Le fait de scinder la demande DELETE en deux opérations (par exemple, PUT delete metadata, puis DELETE) scinde une opération atomique en deux, laissant potentiellement un état incohérent. La raison de la suppression (et les autres métadonnées associées) ne font pas partie de la représentation de la ressource elle-même.

Ma première préférence serait probablement d'utiliser le corps du message, puis les en-têtes HTTP personnalisés ; toutefois, comme indiqué, ces approches présentent certains inconvénients.

Existe-t-il des recommandations ou des bonnes pratiques conformes aux normes REST/HTTP pour inclure ces métadonnées requises dans les demandes DELETE ? Existe-t-il d'autres solutions que je n'ai pas envisagées ?

59voto

shelley Points 2599

Malgré certaines recommandations de ne pas utiliser le corps du message pour les demandes DELETE, cette approche peut être appropriée dans certains cas d'utilisation. C'est l'approche que nous avons fini par utiliser après avoir évalué les autres options mentionnées dans la question/réponse, et après avoir collaboré avec les consommateurs du service.

Si l'utilisation du corps du message n'est pas idéale, aucune des autres options n'était non plus parfaitement adaptée. Le corps de la demande DELETE nous a permis d'ajouter facilement et clairement une sémantique autour des données/métadonnées supplémentaires qui étaient nécessaires pour accompagner l'opération DELETE.

Je suis toujours ouvert à d'autres réflexions et discussions, mais je voulais boucler la boucle sur cette question. J'apprécie les réflexions et discussions de chacun sur ce sujet !

3 votes

Je suis d'accord avec @Gabe, envoyer un corps avec des méthodes qui n'ont pas de corps par définition est un moyen sûr de perdre aléatoirement des données lorsque vos bits traversent les tuyaux de l'internet, et vous aurez beaucoup de mal à le déboguer.

7 votes

Ces commentaires contre l'utilisation de DELETE ne sont pas pertinents tant qu'ils n'abordent pas les problèmes très valables soulevés par l'OP. Je fais de mon mieux pour adhérer à l'esprit de REST, mais un delete sans concurrence optimiste et un delete qui n'a pas d'opération atomique par lot n'est pas pratique dans les situations de la vie réelle. Il s'agit d'une grave lacune du modèle REST.

1 votes

Je suis d'accord avec @Quarkly sur ce point. Je ne comprends pas l'idée de RESTFUL sur la façon dont nous devrions vérifier la simultanéité, etc. Les vérifications de simultanéité n'appartiennent pas au client.

12voto

codeprogression Points 1563

Compte tenu de la situation que vous avez, j'adopterais l'une des approches suivantes :

  • Envoyer un PUT ou PATCH : Je déduis que l'opération de suppression est virtuelle, par la nature de la nécessité d'une raison de suppression. Par conséquent, je pense que la mise à jour de l'enregistrement via une opération PUT/PATCH est une approche valide, même s'il ne s'agit pas d'une opération DELETE en soi.
  • Utilisez les paramètres de la requête : L'uri de la ressource n'est pas modifié. En fait, je pense que c'est également une approche valable. La question que vous avez liée parlait de ne pas autoriser la suppression si le paramètre de requête était manquant. Dans votre cas, j'aurais simplement une raison par défaut si la raison n'est pas spécifiée dans la chaîne de requête. La ressource sera toujours resource/:id . Vous pouvez le rendre découvrable avec des en-têtes de lien sur la ressource pour chaque raison (avec un rel sur chacun d'eux pour en identifier la raison).
  • Utiliser un point de terminaison distinct par raison : Utiliser une url comme resource/:id/canceled . Cela modifie effectivement l'URL de la demande et n'est absolument pas RESTful. Une fois de plus, les en-têtes de lien peuvent rendre cela découvrable.

N'oubliez pas que REST n'est pas une loi ou un dogme. Il s'agit plutôt d'un guide. Ainsi, s'il est logique de ne pas suivre les conseils pour votre domaine problématique, ne le faites pas. Assurez-vous simplement que vos consommateurs d'API sont informés de la différence.

0 votes

En ce qui concerne l'utilisation des paramètres de requête, j'ai cru comprendre que la requête faisait partie de l'URL de la requête par section 3.2 et, par conséquent, l'utilisation de cette approche (ou, de la même manière, des points de terminaison distincts) va à l'encontre de la définition de la norme DELETE de telle sorte que "resource identified by the Request-URI" soit supprimé.

1 votes

La ressource est identifiée par le chemin uri. Ainsi, un GET vers /orders/:id renverrait la même ressource que /orders/:id?exclude=orderdetails . La chaîne de requête ne fait que donner des indications au serveur - dans ce cas, il s'agit d'exclure les détails de la commande dans la réponse (si elle est prise en charge). De même, si vous envoyez un DELETE à /orders/:id o /orders/:id?reason=canceled o /orders/:id?reason=bad_credit vous agissez toujours sur la même ressource sous-jacente. Pour conserver une "interface uniforme", j'aurais une raison par défaut afin que l'envoi du paramètre de requête ne soit pas nécessaire.

0 votes

@shelley Vous avez raison de vous inquiéter des chaînes de requête. La chaîne de requête fait partie de l'URI. L'envoi d'une requête DELETE à /foo?123 signifie que vous supprimez une ressource différente que si vous envoyez DELETE à /foo?456 .

12voto

Nicholas Points 2862

Ce que vous semblez vouloir, c'est l'une ou l'autre de deux choses, qui ne sont ni l'une ni l'autre DELETE :

  1. Vous avez deux opérations, a PUT de la raison de la suppression, suivi d'un DELETE de la ressource. Une fois supprimé, le contenu de la ressource n'est plus accessible à quiconque. La "raison" ne peut pas contenir un lien hypertexte vers la ressource supprimée. Ou bien,
  2. Vous essayez de modifier une ressource de state=active a state=deleted en utilisant le DELETE méthode. Les ressources avec state=deleted sont ignorées par votre API principale mais peuvent encore être lues par un administrateur ou une personne ayant accès à la base de données. Ceci est autorisé - DELETE n'a pas à effacer les données de sauvegarde d'une ressource, mais seulement à supprimer la ressource exposée à cet URI.

Toute opération qui nécessite un corps de message sur un DELETE peut être décomposée de la manière la plus générale, à savoir une POST pour faire toutes les tâches nécessaires avec le corps du message, et un DELETE . Je ne vois aucune raison de rompre la sémantique de HTTP.

3 votes

Que se passe-t-il si PUT la raison réussit et DELETE les ressources échouent ? Comment éviter les états incohérents ?

1 votes

@Lightman le PUT spécifie seulement l'intention. Il peut exister sans DELETE correspondant, ce qui indiquerait que quelqu'un voulait effacer mais que cela a échoué ou qu'il a changé d'avis. L'inversion de l'ordre des appels permettrait également aux DELETE de se produire sans raison - la fourniture d'une raison serait alors considérée comme une simple annotation. C'est pour ces deux raisons que je recommanderais d'utiliser l'option 2 ci-dessus, c'est-à-dire de modifier l'état de l'enregistrement sous-jacent de manière à faire disparaître la ressource HTTP de son URL actuelle. Un éboueur/administrateur peut alors purger les enregistrements

0voto

user3013432 Points 11

Je dirais que les paramètres de requête font partie de la définition de la ressource, et que vous pouvez donc les utiliser pour définir la portée de votre opération, puis "appliquer" l'opération. Ma conclusion est la suivante Paramètres d'interrogation telle que vous l'avez définie est la meilleure approche.

0voto

Suresh Kumar Points 3170

Je vous suggère d'inclure les métadonnées requises dans la hiérarchie de l'URI elle-même. Un exemple (naïf) :

Si vous devez supprimer des entrées sur la base d'une plage de dates, au lieu de transmettre la date de début et la date de fin dans le corps ou en tant que paramètres de requête, structurez l'URI de manière à transmettre les informations requises dans le cadre de l'URI.

par exemple

DELETE /entries/range/01012012/31122012 -- Supprimer toutes les entrées entre le 01 janvier 2012 et le 31 décembre 2012

J'espère que cela vous aidera.

4 votes

Wow, c'est une idée terrible. Si vous avez trop de métadonnées, cela va gonfler les restrictions de taille sur l'URI.

2 votes

Cette approche ne suit pas les pratiques RESTful et n'est pas recommandée car vous aurez une structure d'URI alambiquée. Les points de terminaison s'embrouillent avec des identifications de ressources et des métadonnées entremêlées et deviennent un cauchemar de maintenance au fur et à mesure que l'API évolue. Il est de loin préférable d'avoir l'URI range spécifiée dans les paramètres de la requête ou dans la charge utile, ce qui est l'objet de cette question : comprendre l'approche des meilleures pratiques pour résoudre le problème, qui, à mon avis, n'est pas celle-ci.

0 votes

@digitaldreamer - Je ne comprends pas ce que vous entendez par structure URI alambiquée ? De plus, il s'agit d'un HTTP DELETE, donc le payload n'est pas une option, mais les paramètres de requête oui.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X